Merge change 4425 into donut

* changes:
  First step of a more standalone AvdSelector.
This commit is contained in:
Android (Google) Code Review
2009-06-17 11:44:47 -07:00
6 changed files with 296 additions and 384 deletions

View File

@@ -26,13 +26,12 @@ import com.android.ddmuilib.ImageHelper;
import com.android.ddmuilib.TableHelper; import com.android.ddmuilib.TableHelper;
import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.internal.sdk.Sdk; import com.android.ide.eclipse.adt.internal.sdk.Sdk;
import com.android.ide.eclipse.adt.internal.wizards.actions.AvdManagerAction;
import com.android.ide.eclipse.ddms.DdmsPlugin; import com.android.ide.eclipse.ddms.DdmsPlugin;
import com.android.sdklib.IAndroidTarget; import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.internal.avd.AvdManager;
import com.android.sdklib.internal.avd.AvdManager.AvdInfo; import com.android.sdklib.internal.avd.AvdManager.AvdInfo;
import com.android.sdkuilib.internal.widgets.AvdSelector; import com.android.sdkuilib.internal.widgets.AvdSelector;
import com.android.sdkuilib.internal.widgets.AvdSelector.SelectionMode; import com.android.sdkuilib.internal.widgets.AvdSelector.DisplayMode;
import com.android.sdkuilib.internal.widgets.AvdSelector.IAvdFilter;
import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.dialogs.IDialogConstants;
@@ -58,8 +57,6 @@ import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.Table;
import java.util.ArrayList;
/** /**
* A dialog that lets the user choose a device to deploy an application. * A dialog that lets the user choose a device to deploy an application.
* The user can either choose an exiting running device (including running emulators) * The user can either choose an exiting running device (including running emulators)
@@ -91,8 +88,6 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener
private final IAndroidTarget mProjectTarget; private final IAndroidTarget mProjectTarget;
private final Sdk mSdk; private final Sdk mSdk;
private AvdInfo[] mFullAvdList;
private Button mDeviceRadioButton; private Button mDeviceRadioButton;
private boolean mDisableAvdSelectionChange = false; private boolean mDisableAvdSelectionChange = false;
@@ -264,6 +259,7 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener
mProjectTarget = projectTarget; mProjectTarget = projectTarget;
mSdk = Sdk.getCurrent(); mSdk = Sdk.getCurrent();
AndroidDebugBridge.addDeviceChangeListener(this);
loadImages(); loadImages();
} }
@@ -415,24 +411,9 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener
offsetComp.setLayout(layout); offsetComp.setLayout(layout);
mPreferredAvdSelector = new AvdSelector(offsetComp, mPreferredAvdSelector = new AvdSelector(offsetComp,
getNonRunningAvds(false /*reloadAvds*/), mSdk.getAvdManager(),
mProjectTarget, new NonRunningAvdFilter(),
new AvdSelector.IExtraAction() { DisplayMode.SIMPLE_SELECTION);
public void run() {
AvdManagerAction action = new AvdManagerAction();
action.run(null);
refillAvdList(true /*reloadAvds*/);
}
public boolean isEnabled() {
return true;
}
public String label() {
return "AVD Manager...";
}
},
SelectionMode.CHECK);
mPreferredAvdSelector.setTableHeightHint(100); mPreferredAvdSelector.setTableHeightHint(100);
mPreferredAvdSelector.setEnabled(false); mPreferredAvdSelector.setEnabled(false);
mPreferredAvdSelector.setSelectionListener(new SelectionAdapter() { mPreferredAvdSelector.setSelectionListener(new SelectionAdapter() {
@@ -594,6 +575,7 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener
// update the display of AvdInfo (since it's filtered to only display // update the display of AvdInfo (since it's filtered to only display
// non running AVD). This is done on deviceChanged because the avd name // non running AVD). This is done on deviceChanged because the avd name
// of a (emulator) device may be updated as the emulator boots. // of a (emulator) device may be updated as the emulator boots.
refillAvdList(false /*reloadAvds*/); refillAvdList(false /*reloadAvds*/);
// if the changed device is the current selection, // if the changed device is the current selection,
@@ -707,50 +689,44 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener
handleDeviceSelection(); handleDeviceSelection();
} }
/** private final class NonRunningAvdFilter implements IAvdFilter {
* Returns the list of {@link AvdInfo} that are not already running in an emulator.
*/
private AvdInfo[] getNonRunningAvds(boolean reloadAvds) {
ArrayList<AvdInfo> list = new ArrayList<AvdInfo>();
// get the full list of Android Virtual Devices private IDevice[] mDevices;
if (reloadAvds || mFullAvdList == null) {
AvdManager avdManager = mSdk.getAvdManager(); public void prepare() {
if (avdManager != null) { mDevices = AndroidDebugBridge.getBridge().getDevices();
mFullAvdList = avdManager.getValidAvds(); }
public boolean accept(AvdInfo avd) {
if (mDevices != null) {
for (IDevice d : mDevices) {
if (mProjectTarget.isCompatibleBaseFor(avd.getTarget()) == false ||
avd.getName().equals(d.getAvdName())) {
return false;
}
} }
} }
// loop through all the Avd and put the one that are not running in the list. return true;
if (mFullAvdList != null) {
IDevice[] devices = AndroidDebugBridge.getBridge().getDevices();
avdLoop: for (AvdInfo info : mFullAvdList) {
for (IDevice d : devices) {
if (info.getName().equals(d.getAvdName())) {
continue avdLoop;
}
}
list.add(info);
}
} }
return list.toArray(new AvdInfo[list.size()]); public void cleanup() {
mDevices = null;
}
} }
/** /**
* Refills the AVD list keeping the current selection. * Refills the AVD list keeping the current selection.
*/ */
private void refillAvdList(boolean reloadAvds) { private void refillAvdList(boolean reloadAvds) {
AvdInfo[] array = getNonRunningAvds(reloadAvds);
// save the current selection // save the current selection
AvdInfo selected = mPreferredAvdSelector.getSelected(); AvdInfo selected = mPreferredAvdSelector.getSelected();
// disable selection change. // disable selection change.
mDisableAvdSelectionChange = true; mDisableAvdSelectionChange = true;
// set the new list in the selector // refresh the list
mPreferredAvdSelector.setAvds(array, mProjectTarget); mPreferredAvdSelector.refresh(false);
// attempt to reselect the proper avd if needed // attempt to reselect the proper avd if needed
if (selected != null) { if (selected != null) {

View File

@@ -20,14 +20,13 @@ import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.internal.launch.AndroidLaunchConfiguration.TargetMode; import com.android.ide.eclipse.adt.internal.launch.AndroidLaunchConfiguration.TargetMode;
import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper; import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
import com.android.ide.eclipse.adt.internal.sdk.Sdk; import com.android.ide.eclipse.adt.internal.sdk.Sdk;
import com.android.ide.eclipse.adt.internal.wizards.actions.AvdManagerAction;
import com.android.ide.eclipse.ddms.DdmsPlugin; import com.android.ide.eclipse.ddms.DdmsPlugin;
import com.android.prefs.AndroidLocation.AndroidLocationException; import com.android.prefs.AndroidLocation.AndroidLocationException;
import com.android.sdklib.IAndroidTarget; import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.internal.avd.AvdManager; import com.android.sdklib.internal.avd.AvdManager;
import com.android.sdklib.internal.avd.AvdManager.AvdInfo; import com.android.sdklib.internal.avd.AvdManager.AvdInfo;
import com.android.sdkuilib.internal.widgets.AvdSelector; import com.android.sdkuilib.internal.widgets.AvdSelector;
import com.android.sdkuilib.internal.widgets.AvdSelector.SelectionMode; import com.android.sdkuilib.internal.widgets.AvdSelector.DisplayMode;
import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.CoreException;
@@ -191,24 +190,12 @@ public class EmulatorConfigTab extends AbstractLaunchConfigurationTab {
mPreferredAvdLabel = new Label(offsetComp, SWT.NONE); mPreferredAvdLabel = new Label(offsetComp, SWT.NONE);
mPreferredAvdLabel.setText("Select a preferred Android Virtual Device for deployment:"); mPreferredAvdLabel.setText("Select a preferred Android Virtual Device for deployment:");
mPreferredAvdSelector = new AvdSelector(offsetComp,
null /*avds*/,
new AvdSelector.IExtraAction() {
public void run() {
AvdManagerAction action = new AvdManagerAction();
action.run(null);
updateAvdList(null);
}
public boolean isEnabled() { // create the selector with no manager, we'll reset the manager every time this is
return true; // displayed to ensure we have the latest one (dialog is reused but SDK could have
} // been changed in between.
mPreferredAvdSelector = new AvdSelector(offsetComp, null /* avd manager */,
public String label() { DisplayMode.SIMPLE_CHECK);
return "AVD Manager...";
}
},
SelectionMode.CHECK);
mPreferredAvdSelector.setTableHeightHint(100); mPreferredAvdSelector.setTableHeightHint(100);
mPreferredAvdSelector.setSelectionListener(new SelectionAdapter() { mPreferredAvdSelector.setSelectionListener(new SelectionAdapter() {
@Override @Override
@@ -322,13 +309,9 @@ public class EmulatorConfigTab extends AbstractLaunchConfigurationTab {
avdManager = Sdk.getCurrent().getAvdManager(); avdManager = Sdk.getCurrent().getAvdManager();
} }
AvdInfo[] avds = null; mPreferredAvdSelector.setManager(avdManager);
// no project? we don't want to display any "compatible" AVDs. mPreferredAvdSelector.setFilter(mProjectTarget);
if (avdManager != null && mProjectTarget != null) { mPreferredAvdSelector.refresh(false);
avds = avdManager.getValidAvds();
}
mPreferredAvdSelector.setAvds(avds, mProjectTarget);
} }
/* (non-Javadoc) /* (non-Javadoc)

View File

@@ -27,7 +27,7 @@ import com.android.sdklib.ISdkLog;
import com.android.sdklib.internal.avd.AvdManager; import com.android.sdklib.internal.avd.AvdManager;
import com.android.sdklib.internal.avd.AvdManager.AvdInfo; import com.android.sdklib.internal.avd.AvdManager.AvdInfo;
import com.android.sdkuilib.internal.widgets.AvdSelector; import com.android.sdkuilib.internal.widgets.AvdSelector;
import com.android.sdkuilib.internal.widgets.AvdSelector.SelectionMode; import com.android.sdkuilib.internal.widgets.AvdSelector.DisplayMode;
import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IProject;
import org.eclipse.jface.wizard.WizardPage; import org.eclipse.jface.wizard.WizardPage;
@@ -162,21 +162,7 @@ class AvdManagerListPage extends WizardPage {
} }
}); });
mAvdSelector = new AvdSelector(parent, mAvdSelector = new AvdSelector(parent, getAvdManager(), DisplayMode.MANAGER);
SelectionMode.SELECT,
new AvdSelector.IExtraAction() {
public String label() {
return "Delete AVD...";
}
public boolean isEnabled() {
return mAvdSelector != null && mAvdSelector.getSelected() != null;
}
public void run() {
onDelete();
}
});
} }
/** /**
@@ -430,29 +416,12 @@ class AvdManagerListPage extends WizardPage {
* Tries to preserve the selection. * Tries to preserve the selection.
*/ */
private void reloadAvdList() { private void reloadAvdList() {
AvdInfo selected = mAvdSelector.getSelected(); mAvdSelector.refresh(true /* reload */);
AvdManager avdm = getAvdManager();
AvdInfo[] avds = null;
// For the AVD manager to reload the list, in case AVDs where created using the
// command line tool.
// The AVD manager may not exist yet, typically when loading the SDK.
if (avdm != null) {
try {
avdm.reloadAvds();
} catch (AndroidLocationException e) {
AdtPlugin.log(e, "AVD Manager reload failed"); //$NON-NLS-1$
}
avds = avdm.getValidAvds();
}
mAvdSelector.setAvds(avds, null /*filter*/);
// Keep the list of known AVD names to check if they exist quickly. however // 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 // use the list of all AVDs, including broken ones (unless we don't know their
// name). // name).
AvdManager avdm = getAvdManager();
mKnownAvdNames.clear(); mKnownAvdNames.clear();
if (avdm != null) { if (avdm != null) {
for (AvdInfo avd : avdm.getAllAvds()) { for (AvdInfo avd : avdm.getAllAvds()) {
@@ -462,14 +431,15 @@ class AvdManagerListPage extends WizardPage {
} }
} }
} }
mAvdSelector.setSelection(selected);
} }
/** /**
* Triggered when the user selects the "delete" button (the extra action in the selector) * Triggered when the user selects the "delete" button (the extra action in the selector)
* Deletes the currently selected AVD, if any. * Deletes the currently selected AVD, if any.
*
* This is obsolete. Kept around to reuse the code later in the AvdSelector itself.
*/ */
@Deprecated
private void onDelete() { private void onDelete() {
AvdInfo avdInfo = mAvdSelector.getSelected(); AvdInfo avdInfo = mAvdSelector.getSelected();
AvdManager avdm = getAvdManager(); AvdManager avdm = getAvdManager();

View File

@@ -16,29 +16,19 @@
package com.android.sdkuilib.internal.repository; package com.android.sdkuilib.internal.repository;
import com.android.sdklib.internal.avd.AvdManager;
import com.android.sdklib.internal.avd.AvdManager.AvdInfo;
import com.android.sdkuilib.internal.repository.UpdaterData.ISdkListener; import com.android.sdkuilib.internal.repository.UpdaterData.ISdkListener;
import com.android.sdkuilib.internal.widgets.AvdSelector; import com.android.sdkuilib.internal.widgets.AvdSelector;
import com.android.sdkuilib.internal.widgets.AvdSelector.SelectionMode; import com.android.sdkuilib.internal.widgets.AvdSelector.DisplayMode;
import org.eclipse.swt.SWT; import org.eclipse.swt.SWT;
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.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Label;
import java.util.HashSet;
public class AvdManagerPage extends Composite implements ISdkListener { public class AvdManagerPage extends Composite implements ISdkListener {
private Button mRefreshButton;
private AvdSelector mAvdSelector; private AvdSelector mAvdSelector;
private final HashSet<String> mKnownAvdNames = new HashSet<String>();
private final UpdaterData mUpdaterData; private final UpdaterData mUpdaterData;
/** /**
@@ -57,43 +47,12 @@ public class AvdManagerPage extends Composite implements ISdkListener {
} }
private void createContents(Composite parent) { private void createContents(Composite parent) {
parent.setLayout(new GridLayout(3, false)); parent.setLayout(new GridLayout(1, false));
Label label = new Label(parent, SWT.NONE); Label label = new Label(parent, SWT.NONE);
label.setText("List of existing Android Virtual Devices:"); label.setText("List of existing Android Virtual Devices:");
label.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, true, false, 2, 1));
mRefreshButton = new Button(parent, SWT.PUSH); mAvdSelector = new AvdSelector(parent, mUpdaterData.getAvdManager(), DisplayMode.MANAGER);
mRefreshButton.setText("Refresh");
mRefreshButton.setLayoutData(new GridData(SWT.END, SWT.CENTER, false, false));
mRefreshButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
onRefreshSelected(); //$hide$
}
});
Composite group = new Composite(parent, SWT.NONE);
group.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 3, 1));
GridLayout gl;
group.setLayout(gl = new GridLayout(1, false /*makeColumnsEqualWidth*/));
gl.marginHeight = gl.marginWidth = 0;
mAvdSelector = new AvdSelector(group,
SelectionMode.SELECT,
new AvdSelector.IExtraAction() {
public String label() {
return "Delete AVD...";
}
public boolean isEnabled() {
return mAvdSelector != null && mAvdSelector.getSelected() != null;
}
public void run() {
//TODO onDelete();
}
});
} }
@Override @Override
@@ -115,48 +74,11 @@ public class AvdManagerPage extends Composite implements ISdkListener {
* Called by the constructor right after {@link #createContents(Composite)}. * Called by the constructor right after {@link #createContents(Composite)}.
*/ */
private void postCreate() { private void postCreate() {
reloadAvdList(); // nothing to be done for now.
}
/**
* Reloads the AVD list in the AVD selector.
* Tries to preserve the selection.
*/
private void reloadAvdList() {
AvdInfo selected = mAvdSelector.getSelected();
AvdInfo[] avds = null;
AvdManager manager = mUpdaterData.getAvdManager();
if (manager != null) {
avds = manager.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 (manager != null) {
for (AvdInfo avd : manager.getAllAvds()) {
String name = avd.getName();
if (name != null) {
mKnownAvdNames.add(name);
}
}
}
mAvdSelector.setSelection(selected);
} }
public void onSdkChange() { public void onSdkChange() {
reloadAvdList(); mAvdSelector.refresh(false /*reload*/);
}
private void onRefreshSelected() {
mUpdaterData.reloadAvds();
reloadAvdList();
} }
// End of hiding from SWT Designer // End of hiding from SWT Designer

View File

@@ -18,7 +18,6 @@ package com.android.sdkuilib.internal.repository.icons;
import org.eclipse.swt.SWTException; import org.eclipse.swt.SWTException;
import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Display;
import java.io.InputStream; import java.io.InputStream;
@@ -74,7 +73,7 @@ public class ImageFactory {
Iterator<Image> it = mImages.values().iterator(); Iterator<Image> it = mImages.values().iterator();
while(it.hasNext()) { while(it.hasNext()) {
Image img = it.next(); Image img = it.next();
if (img != null) { if (img != null && img.isDisposed() == false) {
img.dispose(); img.dispose();
} }
it.remove(); it.remove();

View File

@@ -16,24 +16,28 @@
package com.android.sdkuilib.internal.widgets; package com.android.sdkuilib.internal.widgets;
import com.android.prefs.AndroidLocation.AndroidLocationException;
import com.android.sdklib.IAndroidTarget; import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.internal.avd.AvdManager;
import com.android.sdklib.internal.avd.AvdManager.AvdInfo; import com.android.sdklib.internal.avd.AvdManager.AvdInfo;
import com.android.sdklib.internal.avd.AvdManager.AvdInfo.AvdStatus;
import com.android.sdkuilib.internal.repository.icons.ImageFactory;
import org.eclipse.swt.SWT; import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ControlAdapter; import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent; import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn; import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem; import org.eclipse.swt.widgets.TableItem;
@@ -42,62 +46,96 @@ import org.eclipse.swt.widgets.TableItem;
/** /**
* The AVD selector is a table that is added to the given parent composite. * The AVD selector is a table that is added to the given parent composite.
* <p/> * <p/>
* To use, create it using {@link #AvdSelector(Composite, SelectionMode, IExtraAction)} then * To use, create it using {@link #AvdSelector(Composite, AvdManager, DisplayMode)} then
* call {@link #setSelection(AvdInfo)}, {@link #setSelectionListener(SelectionListener)} * call {@link #setSelection(AvdInfo)}, {@link #setSelectionListener(SelectionListener)}
* and finally use {@link #getSelected()} to retrieve the selection. * and finally use {@link #getSelected()} to retrieve the selection.
*/ */
public final class AvdSelector { public final class AvdSelector {
private AvdInfo[] mAvds;
private SelectionListener mSelectionListener; private SelectionListener mSelectionListener;
private Table mTable; private Table mTable;
private Label mDescription;
private static int NUM_COL = 2; private static int NUM_COL = 2;
private final SelectionMode mSelectionMode; private final DisplayMode mDisplayMode;
private final IExtraAction mExtraAction; private Button mManagerButton;
private Button mExtraActionButton; private IAvdFilter mTargetFilter;
private AvdManager mManager;
private Image mOkImage;
private Image mBrokenImage;
private ImageFactory mIconFactory;
/** The selection mode, either {@link #SELECT} or {@link #CHECK} */
public enum SelectionMode {
/** /**
* The display mode of the AVD Selector.
*/
public static enum DisplayMode {
/**
* Manager mode. Invalid AVDs are displayed. Buttons to create/delete AVDs
*/
MANAGER,
/**
* Non manager mode. Only valid AVDs are displayed. Cannot create/delete AVDs, but
* there is a button to open the AVD Manager.
* In the "check" selection mode, checkboxes are displayed on each line * In the "check" selection mode, checkboxes are displayed on each line
* and {@link AvdSelector#getSelected()} returns the line that is checked * and {@link AvdSelector#getSelected()} returns the line that is checked
* even if it is not the currently selected line. Only one line can * even if it is not the currently selected line. Only one line can
* be checked at once. * be checked at once.
*/ */
CHECK, SIMPLE_CHECK,
/** /**
* Non manager mode. Only valid AVDs are displayed. Cannot create/delete AVDs, but
* there is a button to open the AVD Manager.
* In the "select" selection mode, there are no checkboxes and * In the "select" selection mode, there are no checkboxes and
* {@link AvdSelector#getSelected()} returns the line currently selected. * {@link AvdSelector#getSelected()} returns the line currently selected.
* Only one line can be selected at once. * Only one line can be selected at once.
*/ */
SELECT SIMPLE_SELECTION,
} }
/** /**
* Defines an "extra action" button that can be shown under the AVD Selector. * A filter to control the whether or not an AVD should be displayed by the AVD Selector.
*/ */
public interface IExtraAction { public interface IAvdFilter {
/** /**
* Label of the button that will be created. * Called before {@link #accept(AvdInfo)} is called for any AVD.
* This is invoked once when the button is created and cannot be changed later.
*/ */
public String label(); void prepare();
/** /**
* This is invoked just after the selection has changed to update the "enabled" * Called to decided whether an AVD should be displayed.
* state of the action. Implementation should use {@link AvdSelector#getSelected()}. * @param avd the AVD to test.
* @return true if the AVD should be displayed.
*/ */
public boolean isEnabled(); boolean accept(AvdInfo avd);
/** /**
* Run the action, invoked when the button is clicked. * Called after {@link #accept(AvdInfo)} has been called on all the AVDs.
*
* The caller's action is responsible for reloading the AVD list
* using {@link AvdSelector#setAvds(AvdInfo[], IAndroidTarget)}.
*/ */
public void run(); void cleanup();
}
/**
* Internal implementation of {@link IAvdFilter} to filter out the AVDs that are not
* running an image compatible with a specific target.
*/
private final static class TargetBasedFilter implements IAvdFilter {
private final IAndroidTarget mTarget;
TargetBasedFilter(IAndroidTarget target) {
mTarget = target;
}
public void prepare() {
// nothing to prepare
}
public boolean accept(AvdInfo avd) {
return mTarget.isCompatibleBaseFor(avd.getTarget());
}
public void cleanup() {
// nothing to clean up
}
} }
/** /**
@@ -107,20 +145,18 @@ public final class AvdSelector {
* {@link IAndroidTarget} will be displayed. * {@link IAndroidTarget} will be displayed.
* *
* @param parent The parent composite where the selector will be added. * @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 manager the AVD manager.
* It can be null. * @param filter When non-null, will allow filtering the AVDs to display.
* @param filter When non-null, will display only the AVDs matching this target.
* @param extraAction When non-null, displays an extra action button. * @param extraAction When non-null, displays an extra action button.
* @param selectionMode One of {@link SelectionMode#SELECT} or {@link SelectionMode#CHECK} * @param displayMode The display mode ({@link DisplayMode}).
*/ */
public AvdSelector(Composite parent, public AvdSelector(Composite parent,
AvdInfo[] avds, AvdManager manager,
IAndroidTarget filter, IAvdFilter filter,
IExtraAction extraAction, DisplayMode displayMode) {
SelectionMode selectionMode) { mManager = manager;
mAvds = avds; mTargetFilter = filter;
mExtraAction = extraAction; mDisplayMode = displayMode;
mSelectionMode = selectionMode;
// Layout has 2 columns // Layout has 2 columns
Composite group = new Composite(parent, SWT.NONE); Composite group = new Composite(parent, SWT.NONE);
@@ -129,9 +165,14 @@ public final class AvdSelector {
gl.marginHeight = gl.marginWidth = 0; gl.marginHeight = gl.marginWidth = 0;
group.setLayoutData(new GridData(GridData.FILL_BOTH)); group.setLayoutData(new GridData(GridData.FILL_BOTH));
group.setFont(parent.getFont()); group.setFont(parent.getFont());
group.addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent arg0) {
mIconFactory.dispose();
}
});
int style = SWT.FULL_SELECTION | SWT.SINGLE | SWT.BORDER; int style = SWT.FULL_SELECTION | SWT.SINGLE | SWT.BORDER;
if (selectionMode == SelectionMode.CHECK) { if (displayMode == DisplayMode.SIMPLE_CHECK) {
style |= SWT.CHECK; style |= SWT.CHECK;
} }
mTable = new Table(group, style); mTable = new Table(group, style);
@@ -139,18 +180,53 @@ public final class AvdSelector {
mTable.setLinesVisible(false); mTable.setLinesVisible(false);
setTableHeightHint(0); setTableHeightHint(0);
mDescription = new Label(group, SWT.WRAP); Composite buttons = new Composite(group, SWT.NONE);
mDescription.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); buttons.setLayout(gl = new GridLayout(1, false /*makeColumnsEqualWidth*/));
gl.marginHeight = gl.marginWidth = 0;
buttons.setLayoutData(new GridData(GridData.FILL_VERTICAL));
buttons.setFont(group.getFont());
if (extraAction != null) { if (displayMode == DisplayMode.MANAGER) {
mExtraActionButton = new Button(group, SWT.PUSH); Button newButton = new Button(buttons, SWT.PUSH | SWT.FLAT);
mExtraActionButton.setText(extraAction.label()); newButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
mExtraActionButton.setEnabled(extraAction.isEnabled()); newButton.setText("New");
mExtraActionButton.addSelectionListener(new SelectionAdapter() { // TODO: callback for button
Button deleteButton = new Button(buttons, SWT.PUSH | SWT.FLAT);
deleteButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
deleteButton.setText("Delete");
// TODO: callback for button
Label l = new Label(buttons, SWT.SEPARATOR | SWT.HORIZONTAL);
l.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
}
Button infoButton = new Button(buttons, SWT.PUSH | SWT.FLAT);
infoButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
infoButton.setText("Info...");
// TODO: callback for button
Composite padding = new Composite(buttons, SWT.NONE);
padding.setLayoutData(new GridData(GridData.FILL_VERTICAL));
Button refreshButton = new Button(buttons, SWT.PUSH | SWT.FLAT);
refreshButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
refreshButton.setText("Resfresh");
refreshButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent arg0) {
refresh(true);
}
});
if (displayMode != DisplayMode.MANAGER) {
mManagerButton = new Button(buttons, SWT.PUSH | SWT.FLAT);
mManagerButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
mManagerButton.setText("Manager...");
mManagerButton.addSelectionListener(new SelectionAdapter() {
@Override @Override
public void widgetSelected(SelectionEvent e) { public void widgetSelected(SelectionEvent e) {
super.widgetSelected(e); super.widgetSelected(e);
mExtraAction.run();
} }
}); });
} }
@@ -165,41 +241,45 @@ public final class AvdSelector {
final TableColumn column3 = new TableColumn(mTable, SWT.NONE); final TableColumn column3 = new TableColumn(mTable, SWT.NONE);
column3.setText("API Level"); column3.setText("API Level");
// get some bitmaps.
mIconFactory = new ImageFactory(parent.getDisplay());
mOkImage = mIconFactory.getImage("accept_icon16.png");
mBrokenImage = mIconFactory.getImage("reject_icon16.png");
adjustColumnsWidth(mTable, column0, column1, column2, column3); adjustColumnsWidth(mTable, column0, column1, column2, column3);
setupSelectionListener(mTable); setupSelectionListener(mTable);
fillTable(mTable, filter); fillTable(mTable);
setupTooltip(mTable);
} }
/** /**
* Creates a new SDK Target Selector, and fills it with a list of {@link AvdInfo}. * 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 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 manager the AVD manager.
* It can be null. * @param displayMode The display mode ({@link DisplayMode}).
* @param extraAction When non-null, displays an extra action button.
* @param selectionMode One of {@link SelectionMode#SELECT} or {@link SelectionMode#CHECK}
*/ */
public AvdSelector(Composite parent, public AvdSelector(Composite parent, AvdManager manager,
AvdInfo[] avds, DisplayMode displayMode) {
IExtraAction extraAction, this(parent, manager, (IAvdFilter)null /* filter */, displayMode);
SelectionMode selectionMode) {
this(parent, avds, null /* filter */, extraAction, selectionMode);
} }
/** /**
* Creates a new SDK Target Selector, and fills it with a list of {@link AvdInfo}. * Creates a new SDK Target Selector, and fills it with a list of {@link AvdInfo}, filtered
* by an {@link IAndroidTarget}.
* <p/>Only the {@link AvdInfo} able to run applications developed for the given
* {@link IAndroidTarget} will be displayed.
* *
* @param parent The parent composite where the selector will be added. * @param parent The parent composite where the selector will be added.
* @param extraAction When non-null, displays an extra action button. * @param manager the AVD manager.
* @param selectionMode One of {@link SelectionMode#SELECT} or {@link SelectionMode#CHECK} * @param filter Only shows the AVDs matching this target (must not be null).
* @param displayMode The display mode ({@link DisplayMode}).
*/ */
public AvdSelector(Composite parent, public AvdSelector(Composite parent,
SelectionMode selectionMode, AvdManager manager,
IExtraAction extraAction) { IAndroidTarget filter,
this(parent, null /*avds*/, null /* filter */, extraAction, selectionMode); DisplayMode displayMode) {
this(parent, manager, new TargetBasedFilter(filter), displayMode);
} }
/** /**
* Sets the table grid layout data. * Sets the table grid layout data.
* *
@@ -212,41 +292,70 @@ public final class AvdSelector {
} }
data.grabExcessVerticalSpace = true; data.grabExcessVerticalSpace = true;
data.grabExcessHorizontalSpace = true; data.grabExcessHorizontalSpace = true;
data.horizontalSpan = NUM_COL;
data.horizontalAlignment = GridData.FILL; data.horizontalAlignment = GridData.FILL;
data.verticalAlignment = GridData.FILL; data.verticalAlignment = GridData.FILL;
mTable.setLayoutData(data); mTable.setLayoutData(data);
} }
/** /**
* Sets a new set of AVD, with an optional filter. * Refresh the display of Android Virtual Devices.
* Tries to keep the selection. * Tries to keep the selection.
* <p/> * <p/>
* This must be called from the UI thread. * This must be called from the UI thread.
* *
* * @param reload if true, the AVD manager will reload the AVD from the disk.
* @param avds The list of AVDs. This is <em>not</em> copied, the caller must not modify. * @throws AndroidLocationException if reload the AVD failed.
* It can be null. * @return false if the reloading failed. This is always true if <var>reload</var> is
* @param filter An IAndroidTarget. If non-null, only AVD whose target are compatible with the * <code>false</code>.
* filter target will displayed an available for selection.
*/ */
public void setAvds(AvdInfo[] avds, IAndroidTarget filter) { public boolean refresh(boolean reload) {
if (reload) {
try {
mManager.reloadAvds();
} catch (AndroidLocationException e) {
return false;
}
}
AvdInfo selected = getSelected(); AvdInfo selected = getSelected();
mAvds = avds; fillTable(mTable);
fillTable(mTable, filter);
setSelection(selected); setSelection(selected);
return true;
} }
/** /**
* Returns the list of known AVDs. * Sets a new AVD manager
* <p/> * This does not refresh the display. Call {@link #refresh(boolean)} to do so.
* This is not a copy. Callers must <em>not</em> modify this array. * @param manager the AVD manager.
*/ */
public AvdInfo[] getAvds() { public void setManager(AvdManager manager) {
return mAvds; mManager = manager;
}
/**
* Sets a new AVD filter.
* This does not refresh the display. Call {@link #refresh(boolean)} to do so.
* @param filter An IAvdFilter. If non-null, this will filter out the AVD to not display.
*/
public void setFilter(IAvdFilter filter) {
mTargetFilter = filter;
}
/**
* Sets a new Android Target-based AVD filter.
* This does not refresh the display. Call {@link #refresh(boolean)} to do so.
* @param target An IAndroidTarget. If non-null, only AVD whose target are compatible with the
* filter target will displayed an available for selection.
*/
public void setFilter(IAndroidTarget target) {
if (target != null) {
mTargetFilter = new TargetBasedFilter(target);
} else {
mTargetFilter = null;
}
} }
/** /**
@@ -281,19 +390,7 @@ public final class AvdSelector {
int selIndex = mTable.getSelectionIndex(); int selIndex = mTable.getSelectionIndex();
int index = 0; int index = 0;
for (TableItem i : mTable.getItems()) { for (TableItem i : mTable.getItems()) {
if (mSelectionMode == SelectionMode.SELECT) { if (mDisplayMode == DisplayMode.SIMPLE_CHECK) {
if ((AvdInfo) i.getData() == target) {
found = true;
if (index != selIndex) {
mTable.setSelection(index);
modified = true;
}
break;
}
index++;
} else if (mSelectionMode == SelectionMode.CHECK){
if ((AvdInfo) i.getData() == target) { if ((AvdInfo) i.getData() == target) {
found = true; found = true;
if (!i.getChecked()) { if (!i.getChecked()) {
@@ -304,6 +401,17 @@ public final class AvdSelector {
modified = true; modified = true;
i.setChecked(false); i.setChecked(false);
} }
} else {
if ((AvdInfo) i.getData() == target) {
found = true;
if (index != selIndex) {
mTable.setSelection(index);
modified = true;
}
break;
}
index++;
} }
} }
@@ -311,10 +419,6 @@ public final class AvdSelector {
mSelectionListener.widgetSelected(null); mSelectionListener.widgetSelected(null);
} }
if (mExtraAction != null && mExtraActionButton != null) {
mExtraActionButton.setEnabled(mExtraAction.isEnabled());
}
return found; return found;
} }
@@ -324,18 +428,17 @@ public final class AvdSelector {
* @return The currently selected item or null. * @return The currently selected item or null.
*/ */
public AvdInfo getSelected() { public AvdInfo getSelected() {
if (mSelectionMode == SelectionMode.SELECT) { if (mDisplayMode == DisplayMode.SIMPLE_CHECK) {
int selIndex = mTable.getSelectionIndex();
if (selIndex >= 0) {
return (AvdInfo) mTable.getItem(selIndex).getData();
}
} else if (mSelectionMode == SelectionMode.CHECK) {
for (TableItem i : mTable.getItems()) { for (TableItem i : mTable.getItems()) {
if (i.getChecked()) { if (i.getChecked()) {
return (AvdInfo) i.getData(); return (AvdInfo) i.getData();
} }
} }
} else {
int selIndex = mTable.getSelectionIndex();
if (selIndex >= 0) {
return (AvdInfo) mTable.getItem(selIndex).getData();
}
} }
return null; return null;
} }
@@ -349,7 +452,6 @@ public final class AvdSelector {
*/ */
public void setEnabled(boolean enabled) { public void setEnabled(boolean enabled) {
mTable.setEnabled(enabled); mTable.setEnabled(enabled);
mDescription.setEnabled(enabled);
} }
/** /**
@@ -368,15 +470,14 @@ public final class AvdSelector {
@Override @Override
public void controlResized(ControlEvent e) { public void controlResized(ControlEvent e) {
Rectangle r = table.getClientArea(); Rectangle r = table.getClientArea();
column0.setWidth(r.width * 30 / 100); // 30% column0.setWidth(r.width * 25 / 100); // 25%
column1.setWidth(r.width * 45 / 100); // 45% column1.setWidth(r.width * 45 / 100); // 45%
column2.setWidth(r.width * 10 / 100); // 10% column2.setWidth(r.width * 15 / 100); // 15%
column3.setWidth(r.width * 15 / 100); // 15% column3.setWidth(r.width * 15 / 100); // 15%
} }
}); });
} }
/** /**
* Creates a selection listener that will check or uncheck the whole line when * Creates a selection listener that will check or uncheck the whole line when
* double-clicked (aka "the default selection"). * double-clicked (aka "the default selection").
@@ -393,16 +494,11 @@ public final class AvdSelector {
if (e.item instanceof TableItem) { if (e.item instanceof TableItem) {
TableItem i = (TableItem) e.item; TableItem i = (TableItem) e.item;
enforceSingleSelection(i); enforceSingleSelection(i);
updateDescription(i);
} }
if (mSelectionListener != null) { if (mSelectionListener != null) {
mSelectionListener.widgetSelected(e); mSelectionListener.widgetSelected(e);
} }
if (mExtraAction != null && mExtraActionButton != null) {
mExtraActionButton.setEnabled(mExtraAction.isEnabled());
}
} }
/** /**
@@ -416,20 +512,15 @@ public final class AvdSelector {
public void widgetDefaultSelected(SelectionEvent e) { public void widgetDefaultSelected(SelectionEvent e) {
if (e.item instanceof TableItem) { if (e.item instanceof TableItem) {
TableItem i = (TableItem) e.item; TableItem i = (TableItem) e.item;
if (mSelectionMode == SelectionMode.CHECK) { if (mDisplayMode == DisplayMode.SIMPLE_CHECK) {
i.setChecked(true); i.setChecked(true);
} }
enforceSingleSelection(i); enforceSingleSelection(i);
updateDescription(i);
} }
if (mSelectionListener != null) { if (mSelectionListener != null) {
mSelectionListener.widgetDefaultSelected(e); mSelectionListener.widgetDefaultSelected(e);
} }
if (mExtraAction != null && mExtraActionButton != null) {
mExtraActionButton.setEnabled(mExtraAction.isEnabled());
}
} }
/** /**
@@ -437,10 +528,7 @@ public final class AvdSelector {
* This makes the chekboxes act as radio buttons. * This makes the chekboxes act as radio buttons.
*/ */
private void enforceSingleSelection(TableItem item) { private void enforceSingleSelection(TableItem item) {
if (mSelectionMode == SelectionMode.SELECT) { if (mDisplayMode == DisplayMode.SIMPLE_CHECK) {
// pass
} else if (mSelectionMode == SelectionMode.CHECK) {
if (item.getChecked()) { if (item.getChecked()) {
Table parentTable = item.getParent(); Table parentTable = item.getParent();
for (TableItem i2 : parentTable.getItems()) { for (TableItem i2 : parentTable.getItems()) {
@@ -449,6 +537,8 @@ public final class AvdSelector {
} }
} }
} }
} else {
// pass
} }
} }
}); });
@@ -464,23 +554,52 @@ public final class AvdSelector {
* <li>column 3: sdk version * <li>column 3: sdk version
* </ul> * </ul>
*/ */
private void fillTable(final Table table, IAndroidTarget filter) { private void fillTable(final Table table) {
table.removeAll(); table.removeAll();
if (mAvds != null && mAvds.length > 0) {
// get the AVDs
AvdInfo avds[] = null;
if (mManager != null) {
if (mDisplayMode == DisplayMode.MANAGER) {
avds = mManager.getAllAvds();
} else {
avds = mManager.getValidAvds();
}
}
if (avds != null && avds.length > 0) {
table.setEnabled(true); table.setEnabled(true);
for (AvdInfo avd : mAvds) {
if (filter == null || filter.isCompatibleBaseFor(avd.getTarget())) { if (mTargetFilter != null) {
mTargetFilter.prepare();
}
for (AvdInfo avd : avds) {
if (mTargetFilter == null || mTargetFilter.accept(avd)) {
TableItem item = new TableItem(table, SWT.NONE); TableItem item = new TableItem(table, SWT.NONE);
item.setData(avd); item.setData(avd);
item.setText(0, avd.getName()); item.setText(0, avd.getName());
if (mDisplayMode == DisplayMode.MANAGER) {
item.setImage(0, avd.getStatus() == AvdStatus.OK ? mOkImage : mBrokenImage);
}
IAndroidTarget target = avd.getTarget(); IAndroidTarget target = avd.getTarget();
if (target != null) {
item.setText(1, target.getFullName()); item.setText(1, target.getFullName());
item.setText(2, target.getApiVersionName()); item.setText(2, target.getApiVersionName());
item.setText(3, Integer.toString(target.getApiVersionNumber())); item.setText(3, Integer.toString(target.getApiVersionNumber()));
} else {
item.setText(1, "?");
item.setText(2, "?");
item.setText(3, "?");
} }
} }
} }
if (mTargetFilter != null) {
mTargetFilter.cleanup();
}
}
if (table.getItemCount() == 0) { if (table.getItemCount() == 0) {
table.setEnabled(false); table.setEnabled(false);
TableItem item = new TableItem(table, SWT.NONE); TableItem item = new TableItem(table, SWT.NONE);
@@ -491,61 +610,4 @@ public final class AvdSelector {
item.setText(3, "--"); item.setText(3, "--");
} }
} }
/**
* Sets up a tooltip that displays the current item description.
* <p/>
* Displaying a tooltip over the table looks kind of odd here. Instead we actually
* display the description in a label under the table.
*/
private void setupTooltip(final Table table) {
/*
* Reference:
* http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet125.java?view=markup
*/
final Listener listener = new Listener() {
public void handleEvent(Event event) {
switch(event.type) {
case SWT.KeyDown:
case SWT.MouseExit:
case SWT.MouseDown:
return;
case SWT.MouseHover:
updateDescription(table.getItem(new Point(event.x, event.y)));
break;
case SWT.Selection:
if (event.item instanceof TableItem) {
updateDescription((TableItem) event.item);
}
break;
default:
return;
}
}
};
table.addListener(SWT.Dispose, listener);
table.addListener(SWT.KeyDown, listener);
table.addListener(SWT.MouseMove, listener);
table.addListener(SWT.MouseHover, listener);
}
/**
* Updates the description label with the path of the item's AVD, if any.
*/
private void updateDescription(TableItem item) {
if (item != null) {
Object data = item.getData();
if (data instanceof AvdInfo) {
String newTooltip = ((AvdInfo) data).getPath();
mDescription.setText(newTooltip == null ? "" : newTooltip); //$NON-NLS-1$
}
}
}
} }