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