• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
getElements(Object inputElement)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
dispose()106         public void dispose() {
107             // pass
108         }
109 
110         @Override
inputChanged(Viewer viewer, Object oldInput, Object newInput)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
getColumnImage(Object element, int columnIndex)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
getColumnText(Object element, int columnIndex)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
addListener(ILabelProviderListener listener)208         public void addListener(ILabelProviderListener listener) {
209             // pass
210         }
211 
212         @Override
dispose()213         public void dispose() {
214             // pass
215         }
216 
217         @Override
isLabelProperty(Object element, String property)218         public boolean isLabelProperty(Object element, String property) {
219             // pass
220             return false;
221         }
222 
223         @Override
removeListener(ILabelProviderListener listener)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 
setDeviceToUse(IDevice d)234         public void setDeviceToUse(IDevice d) {
235             mDeviceToUse = d;
236             mAvdToLaunch = null;
237         }
238 
setAvdToLaunch(AvdInfo avd)239         public void setAvdToLaunch(AvdInfo avd) {
240             mAvdToLaunch = avd;
241             mDeviceToUse = null;
242         }
243 
getDeviceToUse()244         public IDevice getDeviceToUse() {
245             return mDeviceToUse;
246         }
247 
getAvdToLaunch()248         public AvdInfo getAvdToLaunch() {
249             return mAvdToLaunch;
250         }
251 
setUseDeviceForFutureLaunches(boolean en)252         public void setUseDeviceForFutureLaunches(boolean en) {
253             mUseDeviceForFutureLaunches = en;
254         }
255 
useDeviceForFutureLaunches()256         public boolean useDeviceForFutureLaunches() {
257             return mUseDeviceForFutureLaunches;
258         }
259     }
260 
DeviceChooserDialog(Shell parent, DeviceChooserResponse response, String packageName, IAndroidTarget projectTarget)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 
cleanup()273     private void cleanup() {
274         // done listening.
275         AndroidDebugBridge.removeDeviceChangeListener(this);
276     }
277 
278     @Override
okPressed()279     protected void okPressed() {
280         cleanup();
281         super.okPressed();
282     }
283 
284     @Override
cancelPressed()285     protected void cancelPressed() {
286         cleanup();
287         super.cancelPressed();
288     }
289 
290     @Override
createContents(Composite parent)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
createButtonBar(Composite parent)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
createDialogArea(Composite parent)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 
loadImages()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      */
getStateString(IDevice d)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
deviceConnected(IDevice device)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
deviceDisconnected(IDevice device)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
deviceChanged(final IDevice device, int changeMask)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      */
isDeviceMode()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      */
enableOkButton()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      */
isOkButtonEnabled()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      */
exec(Runnable runnable)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 
handleDeviceSelection()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 
handleSelection(IDevice device)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      */
updateDefaultSelection()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
prepare()732         public void prepare() {
733             mDevices = AndroidDebugBridge.getBridge().getDevices();
734         }
735 
736         @Override
accept(AvdInfo avd)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
cleanup()751         public void cleanup() {
752             mDevices = null;
753         }
754     }
755 
756     /**
757      * Refills the AVD list keeping the current selection.
758      */
refillAvdList(boolean reloadAvds)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