• 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 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
getElements(Object inputElement)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 
findCompatibleDevices(IDevice[] devices)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
dispose()126         public void dispose() {
127             // pass
128         }
129 
130         @Override
inputChanged(Viewer viewer, Object oldInput, Object newInput)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
getColumnImage(Object element, int columnIndex)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
getColumnText(Object element, int columnIndex)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
addListener(ILabelProviderListener listener)233         public void addListener(ILabelProviderListener listener) {
234             // pass
235         }
236 
237         @Override
dispose()238         public void dispose() {
239             // pass
240         }
241 
242         @Override
isLabelProperty(Object element, String property)243         public boolean isLabelProperty(Object element, String property) {
244             // pass
245             return false;
246         }
247 
248         @Override
removeListener(ILabelProviderListener listener)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 
setDeviceToUse(IDevice d)259         public void setDeviceToUse(IDevice d) {
260             mDeviceToUse = d;
261             mAvdToLaunch = null;
262         }
263 
setAvdToLaunch(AvdInfo avd)264         public void setAvdToLaunch(AvdInfo avd) {
265             mAvdToLaunch = avd;
266             mDeviceToUse = null;
267         }
268 
getDeviceToUse()269         public IDevice getDeviceToUse() {
270             return mDeviceToUse;
271         }
272 
getAvdToLaunch()273         public AvdInfo getAvdToLaunch() {
274             return mAvdToLaunch;
275         }
276 
setUseDeviceForFutureLaunches(boolean en)277         public void setUseDeviceForFutureLaunches(boolean en) {
278             mUseDeviceForFutureLaunches = en;
279         }
280 
useDeviceForFutureLaunches()281         public boolean useDeviceForFutureLaunches() {
282             return mUseDeviceForFutureLaunches;
283         }
284     }
285 
DeviceChooserDialog(Shell parent, DeviceChooserResponse response, String packageName, IAndroidTarget projectTarget, AndroidVersion minApiVersion)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 
cleanup()300     private void cleanup() {
301         // done listening.
302         AndroidDebugBridge.removeDeviceChangeListener(this);
303     }
304 
305     @Override
okPressed()306     protected void okPressed() {
307         cleanup();
308         super.okPressed();
309     }
310 
311     @Override
cancelPressed()312     protected void cancelPressed() {
313         cleanup();
314         super.cancelPressed();
315     }
316 
317     @Override
createContents(Composite parent)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
createButtonBar(Composite parent)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
createDialogArea(Composite parent)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 
loadImages()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      */
getStateString(IDevice d)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
deviceConnected(IDevice device)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
deviceDisconnected(IDevice device)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
deviceChanged(final IDevice device, int changeMask)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      */
isDeviceMode()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      */
enableOkButton()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      */
isOkButtonEnabled()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      */
exec(Runnable runnable)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 
handleDeviceSelection()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 
handleSelection(IDevice device)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      */
updateDefaultSelection()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
prepare()766         public void prepare() {
767             mDevices = AndroidDebugBridge.getBridge().getDevices();
768         }
769 
770         @Override
accept(AvdInfo avd)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
cleanup()790         public void cleanup() {
791             mDevices = null;
792         }
793     }
794 
795     /**
796      * Refills the AVD list keeping the current selection.
797      */
refillAvdList(boolean reloadAvds)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