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