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