Home | History | Annotate | Download | only in launch
      1 /*
      2  * Copyright (C) 2007 The Android Open Source Project
      3  *
      4  * Licensed under the Eclipse Public License, Version 1.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.eclipse.org/org/documents/epl-v10.php
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.ide.eclipse.adt.internal.launch;
     18 
     19 import com.android.ddmlib.AndroidDebugBridge;
     20 import com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener;
     21 import com.android.ddmlib.Client;
     22 import com.android.ddmlib.IDevice;
     23 import com.android.ddmlib.IDevice.DeviceState;
     24 import com.android.ddmuilib.ImageLoader;
     25 import com.android.ddmuilib.TableHelper;
     26 import com.android.ide.eclipse.adt.internal.editors.IconFactory;
     27 import com.android.ide.eclipse.adt.internal.sdk.AdtConsoleSdkLog;
     28 import com.android.ide.eclipse.adt.internal.sdk.Sdk;
     29 import com.android.ide.eclipse.ddms.DdmsPlugin;
     30 import com.android.sdklib.AndroidVersion;
     31 import com.android.sdklib.IAndroidTarget;
     32 import com.android.sdklib.internal.avd.AvdInfo;
     33 import com.android.sdkuilib.internal.widgets.AvdSelector;
     34 import com.android.sdkuilib.internal.widgets.AvdSelector.DisplayMode;
     35 import com.android.sdkuilib.internal.widgets.AvdSelector.IAvdFilter;
     36 
     37 import org.eclipse.jface.dialogs.Dialog;
     38 import org.eclipse.jface.dialogs.IDialogConstants;
     39 import org.eclipse.jface.viewers.ILabelProviderListener;
     40 import org.eclipse.jface.viewers.IStructuredContentProvider;
     41 import org.eclipse.jface.viewers.ITableLabelProvider;
     42 import org.eclipse.jface.viewers.StructuredSelection;
     43 import org.eclipse.jface.viewers.TableViewer;
     44 import org.eclipse.jface.viewers.Viewer;
     45 import org.eclipse.swt.SWT;
     46 import org.eclipse.swt.SWTException;
     47 import org.eclipse.swt.events.SelectionAdapter;
     48 import org.eclipse.swt.events.SelectionEvent;
     49 import org.eclipse.swt.graphics.Image;
     50 import org.eclipse.swt.layout.GridData;
     51 import org.eclipse.swt.layout.GridLayout;
     52 import org.eclipse.swt.widgets.Button;
     53 import org.eclipse.swt.widgets.Composite;
     54 import org.eclipse.swt.widgets.Control;
     55 import org.eclipse.swt.widgets.Display;
     56 import org.eclipse.swt.widgets.Label;
     57 import org.eclipse.swt.widgets.Shell;
     58 import org.eclipse.swt.widgets.Table;
     59 
     60 /**
     61  * A dialog that lets the user choose a device to deploy an application.
     62  * The user can either choose an exiting running device (including running emulators)
     63  * or start a new emulator using an Android Virtual Device configuration that matches
     64  * the current project.
     65  */
     66 public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener {
     67 
     68     private final static int ICON_WIDTH = 16;
     69 
     70     private Table mDeviceTable;
     71     private TableViewer mViewer;
     72     private AvdSelector mPreferredAvdSelector;
     73 
     74     private Image mDeviceImage;
     75     private Image mEmulatorImage;
     76     private Image mMatchImage;
     77     private Image mNoMatchImage;
     78     private Image mWarningImage;
     79 
     80     private final DeviceChooserResponse mResponse;
     81     private final String mPackageName;
     82     private final IAndroidTarget mProjectTarget;
     83     private final Sdk mSdk;
     84 
     85     private Button mDeviceRadioButton;
     86     private Button mUseDeviceForFutureLaunchesCheckbox;
     87     private static boolean sUseDeviceForFutureLaunchesValue = false;
     88 
     89     private boolean mDisableAvdSelectionChange = false;
     90 
     91     /**
     92      * Basic Content Provider for a table full of {@link IDevice} objects. The input is
     93      * a {@link AndroidDebugBridge}.
     94      */
     95     private static class ContentProvider implements IStructuredContentProvider {
     96         @Override
     97         public Object[] getElements(Object inputElement) {
     98             if (inputElement instanceof AndroidDebugBridge) {
     99                 return ((AndroidDebugBridge)inputElement).getDevices();
    100             }
    101 
    102             return new Object[0];
    103         }
    104 
    105         @Override
    106         public void dispose() {
    107             // pass
    108         }
    109 
    110         @Override
    111         public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
    112             // pass
    113         }
    114     }
    115 
    116 
    117     /**
    118      * A Label Provider for the {@link TableViewer} in {@link DeviceChooserDialog}.
    119      * It provides labels and images for {@link IDevice} objects.
    120      */
    121     private class LabelProvider implements ITableLabelProvider {
    122 
    123         @Override
    124         public Image getColumnImage(Object element, int columnIndex) {
    125             if (element instanceof IDevice) {
    126                 IDevice device = (IDevice)element;
    127                 switch (columnIndex) {
    128                     case 0:
    129                         return device.isEmulator() ? mEmulatorImage : mDeviceImage;
    130 
    131                     case 2:
    132                         // check for compatibility.
    133                         if (device.isEmulator() == false) { // physical device
    134                             // get the version of the device
    135                             AndroidVersion deviceVersion = Sdk.getDeviceVersion(device);
    136                             if (deviceVersion == null) {
    137                                 return mWarningImage;
    138                             } else {
    139                                 if (deviceVersion.canRun(mProjectTarget.getVersion()) == false) {
    140                                     return mNoMatchImage;
    141                                 }
    142 
    143                                 // if the project is compiling against an add-on,
    144                                 // the optional API may be missing from the device.
    145                                 return mProjectTarget.isPlatform() ?
    146                                         mMatchImage : mWarningImage;
    147                             }
    148                         } else {
    149                             // get the AvdInfo
    150                             AvdInfo info = mSdk.getAvdManager().getAvd(device.getAvdName(),
    151                                     true /*validAvdOnly*/);
    152                             if (info == null) {
    153                                 return mWarningImage;
    154                             }
    155                             return mProjectTarget.canRunOn(info.getTarget()) ?
    156                                     mMatchImage : mNoMatchImage;
    157                         }
    158                 }
    159             }
    160 
    161             return null;
    162         }
    163 
    164         @Override
    165         public String getColumnText(Object element, int columnIndex) {
    166             if (element instanceof IDevice) {
    167                 IDevice device = (IDevice)element;
    168                 switch (columnIndex) {
    169                     case 0:
    170                         return device.getSerialNumber();
    171                     case 1:
    172                         if (device.isEmulator()) {
    173                             return device.getAvdName();
    174                         } else {
    175                             return "N/A"; // devices don't have AVD names.
    176                         }
    177                     case 2:
    178                         if (device.isEmulator()) {
    179                             AvdInfo info = mSdk.getAvdManager().getAvd(device.getAvdName(),
    180                                     true /*validAvdOnly*/);
    181                             if (info == null) {
    182                                 return "?";
    183                             }
    184                             return info.getTarget().getFullName();
    185                         } else {
    186                             String deviceBuild = device.getProperty(IDevice.PROP_BUILD_VERSION);
    187                             if (deviceBuild == null) {
    188                                 return "unknown";
    189                             }
    190                             return deviceBuild;
    191                         }
    192                     case 3:
    193                         String debuggable = device.getProperty(IDevice.PROP_DEBUGGABLE);
    194                         if (debuggable != null && debuggable.equals("1")) { //$NON-NLS-1$
    195                             return "Yes";
    196                         } else {
    197                             return "";
    198                         }
    199                     case 4:
    200                         return getStateString(device);
    201                 }
    202             }
    203 
    204             return null;
    205         }
    206 
    207         @Override
    208         public void addListener(ILabelProviderListener listener) {
    209             // pass
    210         }
    211 
    212         @Override
    213         public void dispose() {
    214             // pass
    215         }
    216 
    217         @Override
    218         public boolean isLabelProperty(Object element, String property) {
    219             // pass
    220             return false;
    221         }
    222 
    223         @Override
    224         public void removeListener(ILabelProviderListener listener) {
    225             // pass
    226         }
    227     }
    228 
    229     public static class DeviceChooserResponse {
    230         private AvdInfo mAvdToLaunch;
    231         private IDevice mDeviceToUse;
    232         private boolean mUseDeviceForFutureLaunches;
    233 
    234         public void setDeviceToUse(IDevice d) {
    235             mDeviceToUse = d;
    236             mAvdToLaunch = null;
    237         }
    238 
    239         public void setAvdToLaunch(AvdInfo avd) {
    240             mAvdToLaunch = avd;
    241             mDeviceToUse = null;
    242         }
    243 
    244         public IDevice getDeviceToUse() {
    245             return mDeviceToUse;
    246         }
    247 
    248         public AvdInfo getAvdToLaunch() {
    249             return mAvdToLaunch;
    250         }
    251 
    252         public void setUseDeviceForFutureLaunches(boolean en) {
    253             mUseDeviceForFutureLaunches = en;
    254         }
    255 
    256         public boolean useDeviceForFutureLaunches() {
    257             return mUseDeviceForFutureLaunches;
    258         }
    259     }
    260 
    261     public DeviceChooserDialog(Shell parent, DeviceChooserResponse response, String packageName,
    262             IAndroidTarget projectTarget) {
    263         super(parent);
    264         mResponse = response;
    265         mPackageName = packageName;
    266         mProjectTarget = projectTarget;
    267         mSdk = Sdk.getCurrent();
    268 
    269         AndroidDebugBridge.addDeviceChangeListener(this);
    270         loadImages();
    271     }
    272 
    273     private void cleanup() {
    274         // done listening.
    275         AndroidDebugBridge.removeDeviceChangeListener(this);
    276     }
    277 
    278     @Override
    279     protected void okPressed() {
    280         cleanup();
    281         super.okPressed();
    282     }
    283 
    284     @Override
    285     protected void cancelPressed() {
    286         cleanup();
    287         super.cancelPressed();
    288     }
    289 
    290     @Override
    291     protected Control createContents(Composite parent) {
    292         Control content = super.createContents(parent);
    293 
    294         // this must be called after createContents() has happened so that the
    295         // ok button has been created (it's created after the call to createDialogArea)
    296         updateDefaultSelection();
    297 
    298         return content;
    299     }
    300 
    301     /**
    302      * Create the button bar: We override the Dialog implementation of this method
    303      * so that we can create the checkbox at the same level as the 'Cancel' and 'OK' buttons.
    304      */
    305     @Override
    306     protected Control createButtonBar(Composite parent) {
    307         Composite composite = new Composite(parent, SWT.NONE);
    308 
    309         GridLayout layout = new GridLayout(1, false);
    310         layout.marginHeight = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN);
    311         composite.setLayout(layout);
    312         composite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
    313 
    314         mUseDeviceForFutureLaunchesCheckbox = new Button(composite, SWT.CHECK);
    315         mUseDeviceForFutureLaunchesCheckbox.setSelection(sUseDeviceForFutureLaunchesValue);
    316         mResponse.setUseDeviceForFutureLaunches(sUseDeviceForFutureLaunchesValue);
    317         mUseDeviceForFutureLaunchesCheckbox.setText("Use same device for future launches");
    318         mUseDeviceForFutureLaunchesCheckbox.addSelectionListener(new SelectionAdapter() {
    319             @Override
    320             public void widgetSelected(SelectionEvent e) {
    321                 sUseDeviceForFutureLaunchesValue =
    322                         mUseDeviceForFutureLaunchesCheckbox.getSelection();
    323                 mResponse.setUseDeviceForFutureLaunches(sUseDeviceForFutureLaunchesValue);
    324             }
    325         });
    326         mUseDeviceForFutureLaunchesCheckbox.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
    327 
    328         createButton(composite, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL, true);
    329         createButton(composite, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL, false);
    330 
    331         return composite;
    332     }
    333 
    334     @Override
    335     protected Control createDialogArea(Composite parent) {
    336         // set dialog title
    337         getShell().setText("Android Device Chooser");
    338 
    339         Composite top = new Composite(parent, SWT.NONE);
    340         top.setLayout(new GridLayout(1, true));
    341 
    342         Label label = new Label(top, SWT.NONE);
    343         label.setText(String.format("Select a device compatible with target %s.",
    344                 mProjectTarget.getFullName()));
    345 
    346         mDeviceRadioButton = new Button(top, SWT.RADIO);
    347         mDeviceRadioButton.setText("Choose a running Android device");
    348         mDeviceRadioButton.addSelectionListener(new SelectionAdapter() {
    349             @Override
    350             public void widgetSelected(SelectionEvent e) {
    351                 boolean deviceMode = mDeviceRadioButton.getSelection();
    352 
    353                 mDeviceTable.setEnabled(deviceMode);
    354                 mPreferredAvdSelector.setEnabled(!deviceMode);
    355 
    356                 if (deviceMode) {
    357                     handleDeviceSelection();
    358                 } else {
    359                     mResponse.setAvdToLaunch(mPreferredAvdSelector.getSelected());
    360                 }
    361 
    362                 enableOkButton();
    363             }
    364         });
    365         mDeviceRadioButton.setSelection(true);
    366 
    367 
    368         // offset the selector from the radio button
    369         Composite offsetComp = new Composite(top, SWT.NONE);
    370         offsetComp.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
    371         GridLayout layout = new GridLayout(1, false);
    372         layout.marginRight = layout.marginHeight = 0;
    373         layout.marginLeft = 30;
    374         offsetComp.setLayout(layout);
    375 
    376         mDeviceTable = new Table(offsetComp, SWT.SINGLE | SWT.FULL_SELECTION | SWT.BORDER);
    377         GridData gd;
    378         mDeviceTable.setLayoutData(gd = new GridData(GridData.FILL_BOTH));
    379         gd.heightHint = 100;
    380 
    381         mDeviceTable.setHeaderVisible(true);
    382         mDeviceTable.setLinesVisible(true);
    383 
    384         TableHelper.createTableColumn(mDeviceTable, "Serial Number",
    385                 SWT.LEFT, "AAA+AAAAAAAAAAAAAAAAAAA", //$NON-NLS-1$
    386                 null /* prefs name */, null /* prefs store */);
    387 
    388         TableHelper.createTableColumn(mDeviceTable, "AVD Name",
    389                 SWT.LEFT, "AAAAAAAAAAAAAAAAAAA", //$NON-NLS-1$
    390                 null /* prefs name */, null /* prefs store */);
    391 
    392         TableHelper.createTableColumn(mDeviceTable, "Target",
    393                 SWT.LEFT, "AAA+Android 9.9.9", //$NON-NLS-1$
    394                 null /* prefs name */, null /* prefs store */);
    395 
    396         TableHelper.createTableColumn(mDeviceTable, "Debug",
    397                 SWT.LEFT, "Debug", //$NON-NLS-1$
    398                 null /* prefs name */, null /* prefs store */);
    399 
    400         TableHelper.createTableColumn(mDeviceTable, "State",
    401                 SWT.LEFT, "bootloader", //$NON-NLS-1$
    402                 null /* prefs name */, null /* prefs store */);
    403 
    404         // create the viewer for it
    405         mViewer = new TableViewer(mDeviceTable);
    406         mViewer.setContentProvider(new ContentProvider());
    407         mViewer.setLabelProvider(new LabelProvider());
    408         mViewer.setInput(AndroidDebugBridge.getBridge());
    409 
    410         mDeviceTable.addSelectionListener(new SelectionAdapter() {
    411             /**
    412              * Handles single-click selection on the device selector.
    413              * {@inheritDoc}
    414              */
    415             @Override
    416             public void widgetSelected(SelectionEvent e) {
    417                 handleDeviceSelection();
    418             }
    419 
    420             /**
    421              * Handles double-click selection on the device selector.
    422              * Note that the single-click handler will probably already have been called.
    423              * {@inheritDoc}
    424              */
    425             @Override
    426             public void widgetDefaultSelected(SelectionEvent e) {
    427                 handleDeviceSelection();
    428                 if (isOkButtonEnabled()) {
    429                     okPressed();
    430                 }
    431             }
    432         });
    433 
    434         Button radio2 = new Button(top, SWT.RADIO);
    435         radio2.setText("Launch a new Android Virtual Device");
    436 
    437         // offset the selector from the radio button
    438         offsetComp = new Composite(top, SWT.NONE);
    439         offsetComp.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
    440         layout = new GridLayout(1, false);
    441         layout.marginRight = layout.marginHeight = 0;
    442         layout.marginLeft = 30;
    443         offsetComp.setLayout(layout);
    444 
    445         mPreferredAvdSelector = new AvdSelector(offsetComp,
    446                 mSdk.getSdkLocation(),
    447                 mSdk.getAvdManager(),
    448                 new NonRunningAvdFilter(),
    449                 DisplayMode.SIMPLE_SELECTION,
    450                 new AdtConsoleSdkLog());
    451         mPreferredAvdSelector.setTableHeightHint(100);
    452         mPreferredAvdSelector.setEnabled(false);
    453         mPreferredAvdSelector.setSelectionListener(new SelectionAdapter() {
    454             /**
    455              * Handles single-click selection on the AVD selector.
    456              * {@inheritDoc}
    457              */
    458             @Override
    459             public void widgetSelected(SelectionEvent e) {
    460                 if (mDisableAvdSelectionChange == false) {
    461                     mResponse.setAvdToLaunch(mPreferredAvdSelector.getSelected());
    462                     enableOkButton();
    463                 }
    464             }
    465 
    466             /**
    467              * Handles double-click selection on the AVD selector.
    468              *
    469              * Note that the single-click handler will probably already have been called
    470              * but the selected item can have changed in between.
    471              *
    472              * {@inheritDoc}
    473              */
    474             @Override
    475             public void widgetDefaultSelected(SelectionEvent e) {
    476                 widgetSelected(e);
    477                 if (isOkButtonEnabled()) {
    478                     okPressed();
    479                 }
    480             }
    481         });
    482 
    483         return top;
    484     }
    485 
    486     private void loadImages() {
    487         ImageLoader ddmUiLibLoader = ImageLoader.getDdmUiLibLoader();
    488         Display display = DdmsPlugin.getDisplay();
    489         IconFactory factory = IconFactory.getInstance();
    490 
    491         if (mDeviceImage == null) {
    492             mDeviceImage = ddmUiLibLoader.loadImage(display,
    493                     "device.png", //$NON-NLS-1$
    494                     ICON_WIDTH, ICON_WIDTH,
    495                     display.getSystemColor(SWT.COLOR_RED));
    496         }
    497         if (mEmulatorImage == null) {
    498             mEmulatorImage = ddmUiLibLoader.loadImage(display,
    499                     "emulator.png", ICON_WIDTH, ICON_WIDTH, //$NON-NLS-1$
    500                     display.getSystemColor(SWT.COLOR_BLUE));
    501         }
    502 
    503         if (mMatchImage == null) {
    504             mMatchImage = factory.getIcon("match", //$NON-NLS-1$
    505                     IconFactory.COLOR_GREEN,
    506                     IconFactory.SHAPE_DEFAULT);
    507         }
    508 
    509         if (mNoMatchImage == null) {
    510             mNoMatchImage = factory.getIcon("error", //$NON-NLS-1$
    511                     IconFactory.COLOR_RED,
    512                     IconFactory.SHAPE_DEFAULT);
    513         }
    514 
    515         if (mWarningImage == null) {
    516             mWarningImage = factory.getIcon("warning", //$NON-NLS-1$
    517                     SWT.COLOR_YELLOW,
    518                     IconFactory.SHAPE_DEFAULT);
    519         }
    520 
    521     }
    522 
    523     /**
    524      * Returns a display string representing the state of the device.
    525      * @param d the device
    526      */
    527     private static String getStateString(IDevice d) {
    528         DeviceState deviceState = d.getState();
    529         if (deviceState == DeviceState.ONLINE) {
    530             return "Online";
    531         } else if (deviceState == DeviceState.OFFLINE) {
    532             return "Offline";
    533         } else if (deviceState == DeviceState.BOOTLOADER) {
    534             return "Bootloader";
    535         }
    536 
    537         return "??";
    538     }
    539 
    540     /**
    541      * Sent when the a device is connected to the {@link AndroidDebugBridge}.
    542      * <p/>
    543      * This is sent from a non UI thread.
    544      * @param device the new device.
    545      *
    546      * @see IDeviceChangeListener#deviceConnected(IDevice)
    547      */
    548     @Override
    549     public void deviceConnected(IDevice device) {
    550         final DeviceChooserDialog dialog = this;
    551         exec(new Runnable() {
    552             @Override
    553             public void run() {
    554                 if (mDeviceTable.isDisposed() == false) {
    555                     // refresh all
    556                     mViewer.refresh();
    557 
    558                     // update the selection
    559                     updateDefaultSelection();
    560 
    561                     // update the display of AvdInfo (since it's filtered to only display
    562                     // non running AVD.)
    563                     refillAvdList(false /*reloadAvds*/);
    564                 } else {
    565                     // table is disposed, we need to do something.
    566                     // lets remove ourselves from the listener.
    567                     AndroidDebugBridge.removeDeviceChangeListener(dialog);
    568                 }
    569 
    570             }
    571         });
    572     }
    573 
    574     /**
    575      * Sent when the a device is connected to the {@link AndroidDebugBridge}.
    576      * <p/>
    577      * This is sent from a non UI thread.
    578      * @param device the new device.
    579      *
    580      * @see IDeviceChangeListener#deviceDisconnected(IDevice)
    581      */
    582     @Override
    583     public void deviceDisconnected(IDevice device) {
    584         deviceConnected(device);
    585     }
    586 
    587     /**
    588      * Sent when a device data changed, or when clients are started/terminated on the device.
    589      * <p/>
    590      * This is sent from a non UI thread.
    591      * @param device the device that was updated.
    592      * @param changeMask the mask indicating what changed.
    593      *
    594      * @see IDeviceChangeListener#deviceChanged(IDevice, int)
    595      */
    596     @Override
    597     public void deviceChanged(final IDevice device, int changeMask) {
    598         if ((changeMask & (IDevice.CHANGE_STATE | IDevice.CHANGE_BUILD_INFO)) != 0) {
    599             final DeviceChooserDialog dialog = this;
    600             exec(new Runnable() {
    601                 @Override
    602                 public void run() {
    603                     if (mDeviceTable.isDisposed() == false) {
    604                         // refresh the device
    605                         mViewer.refresh(device);
    606 
    607                         // update the defaultSelection.
    608                         updateDefaultSelection();
    609 
    610                         // update the display of AvdInfo (since it's filtered to only display
    611                         // non running AVD). This is done on deviceChanged because the avd name
    612                         // of a (emulator) device may be updated as the emulator boots.
    613 
    614                         refillAvdList(false /*reloadAvds*/);
    615 
    616                         // if the changed device is the current selection,
    617                         // we update the OK button based on its state.
    618                         if (device == mResponse.getDeviceToUse()) {
    619                             enableOkButton();
    620                         }
    621 
    622                     } else {
    623                         // table is disposed, we need to do something.
    624                         // lets remove ourselves from the listener.
    625                         AndroidDebugBridge.removeDeviceChangeListener(dialog);
    626                     }
    627                 }
    628             });
    629         }
    630     }
    631 
    632     /**
    633      * Returns whether the dialog is in "device" mode (true), or in "avd" mode (false).
    634      */
    635     private boolean isDeviceMode() {
    636         return mDeviceRadioButton.getSelection();
    637     }
    638 
    639     /**
    640      * Enables or disables the OK button of the dialog based on various selections in the dialog.
    641      */
    642     private void enableOkButton() {
    643         Button okButton = getButton(IDialogConstants.OK_ID);
    644 
    645         if (isDeviceMode()) {
    646             okButton.setEnabled(mResponse.getDeviceToUse() != null &&
    647                     mResponse.getDeviceToUse().isOnline());
    648         } else {
    649             okButton.setEnabled(mResponse.getAvdToLaunch() != null);
    650         }
    651     }
    652 
    653     /**
    654      * Returns true if the ok button is enabled.
    655      */
    656     private boolean isOkButtonEnabled() {
    657         Button okButton = getButton(IDialogConstants.OK_ID);
    658         return okButton.isEnabled();
    659     }
    660 
    661     /**
    662      * Executes the {@link Runnable} in the UI thread.
    663      * @param runnable the runnable to execute.
    664      */
    665     private void exec(Runnable runnable) {
    666         try {
    667             Display display = mDeviceTable.getDisplay();
    668             display.asyncExec(runnable);
    669         } catch (SWTException e) {
    670             // tree is disposed, we need to do something. lets remove ourselves from the listener.
    671             AndroidDebugBridge.removeDeviceChangeListener(this);
    672         }
    673     }
    674 
    675     private void handleDeviceSelection() {
    676         int count = mDeviceTable.getSelectionCount();
    677         if (count != 1) {
    678             handleSelection(null);
    679         } else {
    680             int index = mDeviceTable.getSelectionIndex();
    681             Object data = mViewer.getElementAt(index);
    682             if (data instanceof IDevice) {
    683                 handleSelection((IDevice)data);
    684             } else {
    685                 handleSelection(null);
    686             }
    687         }
    688     }
    689 
    690     private void handleSelection(IDevice device) {
    691         mResponse.setDeviceToUse(device);
    692         enableOkButton();
    693     }
    694 
    695     /**
    696      * Look for a default device to select. This is done by looking for the running
    697      * clients on each device and finding one similar to the one being launched.
    698      * <p/>
    699      * This is done every time the device list changed unless there is a already selection.
    700      */
    701     private void updateDefaultSelection() {
    702         if (mDeviceTable.getSelectionCount() == 0) {
    703             AndroidDebugBridge bridge = AndroidDebugBridge.getBridge();
    704 
    705             IDevice[] devices = bridge.getDevices();
    706 
    707             for (IDevice device : devices) {
    708                 Client[] clients = device.getClients();
    709 
    710                 for (Client client : clients) {
    711 
    712                     if (mPackageName.equals(client.getClientData().getClientDescription())) {
    713                         // found a match! Select it.
    714                         mViewer.setSelection(new StructuredSelection(device));
    715                         handleSelection(device);
    716 
    717                         // and we're done.
    718                         return;
    719                     }
    720                 }
    721             }
    722         }
    723 
    724         handleDeviceSelection();
    725     }
    726 
    727     private final class NonRunningAvdFilter implements IAvdFilter {
    728 
    729         private IDevice[] mDevices;
    730 
    731         @Override
    732         public void prepare() {
    733             mDevices = AndroidDebugBridge.getBridge().getDevices();
    734         }
    735 
    736         @Override
    737         public boolean accept(AvdInfo avd) {
    738             if (mDevices != null) {
    739                 for (IDevice d : mDevices) {
    740                     if (mProjectTarget.canRunOn(avd.getTarget()) == false ||
    741                             avd.getName().equals(d.getAvdName())) {
    742                         return false;
    743                     }
    744                 }
    745             }
    746 
    747             return true;
    748         }
    749 
    750         @Override
    751         public void cleanup() {
    752             mDevices = null;
    753         }
    754     }
    755 
    756     /**
    757      * Refills the AVD list keeping the current selection.
    758      */
    759     private void refillAvdList(boolean reloadAvds) {
    760         // save the current selection
    761         AvdInfo selected = mPreferredAvdSelector.getSelected();
    762 
    763         // disable selection change.
    764         mDisableAvdSelectionChange = true;
    765 
    766         // refresh the list
    767         mPreferredAvdSelector.refresh(false);
    768 
    769         // attempt to reselect the proper avd if needed
    770         if (selected != null) {
    771             if (mPreferredAvdSelector.setSelection(selected) == false) {
    772                 // looks like the selection is lost. this can happen if an emulator
    773                 // running the AVD that was selected was launched from outside of Eclipse).
    774                 mResponse.setAvdToLaunch(null);
    775                 enableOkButton();
    776             }
    777         }
    778 
    779         // enable the selection change
    780         mDisableAvdSelectionChange = false;
    781     }
    782 }
    783 
    784