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