1/* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 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 17package com.android.ddmuilib; 18 19import com.android.ddmlib.AndroidDebugBridge; 20import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener; 21import com.android.ddmlib.AndroidDebugBridge.IDebugBridgeChangeListener; 22import com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener; 23import com.android.ddmlib.Client; 24import com.android.ddmlib.ClientData; 25import com.android.ddmlib.ClientData.DebuggerStatus; 26import com.android.ddmlib.DdmPreferences; 27import com.android.ddmlib.IDevice; 28import com.android.ddmlib.IDevice.DeviceState; 29 30import org.eclipse.jface.preference.IPreferenceStore; 31import org.eclipse.jface.viewers.ILabelProviderListener; 32import org.eclipse.jface.viewers.ITableLabelProvider; 33import org.eclipse.jface.viewers.ITreeContentProvider; 34import org.eclipse.jface.viewers.TreePath; 35import org.eclipse.jface.viewers.TreeSelection; 36import org.eclipse.jface.viewers.TreeViewer; 37import org.eclipse.jface.viewers.Viewer; 38import org.eclipse.swt.SWT; 39import org.eclipse.swt.SWTException; 40import org.eclipse.swt.events.SelectionAdapter; 41import org.eclipse.swt.events.SelectionEvent; 42import org.eclipse.swt.graphics.Image; 43import org.eclipse.swt.layout.FillLayout; 44import org.eclipse.swt.widgets.Composite; 45import org.eclipse.swt.widgets.Control; 46import org.eclipse.swt.widgets.Display; 47import org.eclipse.swt.widgets.Tree; 48import org.eclipse.swt.widgets.TreeColumn; 49import org.eclipse.swt.widgets.TreeItem; 50 51import java.util.ArrayList; 52import java.util.Locale; 53 54/** 55 * A display of both the devices and their clients. 56 */ 57public final class DevicePanel extends Panel implements IDebugBridgeChangeListener, 58 IDeviceChangeListener, IClientChangeListener { 59 60 private final static String PREFS_COL_NAME_SERIAL = "devicePanel.Col0"; //$NON-NLS-1$ 61 private final static String PREFS_COL_PID_STATE = "devicePanel.Col1"; //$NON-NLS-1$ 62 private final static String PREFS_COL_PORT_BUILD = "devicePanel.Col4"; //$NON-NLS-1$ 63 64 private final static int DEVICE_COL_SERIAL = 0; 65 private final static int DEVICE_COL_STATE = 1; 66 // col 2, 3 not used. 67 private final static int DEVICE_COL_BUILD = 4; 68 69 private final static int CLIENT_COL_NAME = 0; 70 private final static int CLIENT_COL_PID = 1; 71 private final static int CLIENT_COL_THREAD = 2; 72 private final static int CLIENT_COL_HEAP = 3; 73 private final static int CLIENT_COL_PORT = 4; 74 75 public final static int ICON_WIDTH = 16; 76 public final static String ICON_THREAD = "thread.png"; //$NON-NLS-1$ 77 public final static String ICON_HEAP = "heap.png"; //$NON-NLS-1$ 78 public final static String ICON_HALT = "halt.png"; //$NON-NLS-1$ 79 public final static String ICON_GC = "gc.png"; //$NON-NLS-1$ 80 public final static String ICON_HPROF = "hprof.png"; //$NON-NLS-1$ 81 public final static String ICON_TRACING_START = "tracing_start.png"; //$NON-NLS-1$ 82 public final static String ICON_TRACING_STOP = "tracing_stop.png"; //$NON-NLS-1$ 83 84 private IDevice mCurrentDevice; 85 private Client mCurrentClient; 86 87 private Tree mTree; 88 private TreeViewer mTreeViewer; 89 90 private Image mDeviceImage; 91 private Image mEmulatorImage; 92 93 private Image mThreadImage; 94 private Image mHeapImage; 95 private Image mWaitingImage; 96 private Image mDebuggerImage; 97 private Image mDebugErrorImage; 98 99 private final ArrayList<IUiSelectionListener> mListeners = new ArrayList<IUiSelectionListener>(); 100 101 private final ArrayList<IDevice> mDevicesToExpand = new ArrayList<IDevice>(); 102 103 private boolean mAdvancedPortSupport; 104 105 /** 106 * A Content provider for the {@link TreeViewer}. 107 * <p/> 108 * The input is a {@link AndroidDebugBridge}. First level elements are {@link IDevice} objects, 109 * and second level elements are {@link Client} object. 110 */ 111 private class ContentProvider implements ITreeContentProvider { 112 @Override 113 public Object[] getChildren(Object parentElement) { 114 if (parentElement instanceof IDevice) { 115 return ((IDevice)parentElement).getClients(); 116 } 117 return new Object[0]; 118 } 119 120 @Override 121 public Object getParent(Object element) { 122 if (element instanceof Client) { 123 return ((Client)element).getDevice(); 124 } 125 return null; 126 } 127 128 @Override 129 public boolean hasChildren(Object element) { 130 if (element instanceof IDevice) { 131 return ((IDevice)element).hasClients(); 132 } 133 134 // Clients never have children. 135 return false; 136 } 137 138 @Override 139 public Object[] getElements(Object inputElement) { 140 if (inputElement instanceof AndroidDebugBridge) { 141 return ((AndroidDebugBridge)inputElement).getDevices(); 142 } 143 return new Object[0]; 144 } 145 146 @Override 147 public void dispose() { 148 // pass 149 } 150 151 @Override 152 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { 153 // pass 154 } 155 } 156 157 /** 158 * A Label Provider for the {@link TreeViewer} in {@link DevicePanel}. It provides 159 * labels and images for {@link IDevice} and {@link Client} objects. 160 */ 161 private class LabelProvider implements ITableLabelProvider { 162 @Override 163 public Image getColumnImage(Object element, int columnIndex) { 164 if (columnIndex == DEVICE_COL_SERIAL && element instanceof IDevice) { 165 IDevice device = (IDevice)element; 166 if (device.isEmulator()) { 167 return mEmulatorImage; 168 } 169 170 return mDeviceImage; 171 } else if (element instanceof Client) { 172 Client client = (Client)element; 173 ClientData cd = client.getClientData(); 174 175 switch (columnIndex) { 176 case CLIENT_COL_NAME: 177 switch (cd.getDebuggerConnectionStatus()) { 178 case DEFAULT: 179 return null; 180 case WAITING: 181 return mWaitingImage; 182 case ATTACHED: 183 return mDebuggerImage; 184 case ERROR: 185 return mDebugErrorImage; 186 } 187 return null; 188 case CLIENT_COL_THREAD: 189 if (client.isThreadUpdateEnabled()) { 190 return mThreadImage; 191 } 192 return null; 193 case CLIENT_COL_HEAP: 194 if (client.isHeapUpdateEnabled()) { 195 return mHeapImage; 196 } 197 return null; 198 } 199 } 200 return null; 201 } 202 203 @Override 204 public String getColumnText(Object element, int columnIndex) { 205 if (element instanceof IDevice) { 206 IDevice device = (IDevice)element; 207 switch (columnIndex) { 208 case DEVICE_COL_SERIAL: 209 return device.getName(); 210 case DEVICE_COL_STATE: 211 return getStateString(device); 212 case DEVICE_COL_BUILD: { 213 String version = device.getProperty(IDevice.PROP_BUILD_VERSION); 214 if (version != null) { 215 String debuggable = device.getProperty(IDevice.PROP_DEBUGGABLE); 216 if (device.isEmulator()) { 217 String avdName = device.getAvdName(); 218 if (avdName == null) { 219 avdName = "?"; // the device is probably not online yet, so 220 // we don't know its AVD name just yet. 221 } 222 if (debuggable != null && debuggable.equals("1")) { //$NON-NLS-1$ 223 return String.format("%1$s [%2$s, debug]", avdName, 224 version); 225 } else { 226 return String.format("%1$s [%2$s]", avdName, version); //$NON-NLS-1$ 227 } 228 } else { 229 if (debuggable != null && debuggable.equals("1")) { //$NON-NLS-1$ 230 return String.format("%1$s, debug", version); 231 } else { 232 return String.format("%1$s", version); //$NON-NLS-1$ 233 } 234 } 235 } else { 236 return "unknown"; 237 } 238 } 239 } 240 } else if (element instanceof Client) { 241 Client client = (Client)element; 242 ClientData cd = client.getClientData(); 243 244 switch (columnIndex) { 245 case CLIENT_COL_NAME: 246 String name = cd.getClientDescription(); 247 if (name != null) { 248 if (cd.isValidUserId()) { 249 return String.format(Locale.US, "%s (%d)", name, cd.getUserId()); 250 } else { 251 return name; 252 } 253 } 254 return "?"; 255 case CLIENT_COL_PID: 256 return Integer.toString(cd.getPid()); 257 case CLIENT_COL_PORT: 258 if (mAdvancedPortSupport) { 259 int port = client.getDebuggerListenPort(); 260 String portString = "?"; 261 if (port != 0) { 262 portString = Integer.toString(port); 263 } 264 if (client.isSelectedClient()) { 265 return String.format("%1$s / %2$d", portString, //$NON-NLS-1$ 266 DdmPreferences.getSelectedDebugPort()); 267 } 268 269 return portString; 270 } 271 } 272 } 273 return null; 274 } 275 276 @Override 277 public void addListener(ILabelProviderListener listener) { 278 // pass 279 } 280 281 @Override 282 public void dispose() { 283 // pass 284 } 285 286 @Override 287 public boolean isLabelProperty(Object element, String property) { 288 // pass 289 return false; 290 } 291 292 @Override 293 public void removeListener(ILabelProviderListener listener) { 294 // pass 295 } 296 } 297 298 /** 299 * Classes which implement this interface provide methods that deals 300 * with {@link IDevice} and {@link Client} selection changes coming from the ui. 301 */ 302 public interface IUiSelectionListener { 303 /** 304 * Sent when a new {@link IDevice} and {@link Client} are selected. 305 * @param selectedDevice the selected device. If null, no devices are selected. 306 * @param selectedClient The selected client. If null, no clients are selected. 307 */ 308 public void selectionChanged(IDevice selectedDevice, Client selectedClient); 309 } 310 311 /** 312 * Creates the {@link DevicePanel} object. 313 * @param loader 314 * @param advancedPortSupport if true the device panel will add support for selected client port 315 * and display the ports in the ui. 316 */ 317 public DevicePanel(boolean advancedPortSupport) { 318 mAdvancedPortSupport = advancedPortSupport; 319 } 320 321 public void addSelectionListener(IUiSelectionListener listener) { 322 mListeners.add(listener); 323 } 324 325 public void removeSelectionListener(IUiSelectionListener listener) { 326 mListeners.remove(listener); 327 } 328 329 @Override 330 protected Control createControl(Composite parent) { 331 loadImages(parent.getDisplay()); 332 333 parent.setLayout(new FillLayout()); 334 335 // create the tree and its column 336 mTree = new Tree(parent, SWT.SINGLE | SWT.FULL_SELECTION); 337 mTree.setHeaderVisible(true); 338 mTree.setLinesVisible(true); 339 340 IPreferenceStore store = DdmUiPreferences.getStore(); 341 342 TableHelper.createTreeColumn(mTree, "Name", SWT.LEFT, 343 "com.android.home", //$NON-NLS-1$ 344 PREFS_COL_NAME_SERIAL, store); 345 TableHelper.createTreeColumn(mTree, "", SWT.LEFT, //$NON-NLS-1$ 346 "Offline", //$NON-NLS-1$ 347 PREFS_COL_PID_STATE, store); 348 349 TreeColumn col = new TreeColumn(mTree, SWT.NONE); 350 col.setWidth(ICON_WIDTH + 8); 351 col.setResizable(false); 352 col = new TreeColumn(mTree, SWT.NONE); 353 col.setWidth(ICON_WIDTH + 8); 354 col.setResizable(false); 355 356 TableHelper.createTreeColumn(mTree, "", SWT.LEFT, //$NON-NLS-1$ 357 "9999-9999", //$NON-NLS-1$ 358 PREFS_COL_PORT_BUILD, store); 359 360 // create the tree viewer 361 mTreeViewer = new TreeViewer(mTree); 362 363 // make the device auto expanded. 364 mTreeViewer.setAutoExpandLevel(TreeViewer.ALL_LEVELS); 365 366 // set up the content and label providers. 367 mTreeViewer.setContentProvider(new ContentProvider()); 368 mTreeViewer.setLabelProvider(new LabelProvider()); 369 370 mTree.addSelectionListener(new SelectionAdapter() { 371 @Override 372 public void widgetSelected(SelectionEvent e) { 373 notifyListeners(); 374 } 375 }); 376 377 return mTree; 378 } 379 380 /** 381 * Sets the focus to the proper control inside the panel. 382 */ 383 @Override 384 public void setFocus() { 385 mTree.setFocus(); 386 } 387 388 @Override 389 protected void postCreation() { 390 // ask for notification of changes in AndroidDebugBridge (a new one is created when 391 // adb is restarted from a different location), IDevice and Client objects. 392 AndroidDebugBridge.addDebugBridgeChangeListener(this); 393 AndroidDebugBridge.addDeviceChangeListener(this); 394 AndroidDebugBridge.addClientChangeListener(this); 395 } 396 397 public void dispose() { 398 AndroidDebugBridge.removeDebugBridgeChangeListener(this); 399 AndroidDebugBridge.removeDeviceChangeListener(this); 400 AndroidDebugBridge.removeClientChangeListener(this); 401 } 402 403 /** 404 * Returns the selected {@link Client}. May be null. 405 */ 406 public Client getSelectedClient() { 407 return mCurrentClient; 408 } 409 410 /** 411 * Returns the selected {@link IDevice}. If a {@link Client} is selected, it returns the 412 * IDevice object containing the client. 413 */ 414 public IDevice getSelectedDevice() { 415 return mCurrentDevice; 416 } 417 418 /** 419 * Kills the selected {@link Client} by sending its VM a halt command. 420 */ 421 public void killSelectedClient() { 422 if (mCurrentClient != null) { 423 Client client = mCurrentClient; 424 425 // reset the selection to the device. 426 TreePath treePath = new TreePath(new Object[] { mCurrentDevice }); 427 TreeSelection treeSelection = new TreeSelection(treePath); 428 mTreeViewer.setSelection(treeSelection); 429 430 client.kill(); 431 } 432 } 433 434 /** 435 * Forces a GC on the selected {@link Client}. 436 */ 437 public void forceGcOnSelectedClient() { 438 if (mCurrentClient != null) { 439 mCurrentClient.executeGarbageCollector(); 440 } 441 } 442 443 public void dumpHprof() { 444 if (mCurrentClient != null) { 445 mCurrentClient.dumpHprof(); 446 } 447 } 448 449 public void toggleMethodProfiling() { 450 if (mCurrentClient != null) { 451 mCurrentClient.toggleMethodProfiling(); 452 } 453 } 454 455 public void setEnabledHeapOnSelectedClient(boolean enable) { 456 if (mCurrentClient != null) { 457 mCurrentClient.setHeapUpdateEnabled(enable); 458 } 459 } 460 461 public void setEnabledThreadOnSelectedClient(boolean enable) { 462 if (mCurrentClient != null) { 463 mCurrentClient.setThreadUpdateEnabled(enable); 464 } 465 } 466 467 /** 468 * Sent when a new {@link AndroidDebugBridge} is started. 469 * <p/> 470 * This is sent from a non UI thread. 471 * @param bridge the new {@link AndroidDebugBridge} object. 472 * 473 * @see IDebugBridgeChangeListener#serverChanged(AndroidDebugBridge) 474 */ 475 @Override 476 public void bridgeChanged(final AndroidDebugBridge bridge) { 477 if (mTree.isDisposed() == false) { 478 exec(new Runnable() { 479 @Override 480 public void run() { 481 if (mTree.isDisposed() == false) { 482 // set up the data source. 483 mTreeViewer.setInput(bridge); 484 485 // notify the listener of a possible selection change. 486 notifyListeners(); 487 } else { 488 // tree is disposed, we need to do something. 489 // lets remove ourselves from the listener. 490 AndroidDebugBridge.removeDebugBridgeChangeListener(DevicePanel.this); 491 AndroidDebugBridge.removeDeviceChangeListener(DevicePanel.this); 492 AndroidDebugBridge.removeClientChangeListener(DevicePanel.this); 493 } 494 } 495 }); 496 } 497 498 // all current devices are obsolete 499 synchronized (mDevicesToExpand) { 500 mDevicesToExpand.clear(); 501 } 502 } 503 504 /** 505 * Sent when the a device is connected to the {@link AndroidDebugBridge}. 506 * <p/> 507 * This is sent from a non UI thread. 508 * @param device the new device. 509 * 510 * @see IDeviceChangeListener#deviceConnected(IDevice) 511 */ 512 @Override 513 public void deviceConnected(IDevice device) { 514 exec(new Runnable() { 515 @Override 516 public void run() { 517 if (mTree.isDisposed() == false) { 518 // refresh all 519 mTreeViewer.refresh(); 520 521 // notify the listener of a possible selection change. 522 notifyListeners(); 523 } else { 524 // tree is disposed, we need to do something. 525 // lets remove ourselves from the listener. 526 AndroidDebugBridge.removeDebugBridgeChangeListener(DevicePanel.this); 527 AndroidDebugBridge.removeDeviceChangeListener(DevicePanel.this); 528 AndroidDebugBridge.removeClientChangeListener(DevicePanel.this); 529 } 530 } 531 }); 532 533 // if it doesn't have clients yet, it'll need to be manually expanded when it gets them. 534 if (device.hasClients() == false) { 535 synchronized (mDevicesToExpand) { 536 mDevicesToExpand.add(device); 537 } 538 } 539 } 540 541 /** 542 * Sent when the a device is connected to the {@link AndroidDebugBridge}. 543 * <p/> 544 * This is sent from a non UI thread. 545 * @param device the new device. 546 * 547 * @see IDeviceChangeListener#deviceDisconnected(IDevice) 548 */ 549 @Override 550 public void deviceDisconnected(IDevice device) { 551 deviceConnected(device); 552 553 // just in case, we remove it from the list of devices to expand. 554 synchronized (mDevicesToExpand) { 555 mDevicesToExpand.remove(device); 556 } 557 } 558 559 /** 560 * Sent when a device data changed, or when clients are started/terminated on the device. 561 * <p/> 562 * This is sent from a non UI thread. 563 * @param device the device that was updated. 564 * @param changeMask the mask indicating what changed. 565 * 566 * @see IDeviceChangeListener#deviceChanged(IDevice) 567 */ 568 @Override 569 public void deviceChanged(final IDevice device, int changeMask) { 570 boolean expand = false; 571 synchronized (mDevicesToExpand) { 572 int index = mDevicesToExpand.indexOf(device); 573 if (device.hasClients() && index != -1) { 574 mDevicesToExpand.remove(index); 575 expand = true; 576 } 577 } 578 579 final boolean finalExpand = expand; 580 581 exec(new Runnable() { 582 @Override 583 public void run() { 584 if (mTree.isDisposed() == false) { 585 // look if the current device is selected. This is done in case the current 586 // client of this particular device was killed. In this case, we'll need to 587 // manually reselect the device. 588 589 IDevice selectedDevice = getSelectedDevice(); 590 591 // refresh the device 592 mTreeViewer.refresh(device); 593 594 // if the selected device was the changed device and the new selection is 595 // empty, we reselect the device. 596 if (selectedDevice == device && mTreeViewer.getSelection().isEmpty()) { 597 mTreeViewer.setSelection(new TreeSelection(new TreePath( 598 new Object[] { device }))); 599 } 600 601 // notify the listener of a possible selection change. 602 notifyListeners(); 603 604 if (finalExpand) { 605 mTreeViewer.setExpandedState(device, true); 606 } 607 } else { 608 // tree is disposed, we need to do something. 609 // lets remove ourselves from the listener. 610 AndroidDebugBridge.removeDebugBridgeChangeListener(DevicePanel.this); 611 AndroidDebugBridge.removeDeviceChangeListener(DevicePanel.this); 612 AndroidDebugBridge.removeClientChangeListener(DevicePanel.this); 613 } 614 } 615 }); 616 } 617 618 /** 619 * Sent when an existing client information changed. 620 * <p/> 621 * This is sent from a non UI thread. 622 * @param client the updated client. 623 * @param changeMask the bit mask describing the changed properties. It can contain 624 * any of the following values: {@link Client#CHANGE_INFO}, 625 * {@link Client#CHANGE_DEBUGGER_STATUS}, {@link Client#CHANGE_THREAD_MODE}, 626 * {@link Client#CHANGE_THREAD_DATA}, {@link Client#CHANGE_HEAP_MODE}, 627 * {@link Client#CHANGE_HEAP_DATA}, {@link Client#CHANGE_NATIVE_HEAP_DATA} 628 * 629 * @see IClientChangeListener#clientChanged(Client, int) 630 */ 631 @Override 632 public void clientChanged(final Client client, final int changeMask) { 633 exec(new Runnable() { 634 @Override 635 public void run() { 636 if (mTree.isDisposed() == false) { 637 // refresh the client 638 mTreeViewer.refresh(client); 639 640 if ((changeMask & Client.CHANGE_DEBUGGER_STATUS) == 641 Client.CHANGE_DEBUGGER_STATUS && 642 client.getClientData().getDebuggerConnectionStatus() == 643 DebuggerStatus.WAITING) { 644 // make sure the device is expanded. Normally the setSelection below 645 // will auto expand, but the children of device may not already exist 646 // at this time. Forcing an expand will make the TreeViewer create them. 647 IDevice device = client.getDevice(); 648 if (mTreeViewer.getExpandedState(device) == false) { 649 mTreeViewer.setExpandedState(device, true); 650 } 651 652 // create and set the selection 653 TreePath treePath = new TreePath(new Object[] { device, client}); 654 TreeSelection treeSelection = new TreeSelection(treePath); 655 mTreeViewer.setSelection(treeSelection); 656 657 if (mAdvancedPortSupport) { 658 client.setAsSelectedClient(); 659 } 660 661 // notify the listener of a possible selection change. 662 notifyListeners(device, client); 663 } 664 } else { 665 // tree is disposed, we need to do something. 666 // lets remove ourselves from the listener. 667 AndroidDebugBridge.removeDebugBridgeChangeListener(DevicePanel.this); 668 AndroidDebugBridge.removeDeviceChangeListener(DevicePanel.this); 669 AndroidDebugBridge.removeClientChangeListener(DevicePanel.this); 670 } 671 } 672 }); 673 } 674 675 private void loadImages(Display display) { 676 ImageLoader loader = ImageLoader.getDdmUiLibLoader(); 677 678 if (mDeviceImage == null) { 679 mDeviceImage = loader.loadImage(display, "device.png", //$NON-NLS-1$ 680 ICON_WIDTH, ICON_WIDTH, 681 display.getSystemColor(SWT.COLOR_RED)); 682 } 683 if (mEmulatorImage == null) { 684 mEmulatorImage = loader.loadImage(display, 685 "emulator.png", ICON_WIDTH, ICON_WIDTH, //$NON-NLS-1$ 686 display.getSystemColor(SWT.COLOR_BLUE)); 687 } 688 if (mThreadImage == null) { 689 mThreadImage = loader.loadImage(display, ICON_THREAD, 690 ICON_WIDTH, ICON_WIDTH, 691 display.getSystemColor(SWT.COLOR_YELLOW)); 692 } 693 if (mHeapImage == null) { 694 mHeapImage = loader.loadImage(display, ICON_HEAP, 695 ICON_WIDTH, ICON_WIDTH, 696 display.getSystemColor(SWT.COLOR_BLUE)); 697 } 698 if (mWaitingImage == null) { 699 mWaitingImage = loader.loadImage(display, 700 "debug-wait.png", ICON_WIDTH, ICON_WIDTH, //$NON-NLS-1$ 701 display.getSystemColor(SWT.COLOR_RED)); 702 } 703 if (mDebuggerImage == null) { 704 mDebuggerImage = loader.loadImage(display, 705 "debug-attach.png", ICON_WIDTH, ICON_WIDTH, //$NON-NLS-1$ 706 display.getSystemColor(SWT.COLOR_GREEN)); 707 } 708 if (mDebugErrorImage == null) { 709 mDebugErrorImage = loader.loadImage(display, 710 "debug-error.png", ICON_WIDTH, ICON_WIDTH, //$NON-NLS-1$ 711 display.getSystemColor(SWT.COLOR_RED)); 712 } 713 } 714 715 /** 716 * Returns a display string representing the state of the device. 717 * @param d the device 718 */ 719 private static String getStateString(IDevice d) { 720 DeviceState deviceState = d.getState(); 721 if (deviceState == DeviceState.ONLINE) { 722 return "Online"; 723 } else if (deviceState == DeviceState.OFFLINE) { 724 return "Offline"; 725 } else if (deviceState == DeviceState.BOOTLOADER) { 726 return "Bootloader"; 727 } 728 729 return "??"; 730 } 731 732 /** 733 * Executes the {@link Runnable} in the UI thread. 734 * @param runnable the runnable to execute. 735 */ 736 private void exec(Runnable runnable) { 737 try { 738 Display display = mTree.getDisplay(); 739 display.asyncExec(runnable); 740 } catch (SWTException e) { 741 // tree is disposed, we need to do something. lets remove ourselves from the listener. 742 AndroidDebugBridge.removeDebugBridgeChangeListener(this); 743 AndroidDebugBridge.removeDeviceChangeListener(this); 744 AndroidDebugBridge.removeClientChangeListener(this); 745 } 746 } 747 748 private void notifyListeners() { 749 // get the selection 750 TreeItem[] items = mTree.getSelection(); 751 752 Client client = null; 753 IDevice device = null; 754 755 if (items.length == 1) { 756 Object object = items[0].getData(); 757 if (object instanceof Client) { 758 client = (Client)object; 759 device = client.getDevice(); 760 } else if (object instanceof IDevice) { 761 device = (IDevice)object; 762 } 763 } 764 765 notifyListeners(device, client); 766 } 767 768 private void notifyListeners(IDevice selectedDevice, Client selectedClient) { 769 if (selectedDevice != mCurrentDevice || selectedClient != mCurrentClient) { 770 mCurrentDevice = selectedDevice; 771 mCurrentClient = selectedClient; 772 773 for (IUiSelectionListener listener : mListeners) { 774 // notify the listener with a try/catch-all to make sure this thread won't die 775 // because of an uncaught exception before all the listeners were notified. 776 try { 777 listener.selectionChanged(selectedDevice, selectedClient); 778 } catch (Exception e) { 779 } 780 } 781 } 782 } 783 784} 785