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 boolean mUseDeviceForFutureLaunches; 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, boolean useDeviceForFutureLaunches)286 public DeviceChooserDialog(Shell parent, DeviceChooserResponse response, String packageName, 287 IAndroidTarget projectTarget, AndroidVersion minApiVersion, 288 boolean useDeviceForFutureLaunches) { 289 super(parent); 290 291 mResponse = response; 292 mPackageName = packageName; 293 mProjectTarget = projectTarget; 294 mMinApiVersion = minApiVersion; 295 mSdk = Sdk.getCurrent(); 296 mUseDeviceForFutureLaunches = useDeviceForFutureLaunches; 297 298 AndroidDebugBridge.addDeviceChangeListener(this); 299 loadImages(); 300 } 301 cleanup()302 private void cleanup() { 303 // done listening. 304 AndroidDebugBridge.removeDeviceChangeListener(this); 305 } 306 307 @Override okPressed()308 protected void okPressed() { 309 cleanup(); 310 super.okPressed(); 311 } 312 313 @Override cancelPressed()314 protected void cancelPressed() { 315 cleanup(); 316 super.cancelPressed(); 317 } 318 319 @Override createContents(Composite parent)320 protected Control createContents(Composite parent) { 321 Control content = super.createContents(parent); 322 323 // this must be called after createContents() has happened so that the 324 // ok button has been created (it's created after the call to createDialogArea) 325 updateDefaultSelection(); 326 327 return content; 328 } 329 330 /** 331 * Create the button bar: We override the Dialog implementation of this method 332 * so that we can create the checkbox at the same level as the 'Cancel' and 'OK' buttons. 333 */ 334 @Override createButtonBar(Composite parent)335 protected Control createButtonBar(Composite parent) { 336 Composite composite = new Composite(parent, SWT.NONE); 337 338 GridLayout layout = new GridLayout(1, false); 339 layout.marginHeight = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN); 340 composite.setLayout(layout); 341 composite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 342 343 mUseDeviceForFutureLaunchesCheckbox = new Button(composite, SWT.CHECK); 344 mUseDeviceForFutureLaunchesCheckbox.setSelection(mUseDeviceForFutureLaunches); 345 mResponse.setUseDeviceForFutureLaunches(mUseDeviceForFutureLaunches); 346 mUseDeviceForFutureLaunchesCheckbox.setText("Use same device for future launches"); 347 mUseDeviceForFutureLaunchesCheckbox.addSelectionListener(new SelectionAdapter() { 348 @Override 349 public void widgetSelected(SelectionEvent e) { 350 mUseDeviceForFutureLaunches = 351 mUseDeviceForFutureLaunchesCheckbox.getSelection(); 352 mResponse.setUseDeviceForFutureLaunches(mUseDeviceForFutureLaunches); 353 } 354 }); 355 mUseDeviceForFutureLaunchesCheckbox.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 356 357 createButton(composite, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL, true); 358 createButton(composite, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL, false); 359 360 return composite; 361 } 362 363 @Override createDialogArea(Composite parent)364 protected Control createDialogArea(Composite parent) { 365 // set dialog title 366 getShell().setText("Android Device Chooser"); 367 368 Composite top = new Composite(parent, SWT.NONE); 369 top.setLayout(new GridLayout(1, true)); 370 371 String msg; 372 if (mProjectTarget.isPlatform()) { 373 msg = String.format("Select a device with min API level %s.", 374 mMinApiVersion.getApiString()); 375 } else { 376 msg = String.format("Select a device compatible with target %s.", 377 mProjectTarget.getFullName()); 378 } 379 Label label = new Label(top, SWT.NONE); 380 label.setText(msg); 381 382 mDeviceRadioButton = new Button(top, SWT.RADIO); 383 mDeviceRadioButton.setText("Choose a running Android device"); 384 mDeviceRadioButton.addSelectionListener(new SelectionAdapter() { 385 @Override 386 public void widgetSelected(SelectionEvent e) { 387 boolean deviceMode = mDeviceRadioButton.getSelection(); 388 389 mDeviceTable.setEnabled(deviceMode); 390 mPreferredAvdSelector.setEnabled(!deviceMode); 391 392 if (deviceMode) { 393 handleDeviceSelection(); 394 } else { 395 mResponse.setAvdToLaunch(mPreferredAvdSelector.getSelected()); 396 } 397 398 enableOkButton(); 399 } 400 }); 401 mDeviceRadioButton.setSelection(true); 402 403 404 // offset the selector from the radio button 405 Composite offsetComp = new Composite(top, SWT.NONE); 406 offsetComp.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 407 GridLayout layout = new GridLayout(1, false); 408 layout.marginRight = layout.marginHeight = 0; 409 layout.marginLeft = 30; 410 offsetComp.setLayout(layout); 411 412 mDeviceTable = new Table(offsetComp, SWT.SINGLE | SWT.FULL_SELECTION | SWT.BORDER); 413 GridData gd; 414 mDeviceTable.setLayoutData(gd = new GridData(GridData.FILL_BOTH)); 415 gd.heightHint = 100; 416 417 mDeviceTable.setHeaderVisible(true); 418 mDeviceTable.setLinesVisible(true); 419 420 TableHelper.createTableColumn(mDeviceTable, "Serial Number", 421 SWT.LEFT, "AAA+AAAAAAAAAAAAAAAAAAA", //$NON-NLS-1$ 422 null /* prefs name */, null /* prefs store */); 423 424 TableHelper.createTableColumn(mDeviceTable, "AVD Name", 425 SWT.LEFT, "AAAAAAAAAAAAAAAAAAA", //$NON-NLS-1$ 426 null /* prefs name */, null /* prefs store */); 427 428 TableHelper.createTableColumn(mDeviceTable, "Target", 429 SWT.LEFT, "AAA+Android 9.9.9", //$NON-NLS-1$ 430 null /* prefs name */, null /* prefs store */); 431 432 TableHelper.createTableColumn(mDeviceTable, "Debug", 433 SWT.LEFT, "Debug", //$NON-NLS-1$ 434 null /* prefs name */, null /* prefs store */); 435 436 TableHelper.createTableColumn(mDeviceTable, "State", 437 SWT.LEFT, "bootloader", //$NON-NLS-1$ 438 null /* prefs name */, null /* prefs store */); 439 440 // create the viewer for it 441 mViewer = new TableViewer(mDeviceTable); 442 mViewer.setContentProvider(new ContentProvider()); 443 mViewer.setLabelProvider(new LabelProvider()); 444 mViewer.setInput(AndroidDebugBridge.getBridge()); 445 446 mDeviceTable.addSelectionListener(new SelectionAdapter() { 447 /** 448 * Handles single-click selection on the device selector. 449 * {@inheritDoc} 450 */ 451 @Override 452 public void widgetSelected(SelectionEvent e) { 453 handleDeviceSelection(); 454 } 455 456 /** 457 * Handles double-click selection on the device selector. 458 * Note that the single-click handler will probably already have been called. 459 * {@inheritDoc} 460 */ 461 @Override 462 public void widgetDefaultSelected(SelectionEvent e) { 463 handleDeviceSelection(); 464 if (isOkButtonEnabled()) { 465 okPressed(); 466 } 467 } 468 }); 469 470 Button radio2 = new Button(top, SWT.RADIO); 471 radio2.setText("Launch a new Android Virtual Device"); 472 473 // offset the selector from the radio button 474 offsetComp = new Composite(top, SWT.NONE); 475 offsetComp.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 476 layout = new GridLayout(1, false); 477 layout.marginRight = layout.marginHeight = 0; 478 layout.marginLeft = 30; 479 offsetComp.setLayout(layout); 480 481 mPreferredAvdSelector = new AvdSelector(offsetComp, 482 mSdk.getSdkOsLocation(), 483 mSdk.getAvdManager(), 484 new NonRunningAvdFilter(), 485 DisplayMode.SIMPLE_SELECTION, 486 new AdtConsoleSdkLog()); 487 mPreferredAvdSelector.setTableHeightHint(100); 488 mPreferredAvdSelector.setEnabled(false); 489 mPreferredAvdSelector.setSelectionListener(new SelectionAdapter() { 490 /** 491 * Handles single-click selection on the AVD selector. 492 * {@inheritDoc} 493 */ 494 @Override 495 public void widgetSelected(SelectionEvent e) { 496 if (mDisableAvdSelectionChange == false) { 497 mResponse.setAvdToLaunch(mPreferredAvdSelector.getSelected()); 498 enableOkButton(); 499 } 500 } 501 502 /** 503 * Handles double-click selection on the AVD selector. 504 * 505 * Note that the single-click handler will probably already have been called 506 * but the selected item can have changed in between. 507 * 508 * {@inheritDoc} 509 */ 510 @Override 511 public void widgetDefaultSelected(SelectionEvent e) { 512 widgetSelected(e); 513 if (isOkButtonEnabled()) { 514 okPressed(); 515 } 516 } 517 }); 518 519 return top; 520 } 521 loadImages()522 private void loadImages() { 523 ImageLoader ddmUiLibLoader = ImageLoader.getDdmUiLibLoader(); 524 Display display = DdmsPlugin.getDisplay(); 525 IconFactory factory = IconFactory.getInstance(); 526 527 if (mDeviceImage == null) { 528 mDeviceImage = ddmUiLibLoader.loadImage(display, 529 "device.png", //$NON-NLS-1$ 530 ICON_WIDTH, ICON_WIDTH, 531 display.getSystemColor(SWT.COLOR_RED)); 532 } 533 if (mEmulatorImage == null) { 534 mEmulatorImage = ddmUiLibLoader.loadImage(display, 535 "emulator.png", ICON_WIDTH, ICON_WIDTH, //$NON-NLS-1$ 536 display.getSystemColor(SWT.COLOR_BLUE)); 537 } 538 539 if (mMatchImage == null) { 540 mMatchImage = factory.getIcon("match", //$NON-NLS-1$ 541 IconFactory.COLOR_GREEN, 542 IconFactory.SHAPE_DEFAULT); 543 } 544 545 if (mNoMatchImage == null) { 546 mNoMatchImage = factory.getIcon("error", //$NON-NLS-1$ 547 IconFactory.COLOR_RED, 548 IconFactory.SHAPE_DEFAULT); 549 } 550 551 if (mWarningImage == null) { 552 mWarningImage = factory.getIcon("warning", //$NON-NLS-1$ 553 SWT.COLOR_YELLOW, 554 IconFactory.SHAPE_DEFAULT); 555 } 556 557 } 558 559 /** 560 * Returns a display string representing the state of the device. 561 * @param d the device 562 */ getStateString(IDevice d)563 private static String getStateString(IDevice d) { 564 DeviceState deviceState = d.getState(); 565 if (deviceState == DeviceState.ONLINE) { 566 return "Online"; 567 } else if (deviceState == DeviceState.OFFLINE) { 568 return "Offline"; 569 } else if (deviceState == DeviceState.BOOTLOADER) { 570 return "Bootloader"; 571 } 572 573 return "??"; 574 } 575 576 /** 577 * Sent when the a device is connected to the {@link AndroidDebugBridge}. 578 * <p/> 579 * This is sent from a non UI thread. 580 * @param device the new device. 581 * 582 * @see IDeviceChangeListener#deviceConnected(IDevice) 583 */ 584 @Override deviceConnected(IDevice device)585 public void deviceConnected(IDevice device) { 586 final DeviceChooserDialog dialog = this; 587 exec(new Runnable() { 588 @Override 589 public void run() { 590 if (mDeviceTable.isDisposed() == false) { 591 // refresh all 592 mViewer.refresh(); 593 594 // update the selection 595 updateDefaultSelection(); 596 597 // update the display of AvdInfo (since it's filtered to only display 598 // non running AVD.) 599 refillAvdList(false /*reloadAvds*/); 600 } else { 601 // table is disposed, we need to do something. 602 // lets remove ourselves from the listener. 603 AndroidDebugBridge.removeDeviceChangeListener(dialog); 604 } 605 606 } 607 }); 608 } 609 610 /** 611 * Sent when the a device is connected to the {@link AndroidDebugBridge}. 612 * <p/> 613 * This is sent from a non UI thread. 614 * @param device the new device. 615 * 616 * @see IDeviceChangeListener#deviceDisconnected(IDevice) 617 */ 618 @Override deviceDisconnected(IDevice device)619 public void deviceDisconnected(IDevice device) { 620 deviceConnected(device); 621 } 622 623 /** 624 * Sent when a device data changed, or when clients are started/terminated on the device. 625 * <p/> 626 * This is sent from a non UI thread. 627 * @param device the device that was updated. 628 * @param changeMask the mask indicating what changed. 629 * 630 * @see IDeviceChangeListener#deviceChanged(IDevice, int) 631 */ 632 @Override deviceChanged(final IDevice device, int changeMask)633 public void deviceChanged(final IDevice device, int changeMask) { 634 if ((changeMask & (IDevice.CHANGE_STATE | IDevice.CHANGE_BUILD_INFO)) != 0) { 635 final DeviceChooserDialog dialog = this; 636 exec(new Runnable() { 637 @Override 638 public void run() { 639 if (mDeviceTable.isDisposed() == false) { 640 // refresh the device 641 mViewer.refresh(device); 642 643 // update the defaultSelection. 644 updateDefaultSelection(); 645 646 // update the display of AvdInfo (since it's filtered to only display 647 // non running AVD). This is done on deviceChanged because the avd name 648 // of a (emulator) device may be updated as the emulator boots. 649 650 refillAvdList(false /*reloadAvds*/); 651 652 // if the changed device is the current selection, 653 // we update the OK button based on its state. 654 if (device == mResponse.getDeviceToUse()) { 655 enableOkButton(); 656 } 657 658 } else { 659 // table is disposed, we need to do something. 660 // lets remove ourselves from the listener. 661 AndroidDebugBridge.removeDeviceChangeListener(dialog); 662 } 663 } 664 }); 665 } 666 } 667 668 /** 669 * Returns whether the dialog is in "device" mode (true), or in "avd" mode (false). 670 */ isDeviceMode()671 private boolean isDeviceMode() { 672 return mDeviceRadioButton.getSelection(); 673 } 674 675 /** 676 * Enables or disables the OK button of the dialog based on various selections in the dialog. 677 */ enableOkButton()678 private void enableOkButton() { 679 Button okButton = getButton(IDialogConstants.OK_ID); 680 681 if (isDeviceMode()) { 682 okButton.setEnabled(mResponse.getDeviceToUse() != null && 683 mResponse.getDeviceToUse().isOnline()); 684 } else { 685 okButton.setEnabled(mResponse.getAvdToLaunch() != null); 686 } 687 } 688 689 /** 690 * Returns true if the ok button is enabled. 691 */ isOkButtonEnabled()692 private boolean isOkButtonEnabled() { 693 Button okButton = getButton(IDialogConstants.OK_ID); 694 return okButton.isEnabled(); 695 } 696 697 /** 698 * Executes the {@link Runnable} in the UI thread. 699 * @param runnable the runnable to execute. 700 */ exec(Runnable runnable)701 private void exec(Runnable runnable) { 702 try { 703 Display display = mDeviceTable.getDisplay(); 704 display.asyncExec(runnable); 705 } catch (SWTException e) { 706 // tree is disposed, we need to do something. lets remove ourselves from the listener. 707 AndroidDebugBridge.removeDeviceChangeListener(this); 708 } 709 } 710 handleDeviceSelection()711 private void handleDeviceSelection() { 712 int count = mDeviceTable.getSelectionCount(); 713 if (count != 1) { 714 handleSelection(null); 715 } else { 716 int index = mDeviceTable.getSelectionIndex(); 717 Object data = mViewer.getElementAt(index); 718 if (data instanceof IDevice) { 719 handleSelection((IDevice)data); 720 } else { 721 handleSelection(null); 722 } 723 } 724 } 725 handleSelection(IDevice device)726 private void handleSelection(IDevice device) { 727 mResponse.setDeviceToUse(device); 728 enableOkButton(); 729 } 730 731 /** 732 * Look for a default device to select. This is done by looking for the running 733 * clients on each device and finding one similar to the one being launched. 734 * <p/> 735 * This is done every time the device list changed unless there is a already selection. 736 */ updateDefaultSelection()737 private void updateDefaultSelection() { 738 if (mDeviceTable.getSelectionCount() == 0) { 739 AndroidDebugBridge bridge = AndroidDebugBridge.getBridge(); 740 741 IDevice[] devices = bridge.getDevices(); 742 743 for (IDevice device : devices) { 744 Client[] clients = device.getClients(); 745 746 for (Client client : clients) { 747 748 if (mPackageName.equals(client.getClientData().getClientDescription())) { 749 // found a match! Select it. 750 mViewer.setSelection(new StructuredSelection(device)); 751 handleSelection(device); 752 753 // and we're done. 754 return; 755 } 756 } 757 } 758 } 759 760 handleDeviceSelection(); 761 } 762 763 private final class NonRunningAvdFilter implements IAvdFilter { 764 765 private IDevice[] mDevices; 766 767 @Override prepare()768 public void prepare() { 769 mDevices = AndroidDebugBridge.getBridge().getDevices(); 770 } 771 772 @Override accept(AvdInfo avd)773 public boolean accept(AvdInfo avd) { 774 if (mDevices != null) { 775 for (IDevice d : mDevices) { 776 // do not accept running avd's 777 if (avd.getName().equals(d.getAvdName())) { 778 return false; 779 } 780 781 // only accept avd's that can actually run the project 782 AvdCompatibility.Compatibility c = 783 AvdCompatibility.canRun(avd, mProjectTarget, mMinApiVersion); 784 return (c == AvdCompatibility.Compatibility.NO) ? false : true; 785 } 786 } 787 788 return true; 789 } 790 791 @Override cleanup()792 public void cleanup() { 793 mDevices = null; 794 } 795 } 796 797 /** 798 * Refills the AVD list keeping the current selection. 799 */ refillAvdList(boolean reloadAvds)800 private void refillAvdList(boolean reloadAvds) { 801 // save the current selection 802 AvdInfo selected = mPreferredAvdSelector.getSelected(); 803 804 // disable selection change. 805 mDisableAvdSelectionChange = true; 806 807 // refresh the list 808 mPreferredAvdSelector.refresh(false); 809 810 // attempt to reselect the proper avd if needed 811 if (selected != null) { 812 if (mPreferredAvdSelector.setSelection(selected) == false) { 813 // looks like the selection is lost. this can happen if an emulator 814 // running the AVD that was selected was launched from outside of Eclipse). 815 mResponse.setAvdToLaunch(null); 816 enableOkButton(); 817 } 818 } 819 820 // enable the selection change 821 mDisableAvdSelectionChange = false; 822 } 823 } 824 825