diff options
Diffstat (limited to 'hierarchyviewer2/libs/hierarchyviewerlib')
7 files changed, 541 insertions, 1 deletions
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ComponentRegistry.java b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ComponentRegistry.java index 528c35c..a50478e 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ComponentRegistry.java +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ComponentRegistry.java @@ -18,6 +18,7 @@ package com.android.hierarchyviewerlib; import com.android.hierarchyviewerlib.models.DeviceSelectionModel; import com.android.hierarchyviewerlib.models.PixelPerfectModel; +import com.android.hierarchyviewerlib.models.TreeViewModel; /** * This is the central point for getting access to the various parts of the @@ -32,6 +33,8 @@ public class ComponentRegistry { private static PixelPerfectModel pixelPerfectModel; + private static TreeViewModel treeViewModel; + public static HierarchyViewerDirector getDirector() { return director; } @@ -55,4 +58,12 @@ public class ComponentRegistry { public static PixelPerfectModel getPixelPerfectModel() { return pixelPerfectModel; } + + public static void setTreeViewModel(TreeViewModel treeViewModel) { + ComponentRegistry.treeViewModel = treeViewModel; + } + + public static TreeViewModel getTreeViewModel() { + return treeViewModel; + } } diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/HierarchyViewerDirector.java b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/HierarchyViewerDirector.java index c21da49..b63fba8 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/HierarchyViewerDirector.java +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/HierarchyViewerDirector.java @@ -109,6 +109,9 @@ public abstract class HierarchyViewerDirector implements IDeviceChangeListener, DeviceBridge.removeDeviceForward(device); DeviceBridge.removeViewServerInfo(device); ComponentRegistry.getDeviceSelectionModel().removeDevice(device); + if (ComponentRegistry.getPixelPerfectModel().getDevice() == device) { + ComponentRegistry.getPixelPerfectModel().setData(null, null, null); + } } public void deviceChanged(IDevice device, int changeMask) { @@ -192,4 +195,17 @@ public abstract class HierarchyViewerDirector implements IDeviceChangeListener, } }); } + + public void loadViewTreeData(final Window window) { + executeInBackground(new Runnable() { + public void run() { + + ViewNode viewNode = DeviceBridge.loadWindowData(window); + if (viewNode != null) { + DeviceBridge.loadProfileData(window, viewNode); + ComponentRegistry.getTreeViewModel().setData(window, viewNode); + } + } + }); + } } 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 001f98b..34e7c03 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/device/DeviceBridge.java +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/device/DeviceBridge.java @@ -430,4 +430,41 @@ public class DeviceBridge { } return null; } + + public static boolean loadProfileData(Window window, ViewNode viewNode) { + DeviceConnection connection = null; + try { + connection = new DeviceConnection(window.getDevice()); + connection.sendCommand("PROFILE " + window.encode() + " " + viewNode.toString()); + BufferedReader in = connection.getInputStream(); + return loadProfileDataRecursive(viewNode, in); + } catch (IOException e) { + Log.e(TAG, "Unable to load profiling data for window " + window.getTitle() + + " on device " + window.getDevice()); + } finally { + if (connection != null) { + connection.close(); + } + } + return false; + } + + private static boolean loadProfileDataRecursive(ViewNode node, BufferedReader in) + throws IOException { + String line; + if ((line = in.readLine()) == null || line.equalsIgnoreCase("-1 -1 -1") + || line.equalsIgnoreCase("DONE.")) { + return false; + } + String[] data = line.split(" "); + node.measureTime = (Long.parseLong(data[0]) / 1000.0) / 1000.0; + node.layoutTime = (Long.parseLong(data[1]) / 1000.0) / 1000.0; + node.drawTime = (Long.parseLong(data[2]) / 1000.0) / 1000.0; + for (int i = 0; i < node.children.size(); i++) { + if (!loadProfileDataRecursive(node.children.get(i), in)) { + return false; + } + } + return true; + } } diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/device/ViewNode.java b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/device/ViewNode.java index 00453ee..2e22b56 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/device/ViewNode.java +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/device/ViewNode.java @@ -76,6 +76,12 @@ public class ViewNode { public int index; + public double measureTime; + + public double layoutTime; + + public double drawTime; + public ViewNode(ViewNode parent, String data) { this.parent = parent; index = this.parent == null ? 0 : this.parent.children.size(); @@ -88,6 +94,10 @@ public class ViewNode { delimIndex = data.indexOf(' '); hashCode = data.substring(0, delimIndex); loadProperties(data.substring(delimIndex + 1).trim()); + + measureTime = -1; + layoutTime = -1; + drawTime = -1; } private void loadProperties(String data) { diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/models/PixelPerfectModel.java b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/models/PixelPerfectModel.java index 6963b25..4f19368 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/models/PixelPerfectModel.java +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/models/PixelPerfectModel.java @@ -24,6 +24,12 @@ import java.util.ArrayList; public class PixelPerfectModel { + public static final int MIN_ZOOM = 2; + + public static final int MAX_ZOOM = 24; + + private static final int DEFAULT_ZOOM = 8; + public static class Point { public int x; @@ -45,6 +51,8 @@ public class PixelPerfectModel { private ViewNode selected; + private int zoom; + private final ArrayList<ImageChangeListener> imageChangeListeners = new ArrayList<ImageChangeListener>(); @@ -53,8 +61,13 @@ public class PixelPerfectModel { this.device = device; this.image = image; this.viewNode = viewNode; - this.crosshairLocation = new Point(image.width / 2, image.height / 2); + if (image != null) { + this.crosshairLocation = new Point(image.width / 2, image.height / 2); + } else { + this.crosshairLocation = null; + } this.selected = null; + zoom = DEFAULT_ZOOM; } notifyImageLoaded(); } @@ -82,6 +95,19 @@ public class PixelPerfectModel { notifyFocusChanged(); } + public void setZoom(int newZoom) { + synchronized (this) { + if (newZoom < MIN_ZOOM) { + newZoom = MIN_ZOOM; + } + if (newZoom > MAX_ZOOM) { + newZoom = MAX_ZOOM; + } + zoom = newZoom; + } + notifyZoomChanged(); + } + public ViewNode getViewNode() { synchronized (this) { return viewNode; @@ -112,6 +138,12 @@ public class PixelPerfectModel { } } + public int getZoom() { + synchronized (this) { + return zoom; + } + } + public static interface ImageChangeListener { public void imageLoaded(); @@ -122,6 +154,8 @@ public class PixelPerfectModel { public void selectionChanged(); public void focusChanged(); + + public void zoomChanged(); } private ImageChangeListener[] getImageChangeListenerList() { @@ -182,6 +216,15 @@ public class PixelPerfectModel { } } + public void notifyZoomChanged() { + ImageChangeListener[] listeners = getImageChangeListenerList(); + if (listeners != null) { + for (int i = 0; i < listeners.length; i++) { + listeners[i].zoomChanged(); + } + } + } + public void addImageChangeListener(ImageChangeListener listener) { synchronized (imageChangeListeners) { imageChangeListeners.add(listener); diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/models/TreeViewModel.java b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/models/TreeViewModel.java new file mode 100644 index 0000000..890b88c --- /dev/null +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/models/TreeViewModel.java @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2010 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.models; + +import com.android.hierarchyviewerlib.device.ViewNode; +import com.android.hierarchyviewerlib.device.Window; +import com.android.hierarchyviewerlib.scene.DrawableViewNode; +import com.android.hierarchyviewerlib.scene.DrawableViewNode.Point; +import com.android.hierarchyviewerlib.scene.DrawableViewNode.Rectangle; + +import java.util.ArrayList; + +public class TreeViewModel { + public static final double MAX_ZOOM = 2; + + public static final double MIN_ZOOM = 0.2; + + private Window window; + + private DrawableViewNode tree; + + private Rectangle viewport; + + private double zoom; + + private final ArrayList<TreeChangeListener> treeChangeListeners = + new ArrayList<TreeChangeListener>(); + + public void setData(Window window, ViewNode viewNode) { + synchronized (this) { + this.window = window; + tree = new DrawableViewNode(viewNode); + tree.setLeft(); + tree.placeRoot(); + viewport = null; + zoom = 1; + } + notifyTreeChanged(); + } + + public void setViewport(Rectangle viewport) { + synchronized (this) { + this.viewport = viewport; + } + notifyViewportChanged(); + } + + public void setZoom(double newZoom) { + Point zoomPoint = null; + synchronized (this) { + if (tree != null && viewport != null) { + zoomPoint = + new Point(viewport.x + viewport.width / 2, viewport.y + viewport.height / 2); + } + } + zoomOnPoint(newZoom, zoomPoint); + } + + public void zoomOnPoint(double newZoom, Point zoomPoint) { + synchronized (this) { + if (tree != null && this.viewport != null) { + if (newZoom < MIN_ZOOM) { + newZoom = MIN_ZOOM; + } + if (newZoom > MAX_ZOOM) { + newZoom = MAX_ZOOM; + } + viewport.x = zoomPoint.x - (zoomPoint.x - viewport.x) * zoom / newZoom; + viewport.y = zoomPoint.y - (zoomPoint.y - viewport.y) * zoom / newZoom; + viewport.width = viewport.width * zoom / newZoom; + viewport.height = viewport.height * zoom / newZoom; + zoom = newZoom; + } + } + notifyZoomChanged(); + } + + public DrawableViewNode getTree() { + synchronized (this) { + return tree; + } + } + + public Window getWindow() { + synchronized (this) { + return window; + } + } + + public Rectangle getViewport() { + synchronized (this) { + return viewport; + } + } + + public double getZoom() { + synchronized (this) { + return zoom; + } + } + + public static interface TreeChangeListener { + public void treeChanged(); + + public void viewportChanged(); + + public void zoomChanged(); + } + + private TreeChangeListener[] getTreeChangeListenerList() { + TreeChangeListener[] listeners = null; + synchronized (treeChangeListeners) { + if (treeChangeListeners.size() == 0) { + return null; + } + listeners = + treeChangeListeners.toArray(new TreeChangeListener[treeChangeListeners.size()]); + } + return listeners; + } + + public void notifyTreeChanged() { + TreeChangeListener[] listeners = getTreeChangeListenerList(); + if (listeners != null) { + for (int i = 0; i < listeners.length; i++) { + listeners[i].treeChanged(); + } + } + } + + public void notifyViewportChanged() { + TreeChangeListener[] listeners = getTreeChangeListenerList(); + if (listeners != null) { + for (int i = 0; i < listeners.length; i++) { + listeners[i].viewportChanged(); + } + } + } + + public void notifyZoomChanged() { + TreeChangeListener[] listeners = getTreeChangeListenerList(); + if (listeners != null) { + for (int i = 0; i < listeners.length; i++) { + listeners[i].zoomChanged(); + } + } + } + + public void addTreeChangeListener(TreeChangeListener listener) { + synchronized (treeChangeListeners) { + treeChangeListeners.add(listener); + } + } + + public void removeTreeChangeListener(TreeChangeListener listener) { + synchronized (treeChangeListeners) { + treeChangeListeners.remove(listener); + } + } +} diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/scene/DrawableViewNode.java b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/scene/DrawableViewNode.java new file mode 100644 index 0000000..aff3d6d --- /dev/null +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/scene/DrawableViewNode.java @@ -0,0 +1,249 @@ +/* + * Copyright (C) 2010 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.scene; + +import com.android.hierarchyviewerlib.device.ViewNode; + +import java.util.ArrayList; + +public class DrawableViewNode { + public ViewNode viewNode; + + public final ArrayList<DrawableViewNode> children = new ArrayList<DrawableViewNode>(); + + public final static int NODE_HEIGHT = 70; + + public final static int NODE_WIDTH = 100; + + public final static int LEAF_NODE_SPACING = 5; + + public final static int NON_LEAF_NODE_SPACING = 10; + + public final static int PARENT_CHILD_SPACING = 40; + + public final static int PADDING = 30; + + public int treeHeight; + + public int treeWidth; + + public boolean leaf; + + public DrawableViewNode parent; + + public int left; + + public double top; + + public int topSpacing; + + public int bottomSpacing; + + public static class Rectangle { + public double x, y, width, height; + + public Rectangle() { + + } + + public Rectangle(Rectangle other) { + this.x = other.x; + this.y = other.y; + this.width = other.width; + this.height = other.height; + } + + public Rectangle(double x, double y, double width, double height) { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + } + + @Override + public String toString() { + return "{" + x + ", " + y + ", " + width + ", " + height + "}"; + } + + } + + public static class Point { + public double x, y; + + public Point() { + } + + public Point(double x, double y) { + this.x = x; + this.y = y; + } + } + + public Rectangle bounds = new Rectangle(); + + public DrawableViewNode(ViewNode viewNode) { + this.viewNode = viewNode; + if (viewNode.children.size() == 0) { + treeHeight = NODE_HEIGHT; + treeWidth = NODE_WIDTH; + leaf = true; + } else { + leaf = false; + int N = viewNode.children.size(); + treeHeight = 0; + treeWidth = 0; + for (int i = 0; i < N; i++) { + DrawableViewNode child = new DrawableViewNode(viewNode.children.get(i)); + children.add(child); + child.parent = this; + treeHeight += child.treeHeight; + treeWidth = Math.max(treeWidth, child.treeWidth); + if (i != 0) { + DrawableViewNode prevChild = children.get(i - 1); + if (prevChild.leaf && child.leaf) { + treeHeight += LEAF_NODE_SPACING; + prevChild.bottomSpacing = LEAF_NODE_SPACING; + child.topSpacing = LEAF_NODE_SPACING; + } else { + treeHeight += NON_LEAF_NODE_SPACING; + prevChild.bottomSpacing = NON_LEAF_NODE_SPACING; + child.topSpacing = NON_LEAF_NODE_SPACING; + } + } + } + treeWidth += NODE_WIDTH + PARENT_CHILD_SPACING; + } + } + + public void setLeft() { + if (parent == null) { + left = PADDING; + bounds.x = 0; + bounds.width = treeWidth + 2 * PADDING; + } else { + left = parent.left + NODE_WIDTH + PARENT_CHILD_SPACING; + } + int N = children.size(); + for (int i = 0; i < N; i++) { + children.get(i).setLeft(); + } + } + + public void placeRoot() { + top = PADDING + (treeHeight - NODE_HEIGHT) / 2.0; + double currentTop = PADDING; + int N = children.size(); + for (int i = 0; i < N; i++) { + DrawableViewNode child = children.get(i); + child.place(currentTop, top - currentTop); + currentTop += child.treeHeight + child.bottomSpacing; + } + bounds.y = 0; + bounds.height = treeHeight + 2 * PADDING; + } + + private void place(double treeTop, double rootDistance) { + if (treeHeight <= rootDistance) { + top = treeTop + treeHeight - NODE_HEIGHT; + } else if (rootDistance <= -NODE_HEIGHT) { + top = treeTop; + } else { + if (children.size() == 0) { + top = treeTop; + } else { + top = + rootDistance + treeTop - NODE_HEIGHT + (2.0 * NODE_HEIGHT) + / (treeHeight + NODE_HEIGHT) * (treeHeight - rootDistance); + } + } + int N = children.size(); + double currentTop = treeTop; + for (int i = 0; i < N; i++) { + DrawableViewNode child = children.get(i); + child.place(currentTop, rootDistance); + currentTop += child.treeHeight + child.bottomSpacing; + rootDistance -= child.treeHeight + child.bottomSpacing; + } + } + + public DrawableViewNode getSelected(double x, double y) { + if (x >= left && x < left + NODE_WIDTH && y >= top && y <= top + NODE_HEIGHT) { + return this; + } + int N = children.size(); + for (int i = 0; i < N; i++) { + DrawableViewNode selected = children.get(i).getSelected(x, y); + if (selected != null) { + return selected; + } + } + return null; + } + + /* + * Moves the node the specified distance up. + */ + public void move(double distance) { + top -= distance; + + // Get the root + DrawableViewNode root = this; + while (root.parent != null) { + root = root.parent; + } + + // Figure out the new tree top. + double treeTop; + if (top + NODE_HEIGHT <= root.top) { + treeTop = top + NODE_HEIGHT - treeHeight; + } else if (top >= root.top + NODE_HEIGHT) { + treeTop = top; + } else { + if (leaf) { + treeTop = top; + } else { + double distanceRatio = 1 - (root.top + NODE_HEIGHT - top) / (2.0 * NODE_HEIGHT); + treeTop = root.top - treeHeight + distanceRatio * (treeHeight + NODE_HEIGHT); + } + } + // Go up the tree and figure out the tree top. + DrawableViewNode node = this; + while (node.parent != null) { + int index = node.viewNode.index; + for (int i = 0; i < index; i++) { + DrawableViewNode sibling = node.parent.children.get(i); + treeTop -= sibling.treeHeight + sibling.bottomSpacing; + } + node = node.parent; + } + + // Update the bounds. + root.bounds.y = Math.min(root.top - PADDING, treeTop - PADDING); + root.bounds.height = + Math.max(treeTop + root.treeHeight + PADDING, root.top + NODE_HEIGHT + PADDING) + - root.bounds.y; + // Place all the children of the root + double currentTop = treeTop; + int N = root.children.size(); + for (int i = 0; i < N; i++) { + DrawableViewNode child = root.children.get(i); + child.place(currentTop, root.top - currentTop); + currentTop += child.treeHeight + child.bottomSpacing; + } + + } +} |