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