• 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 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