From a3e3f7b4f2205557792a7a499fce5617f8ed4f0e Mon Sep 17 00:00:00 2001 From: Siva Velusamy Date: Fri, 11 Jan 2013 20:30:15 -0800 Subject: Add support for hv protocol that works via DDM Change-Id: Ia88d107811abd8e36a0f980938c584d79565ac42 --- .../hierarchyviewer/views/TreeViewView.java | 3 + .../HierarchyViewerApplication.java | 8 +- .../HierarchyViewerDirector.java | 20 +- .../actions/ProfileNodesAction.java | 55 +++ .../device/DdmViewDebugDevice.java | 373 +++++++++++++++++++++ .../hierarchyviewerlib/device/DeviceBridge.java | 85 +++-- .../hierarchyviewerlib/device/HvDeviceFactory.java | 21 +- .../hierarchyviewerlib/models/ViewNode.java | 9 +- .../android/hierarchyviewerlib/models/Window.java | 20 +- .../hierarchyviewerlib/ui/TreeViewOverview.java | 3 +- .../libs/hierarchyviewerlib/src/images/profile.png | Bin 0 -> 597 bytes 11 files changed, 553 insertions(+), 44 deletions(-) create mode 100644 hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/actions/ProfileNodesAction.java create mode 100644 hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/device/DdmViewDebugDevice.java create mode 100644 hierarchyviewer2/libs/hierarchyviewerlib/src/images/profile.png diff --git a/eclipse/plugins/com.android.ide.eclipse.hierarchyviewer/src/com/android/ide/eclipse/hierarchyviewer/views/TreeViewView.java b/eclipse/plugins/com.android.ide.eclipse.hierarchyviewer/src/com/android/ide/eclipse/hierarchyviewer/views/TreeViewView.java index f162fe4..98bd909 100644 --- a/eclipse/plugins/com.android.ide.eclipse.hierarchyviewer/src/com/android/ide/eclipse/hierarchyviewer/views/TreeViewView.java +++ b/eclipse/plugins/com.android.ide.eclipse.hierarchyviewer/src/com/android/ide/eclipse/hierarchyviewer/views/TreeViewView.java @@ -20,6 +20,7 @@ import com.android.hierarchyviewerlib.actions.CapturePSDAction; import com.android.hierarchyviewerlib.actions.DisplayViewAction; import com.android.hierarchyviewerlib.actions.DumpDisplayListAction; import com.android.hierarchyviewerlib.actions.InvalidateAction; +import com.android.hierarchyviewerlib.actions.ProfileNodesAction; import com.android.hierarchyviewerlib.actions.RefreshViewAction; import com.android.hierarchyviewerlib.actions.RequestLayoutAction; import com.android.hierarchyviewerlib.actions.SaveTreeViewAction; @@ -77,6 +78,7 @@ public class TreeViewView extends ViewPart { mm.add(InvalidateAction.getAction()); mm.add(RequestLayoutAction.getAction()); mm.add(DumpDisplayListAction.getAction()); + mm.add(ProfileNodesAction.getAction()); IToolBarManager tm = actionBars.getToolBarManager(); tm.removeAll(); @@ -89,6 +91,7 @@ public class TreeViewView extends ViewPart { tm.add(InvalidateAction.getAction()); tm.add(RequestLayoutAction.getAction()); tm.add(DumpDisplayListAction.getAction()); + tm.add(ProfileNodesAction.getAction()); } diff --git a/hierarchyviewer2/app/src/com/android/hierarchyviewer/HierarchyViewerApplication.java b/hierarchyviewer2/app/src/com/android/hierarchyviewer/HierarchyViewerApplication.java index 3257e1c..38f10bc 100644 --- a/hierarchyviewer2/app/src/com/android/hierarchyviewer/HierarchyViewerApplication.java +++ b/hierarchyviewer2/app/src/com/android/hierarchyviewer/HierarchyViewerApplication.java @@ -32,6 +32,7 @@ import com.android.hierarchyviewerlib.actions.InvalidateAction; import com.android.hierarchyviewerlib.actions.LoadOverlayAction; import com.android.hierarchyviewerlib.actions.LoadViewHierarchyAction; import com.android.hierarchyviewerlib.actions.PixelPerfectAutoRefreshAction; +import com.android.hierarchyviewerlib.actions.ProfileNodesAction; import com.android.hierarchyviewerlib.actions.RefreshPixelPerfectAction; import com.android.hierarchyviewerlib.actions.RefreshPixelPerfectTreeAction; import com.android.hierarchyviewerlib.actions.RefreshViewAction; @@ -128,6 +129,7 @@ public class HierarchyViewerApplication extends ApplicationWindow { private Composite mTreeViewControls; private ActionButton dumpDisplayList; + private ActionButton mProfileNodes; private HierarchyViewerDirector mDirector; @@ -372,7 +374,7 @@ public class HierarchyViewerApplication extends ApplicationWindow { Composite innerButtonPanel = new Composite(buttonPanel, SWT.NONE); innerButtonPanel.setLayoutData(new GridData(GridData.FILL_VERTICAL)); - GridLayout innerButtonPanelLayout = new GridLayout(7, true); + GridLayout innerButtonPanelLayout = new GridLayout(8, true); innerButtonPanelLayout.marginWidth = innerButtonPanelLayout.marginHeight = 2; innerButtonPanelLayout.horizontalSpacing = innerButtonPanelLayout.verticalSpacing = 2; innerButtonPanel.setLayout(innerButtonPanelLayout); @@ -404,6 +406,10 @@ public class HierarchyViewerApplication extends ApplicationWindow { new ActionButton(innerButtonPanel, DumpDisplayListAction.getAction()); dumpDisplayList.setLayoutData(new GridData(GridData.FILL_BOTH)); + ActionButton profileNodes = + new ActionButton(innerButtonPanel, ProfileNodesAction.getAction()); + profileNodes.setLayoutData(new GridData(GridData.FILL_BOTH)); + SashForm mainSash = new SashForm(mTreeViewPanel, SWT.HORIZONTAL | SWT.SMOOTH); mainSash.setLayoutData(new GridData(GridData.FILL_BOTH)); Composite treeViewContainer = new Composite(mainSash, SWT.BORDER); diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/HierarchyViewerDirector.java b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/HierarchyViewerDirector.java index 867446d..0f0cf65 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/HierarchyViewerDirector.java +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/HierarchyViewerDirector.java @@ -135,6 +135,10 @@ public abstract class HierarchyViewerDirector implements IDeviceChangeListener, executeInBackground("Connecting device", new Runnable() { @Override public void run() { + if (!device.isOnline()) { + return; + } + IHvDevice hvDevice; synchronized (mDevicesLock) { hvDevice = mDevices.get(device); @@ -304,7 +308,6 @@ public abstract class HierarchyViewerDirector implements IDeviceChangeListener, IHvDevice hvDevice = window.getHvDevice(); ViewNode viewNode = hvDevice.loadWindowData(window); if (viewNode != null) { - hvDevice.loadProfileData(window, viewNode); viewNode.setViewCount(); TreeViewModel.getModel().setData(window, viewNode); } @@ -597,6 +600,21 @@ public abstract class HierarchyViewerDirector implements IDeviceChangeListener, } } + public void profileCurrentNode() { + final DrawableViewNode selectedNode = TreeViewModel.getModel().getSelection(); + if (selectedNode != null) { + executeInBackground("Profile Node", new Runnable() { + @Override + public void run() { + IHvDevice hvDevice = getHvDevice(selectedNode.viewNode.window.getDevice()); + hvDevice.loadProfileData(selectedNode.viewNode.window, selectedNode.viewNode); + // Force the layout viewer to redraw. + TreeViewModel.getModel().notifySelectionChanged(); + } + }); + } + } + public void loadAllViews() { executeInBackground("Loading all views", new Runnable() { @Override diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/actions/ProfileNodesAction.java b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/actions/ProfileNodesAction.java new file mode 100644 index 0000000..4bf93e8 --- /dev/null +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/actions/ProfileNodesAction.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.hierarchyviewerlib.actions; + +import com.android.ddmuilib.ImageLoader; +import com.android.hierarchyviewerlib.HierarchyViewerDirector; + +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Display; + +public class ProfileNodesAction extends SelectedNodeEnabledAction implements ImageAction { + private static ProfileNodesAction sAction; + + private Image mImage; + + public ProfileNodesAction() { + super("Profile Node"); + ImageLoader imageLoader = ImageLoader.getLoader(HierarchyViewerDirector.class); + mImage = imageLoader.loadImage("profile.png", Display.getDefault()); //$NON-NLS-1$ + setImageDescriptor(ImageDescriptor.createFromImage(mImage)); + setToolTipText("Obtain layout times for tree rooted at selected node"); + } + + public static ProfileNodesAction getAction() { + if (sAction == null) { + sAction = new ProfileNodesAction(); + } + return sAction; + } + + @Override + public void run() { + HierarchyViewerDirector.getDirector().profileCurrentNode(); + } + + @Override + public Image getImage() { + return mImage; + } +} diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/device/DdmViewDebugDevice.java b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/device/DdmViewDebugDevice.java new file mode 100644 index 0000000..0a6e938 --- /dev/null +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/device/DdmViewDebugDevice.java @@ -0,0 +1,373 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.hierarchyviewerlib.device; + +import com.android.ddmlib.AndroidDebugBridge; +import com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener; +import com.android.ddmlib.Client; +import com.android.ddmlib.ClientData; +import com.android.ddmlib.HandleViewDebug; +import com.android.ddmlib.HandleViewDebug.ViewDumpHandler; +import com.android.ddmlib.IDevice; +import com.android.ddmlib.Log; +import com.android.hierarchyviewerlib.device.WindowUpdater.IWindowChangeListener; +import com.android.hierarchyviewerlib.models.ViewNode; +import com.android.hierarchyviewerlib.models.Window; +import com.android.hierarchyviewerlib.ui.util.PsdFile; + +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Display; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.IOException; +import java.io.StringReader; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + +public class DdmViewDebugDevice extends AbstractHvDevice implements IDeviceChangeListener { + private static final String TAG = "DdmViewDebugDevice"; + + private final IDevice mDevice; + private Map> mViewRootsPerClient = new HashMap>(40); + + public DdmViewDebugDevice(IDevice device) { + mDevice = device; + } + + @Override + public boolean initializeViewDebug() { + AndroidDebugBridge.addDeviceChangeListener(this); + return reloadWindows(); + } + + private static class ListViewRootsHandler extends ViewDumpHandler { + private List mViewRoots = Collections.synchronizedList(new ArrayList(10)); + + public ListViewRootsHandler() { + super(HandleViewDebug.CHUNK_VULW); + } + + @Override + protected void handleViewDebugResult(ByteBuffer data) { + int nWindows = data.getInt(); + + for (int i = 0; i < nWindows; i++) { + int len = data.getInt(); + mViewRoots.add(getString(data, len)); + } + } + + public List getViewRoots(long timeout, TimeUnit unit) { + waitForResult(timeout, unit); + return mViewRoots; + } + } + + private static class CaptureByteArrayHandler extends ViewDumpHandler { + public CaptureByteArrayHandler(int type) { + super(type); + } + + private AtomicReference mData = new AtomicReference(); + + @Override + protected void handleViewDebugResult(ByteBuffer data) { + byte[] b = new byte[data.remaining()]; + data.get(b); + mData.set(b); + + } + + public byte[] getData(long timeout, TimeUnit unit) { + waitForResult(timeout, unit); + return mData.get(); + } + } + + private static class CaptureLayersHandler extends ViewDumpHandler { + private AtomicReference mPsd = new AtomicReference(); + + public CaptureLayersHandler() { + super(HandleViewDebug.CHUNK_VURT); + } + + @Override + protected void handleViewDebugResult(ByteBuffer data) { + byte[] b = new byte[data.remaining()]; + data.get(b); + DataInputStream dis = new DataInputStream(new ByteArrayInputStream(b)); + try { + mPsd.set(DeviceBridge.parsePsd(dis)); + } catch (IOException e) { + Log.e(TAG, e); + } + } + + public PsdFile getPsdFile(long timeout, TimeUnit unit) { + waitForResult(timeout, unit); + return mPsd.get(); + } + } + + @Override + public boolean reloadWindows() { + mViewRootsPerClient = new HashMap>(40); + + for (Client c : mDevice.getClients()) { + ClientData cd = c.getClientData(); + if (cd != null && cd.hasFeature(ClientData.FEATURE_VIEW_HIERARCHY)) { + ListViewRootsHandler handler = new ListViewRootsHandler(); + + try { + HandleViewDebug.listViewRoots(c, handler); + } catch (IOException e) { + Log.e(TAG, e); + continue; + } + + List viewRoots = new ArrayList( + handler.getViewRoots(200, TimeUnit.MILLISECONDS)); + mViewRootsPerClient.put(c, viewRoots); + } + } + + return true; + } + + @Override + public void terminateViewDebug() { + // nothing to terminate + } + + @Override + public boolean isViewDebugEnabled() { + return true; + } + + @Override + public boolean supportsDisplayListDump() { + return true; + } + + @Override + public Window[] getWindows() { + List windows = new ArrayList(10); + + for (Client c: mViewRootsPerClient.keySet()) { + for (String viewRoot: mViewRootsPerClient.get(c)) { + windows.add(new Window(this, viewRoot, c)); + } + } + + return windows.toArray(new Window[windows.size()]); + } + + @Override + public int getFocusedWindow() { + // TODO: add support for identifying view in focus + return -1; + } + + @Override + public IDevice getDevice() { + return mDevice; + } + + @Override + public ViewNode loadWindowData(Window window) { + Client c = window.getClient(); + if (c == null) { + return null; + } + + String viewRoot = window.getTitle(); + CaptureByteArrayHandler handler = new CaptureByteArrayHandler(HandleViewDebug.CHUNK_VURT); + try { + HandleViewDebug.dumpViewHierarchy(c, viewRoot, + false /* skipChildren */, + true /* includeProperties */, + handler); + } catch (IOException e) { + Log.e(TAG, e); + return null; + } + + byte[] data = handler.getData(10, TimeUnit.SECONDS); + String viewHierarchy = new String(data, Charset.forName("UTF-8")); + return DeviceBridge.parseViewHierarchy(new BufferedReader(new StringReader(viewHierarchy)), + window); + } + + @Override + public void loadProfileData(Window window, ViewNode viewNode) { + Client c = window.getClient(); + if (c == null) { + return; + } + + String viewRoot = window.getTitle(); + CaptureByteArrayHandler handler = new CaptureByteArrayHandler(HandleViewDebug.CHUNK_VUOP); + try { + HandleViewDebug.profileView(c, viewRoot, viewNode.toString(), handler); + } catch (IOException e) { + Log.e(TAG, e); + return; + } + + byte[] data = handler.getData(30, TimeUnit.SECONDS); + if (data == null) { + Log.e(TAG, "Timed out waiting for profile data"); + return; + } + + try { + boolean success = DeviceBridge.loadProfileDataRecursive(viewNode, + new BufferedReader(new StringReader(new String(data)))); + if (success) { + viewNode.setProfileRatings(); + } + } catch (IOException e) { + Log.e(TAG, e); + return; + } + } + + @Override + public Image loadCapture(Window window, ViewNode viewNode) { + Client c = window.getClient(); + if (c == null) { + return null; + } + + String viewRoot = window.getTitle(); + CaptureByteArrayHandler handler = new CaptureByteArrayHandler(HandleViewDebug.CHUNK_VUOP); + + try { + HandleViewDebug.captureView(c, viewRoot, viewNode.toString(), handler); + } catch (IOException e) { + Log.e(TAG, e); + return null; + } + + byte[] data = handler.getData(10, TimeUnit.SECONDS); + return (data == null) ? null : + new Image(Display.getDefault(), new ByteArrayInputStream(data)); + } + + @Override + public PsdFile captureLayers(Window window) { + Client c = window.getClient(); + if (c == null) { + return null; + } + + String viewRoot = window.getTitle(); + CaptureLayersHandler handler = new CaptureLayersHandler(); + try { + HandleViewDebug.captureLayers(c, viewRoot, handler); + } catch (IOException e) { + Log.e(TAG, e); + return null; + } + + return handler.getPsdFile(20, TimeUnit.SECONDS); + } + + @Override + public void invalidateView(ViewNode viewNode) { + Window window = viewNode.window; + Client c = window.getClient(); + if (c == null) { + return; + } + + String viewRoot = window.getTitle(); + try { + HandleViewDebug.invalidateView(c, viewRoot, viewNode.toString()); + } catch (IOException e) { + Log.e(TAG, e); + } + } + + @Override + public void requestLayout(ViewNode viewNode) { + Window window = viewNode.window; + Client c = window.getClient(); + if (c == null) { + return; + } + + String viewRoot = window.getTitle(); + try { + HandleViewDebug.requestLayout(c, viewRoot, viewNode.toString()); + } catch (IOException e) { + Log.e(TAG, e); + } + } + + @Override + public void outputDisplayList(ViewNode viewNode) { + Window window = viewNode.window; + Client c = window.getClient(); + if (c == null) { + return; + } + + String viewRoot = window.getTitle(); + try { + HandleViewDebug.dumpDisplayList(c, viewRoot, viewNode.toString()); + } catch (IOException e) { + Log.e(TAG, e); + } + } + + @Override + public void addWindowChangeListener(IWindowChangeListener l) { + // TODO: add support for listening to view root changes + } + + @Override + public void removeWindowChangeListener(IWindowChangeListener l) { + // TODO: add support for listening to view root changes + } + + @Override + public void deviceConnected(IDevice device) { + // pass + } + + @Override + public void deviceDisconnected(IDevice device) { + // pass + } + + @Override + public void deviceChanged(IDevice device, int changeMask) { + if ((changeMask & IDevice.CHANGE_CLIENT_LIST) != 0) { + reloadWindows(); + } + } +} diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/device/DeviceBridge.java b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/device/DeviceBridge.java index da29703..30fa6f6 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/device/DeviceBridge.java +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/device/DeviceBridge.java @@ -99,7 +99,7 @@ public class DeviceBridge { */ public static void initDebugBridge(String adbLocation) { if (sBridge == null) { - AndroidDebugBridge.init(false /* debugger support */); + AndroidDebugBridge.init(true /* debugger support */); } if (sBridge == null || !sBridge.isConnected()) { sBridge = AndroidDebugBridge.createBridge(adbLocation, true); @@ -438,9 +438,29 @@ public class DeviceBridge { connection = new DeviceConnection(window.getDevice()); connection.sendCommand("DUMP " + window.encode()); //$NON-NLS-1$ BufferedReader in = connection.getInputStream(); - ViewNode currentNode = null; - int currentDepth = -1; - String line; + ViewNode currentNode = parseViewHierarchy(in, window); + ViewServerInfo serverInfo = getViewServerInfo(window.getDevice()); + if (serverInfo != null) { + currentNode.protocolVersion = serverInfo.protocolVersion; + } + return currentNode; + } catch (Exception e) { + Log.e(TAG, "Unable to load window data for window " + window.getTitle() + " on device " + + window.getDevice()); + Log.e(TAG, e.getMessage()); + } finally { + if (connection != null) { + connection.close(); + } + } + return null; + } + + public static ViewNode parseViewHierarchy(BufferedReader in, Window window) { + ViewNode currentNode = null; + int currentDepth = -1; + String line; + try { while ((line = in.readLine()) != null) { if ("DONE.".equalsIgnoreCase(line)) { break; @@ -458,27 +478,18 @@ public class DeviceBridge { currentNode = new ViewNode(window, currentNode, line.substring(depth)); currentDepth = depth; } - if (currentNode == null) { - return null; - } - while (currentNode.parent != null) { - currentNode = currentNode.parent; - } - ViewServerInfo serverInfo = getViewServerInfo(window.getDevice()); - if (serverInfo != null) { - currentNode.protocolVersion = serverInfo.protocolVersion; - } - return currentNode; - } catch (Exception e) { - Log.e(TAG, "Unable to load window data for window " + window.getTitle() + " on device " - + window.getDevice()); - Log.e(TAG, e.getMessage()); - } finally { - if (connection != null) { - connection.close(); - } + } catch (IOException e) { + Log.e(TAG, "Error reading view hierarchy stream: " + e.getMessage()); + return null; } - return null; + if (currentNode == null) { + return null; + } + while (currentNode.parent != null) { + currentNode = currentNode.parent; + } + + return currentNode; } public static boolean loadProfileData(Window window, ViewNode viewNode) { @@ -524,7 +535,7 @@ public class DeviceBridge { return true; } - private static boolean loadProfileDataRecursive(ViewNode node, BufferedReader in) + public static boolean loadProfileDataRecursive(ViewNode node, BufferedReader in) throws IOException { if (!loadProfileData(node, in)) { return false; @@ -567,16 +578,8 @@ public class DeviceBridge { new DataInputStream(new BufferedInputStream(connection.getSocket() .getInputStream())); - int width = in.readInt(); - int height = in.readInt(); - - PsdFile psd = new PsdFile(width, height); - - while (readLayer(in, psd)) { - } - - return psd; - } catch (Exception e) { + return parsePsd(in); + } catch (IOException e) { Log.e(TAG, "Unable to capture layers for window " + window.getTitle() + " on device " + window.getDevice()); } finally { @@ -595,6 +598,18 @@ public class DeviceBridge { return null; } + public static PsdFile parsePsd(DataInputStream in) throws IOException { + int width = in.readInt(); + int height = in.readInt(); + + PsdFile psd = new PsdFile(width, height); + + while (readLayer(in, psd)) { + } + + return psd; + } + private static boolean readLayer(DataInputStream in, PsdFile psd) { try { if (in.read() == 2) { diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/device/HvDeviceFactory.java b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/device/HvDeviceFactory.java index e1fdab3..c38a9cc 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/device/HvDeviceFactory.java +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/device/HvDeviceFactory.java @@ -16,10 +16,29 @@ package com.android.hierarchyviewerlib.device; +import com.android.ddmlib.Client; +import com.android.ddmlib.ClientData; import com.android.ddmlib.IDevice; public class HvDeviceFactory { + private static final boolean ALWAYS_USE_VIEWSERVER = false; // for debugging + public static IHvDevice create(IDevice device) { - return new ViewServerDevice(device); + if (ALWAYS_USE_VIEWSERVER) { + return new ViewServerDevice(device); + } + + boolean ddmViewHierarchy = false; + + // see if any of the clients on the device support view hierarchy via DDMS + for (Client c : device.getClients()) { + ClientData cd = c.getClientData(); + if (cd != null && cd.hasFeature(ClientData.FEATURE_VIEW_HIERARCHY)) { + ddmViewHierarchy = true; + break; + } + } + + return ddmViewHierarchy ? new DdmViewDebugDevice(device) : new ViewServerDevice(device); } } diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/models/ViewNode.java b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/models/ViewNode.java index 49bdf8f..e38da00 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/models/ViewNode.java +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/models/ViewNode.java @@ -132,7 +132,14 @@ public class ViewNode { data = data.substring(delimIndex + 1); delimIndex = data.indexOf(' '); hashCode = data.substring(0, delimIndex); - loadProperties(data.substring(delimIndex + 1).trim()); + + if (data.length() > delimIndex + 1) { + loadProperties(data.substring(delimIndex + 1).trim()); + } else { + // defaults in case properties are not available + id = "unknown"; + width = height = 10; + } measureTime = -1; layoutTime = -1; diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/models/Window.java b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/models/Window.java index 7298ab2..4e260a9 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/models/Window.java +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/models/Window.java @@ -16,6 +16,7 @@ package com.android.hierarchyviewerlib.models; +import com.android.ddmlib.Client; import com.android.ddmlib.IDevice; import com.android.hierarchyviewerlib.device.IHvDevice; @@ -27,11 +28,20 @@ public class Window { private final String mTitle; private final int mHashCode; private final IHvDevice mHvDevice; + private final Client mClient; public Window(IHvDevice device, String title, int hashCode) { - this.mHvDevice = device; - this.mTitle = title; - this.mHashCode = hashCode; + mHvDevice = device; + mTitle = title; + mHashCode = hashCode; + mClient = null; + } + + public Window(IHvDevice device, String title, Client c) { + mHvDevice = device; + mTitle = title; + mClient = c; + mHashCode = c.hashCode(); } public String getTitle() { @@ -59,6 +69,10 @@ public class Window { return mHvDevice.getDevice(); } + public Client getClient() { + return mClient; + } + public static Window getFocusedWindow(IHvDevice device) { return new Window(device, "", -1); } diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/TreeViewOverview.java b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/TreeViewOverview.java index bbff48c..3352df0 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/TreeViewOverview.java +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/TreeViewOverview.java @@ -234,8 +234,7 @@ public class TreeViewOverview extends Canvas implements ITreeChangeListener { .ceil(mViewport.width), (int) Math.ceil(mViewport.height)); e.gc.setAlpha(255); - e.gc - .setForeground(Display.getDefault().getSystemColor( + e.gc.setForeground(Display.getDefault().getSystemColor( SWT.COLOR_DARK_GRAY)); e.gc.setLineWidth((int) Math.ceil(2 / mScale)); e.gc.drawRectangle((int) mViewport.x, (int) mViewport.y, (int) Math diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/images/profile.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/profile.png new file mode 100644 index 0000000..1e9fb5a Binary files /dev/null and b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/profile.png differ -- cgit v1.1