auto import from //depot/cupcake/@135843
This commit is contained in:
30
tools/hierarchyviewer/src/Android.mk
Normal file
30
tools/hierarchyviewer/src/Android.mk
Normal file
@@ -0,0 +1,30 @@
|
||||
# Copyright (C) 2008 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.
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES := $(call all-subdir-java-files)
|
||||
LOCAL_JAVA_RESOURCE_DIRS := resources
|
||||
|
||||
LOCAL_JAR_MANIFEST := ../etc/manifest.txt
|
||||
LOCAL_JAVA_LIBRARIES := \
|
||||
ddmlib \
|
||||
swing-worker-1.1 \
|
||||
org-openide-util \
|
||||
org-netbeans-api-visual
|
||||
LOCAL_MODULE := hierarchyviewer
|
||||
|
||||
include $(BUILD_HOST_JAVA_LIBRARY)
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.hierarchyviewer;
|
||||
|
||||
import com.android.hierarchyviewer.ui.Workspace;
|
||||
import com.android.hierarchyviewer.device.DeviceBridge;
|
||||
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.UnsupportedLookAndFeelException;
|
||||
|
||||
public class HierarchyViewer {
|
||||
private static final CharSequence OS_WINDOWS = "Windows";
|
||||
private static final CharSequence OS_MACOSX = "Mac OS X";
|
||||
|
||||
private static void initUserInterface() {
|
||||
System.setProperty("apple.laf.useScreenMenuBar", "true");
|
||||
System.setProperty("apple.awt.brushMetalLook", "true");
|
||||
System.setProperty("com.apple.mrj.application.apple.menu.about.name", "HierarchyViewer");
|
||||
|
||||
final String os = System.getProperty("os.name");
|
||||
|
||||
try {
|
||||
if (os.contains(OS_WINDOWS) || os.contains(OS_MACOSX)) {
|
||||
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
||||
} else {
|
||||
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InstantiationException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (UnsupportedLookAndFeelException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
initUserInterface();
|
||||
DeviceBridge.initDebugBridge();
|
||||
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
Workspace workspace = new Workspace();
|
||||
workspace.setDefaultCloseOperation(Workspace.EXIT_ON_CLOSE);
|
||||
workspace.setLocationRelativeTo(null);
|
||||
workspace.setVisible(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.hierarchyviewer.device;
|
||||
|
||||
public class Configuration {
|
||||
public static final int DEFAULT_SERVER_PORT = 4939;
|
||||
|
||||
// These codes must match the auto-generated codes in IWindowManager.java
|
||||
// See IWindowManager.aidl as well
|
||||
public static final int SERVICE_CODE_START_SERVER = 1;
|
||||
public static final int SERVICE_CODE_STOP_SERVER = 2;
|
||||
public static final int SERVICE_CODE_IS_SERVER_RUNNING = 3;
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.hierarchyviewer.device;
|
||||
|
||||
import com.android.ddmlib.AndroidDebugBridge;
|
||||
import com.android.ddmlib.Device;
|
||||
import com.android.ddmlib.Log;
|
||||
import com.android.ddmlib.MultiLineReceiver;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class DeviceBridge {
|
||||
private static AndroidDebugBridge bridge;
|
||||
|
||||
private static final HashMap<Device, Integer> devicePortMap = new HashMap<Device, Integer>();
|
||||
private static int nextLocalPort = Configuration.DEFAULT_SERVER_PORT;
|
||||
|
||||
public static void initDebugBridge() {
|
||||
if (bridge == null) {
|
||||
AndroidDebugBridge.init(false /* debugger support */);
|
||||
}
|
||||
if (bridge == null || !bridge.isConnected()) {
|
||||
String adbLocation = System.getProperty("hierarchyviewer.adb");
|
||||
if (adbLocation != null && adbLocation.length() != 0) {
|
||||
adbLocation += File.separator + "adb";
|
||||
} else {
|
||||
adbLocation = "adb";
|
||||
}
|
||||
|
||||
bridge = AndroidDebugBridge.createBridge(adbLocation, true);
|
||||
}
|
||||
}
|
||||
|
||||
public static void startListenForDevices(AndroidDebugBridge.IDeviceChangeListener listener) {
|
||||
AndroidDebugBridge.addDeviceChangeListener(listener);
|
||||
}
|
||||
|
||||
public static void stopListenForDevices(AndroidDebugBridge.IDeviceChangeListener listener) {
|
||||
AndroidDebugBridge.removeDeviceChangeListener(listener);
|
||||
}
|
||||
|
||||
public static Device[] getDevices() {
|
||||
return bridge.getDevices();
|
||||
}
|
||||
|
||||
public static boolean isViewServerRunning(Device device) {
|
||||
initDebugBridge();
|
||||
final boolean[] result = new boolean[1];
|
||||
try {
|
||||
if (device.isOnline()) {
|
||||
device.executeShellCommand(buildIsServerRunningShellCommand(),
|
||||
new BooleanResultReader(result));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return result[0];
|
||||
}
|
||||
|
||||
public static boolean startViewServer(Device device) {
|
||||
return startViewServer(device, Configuration.DEFAULT_SERVER_PORT);
|
||||
}
|
||||
|
||||
public static boolean startViewServer(Device device, int port) {
|
||||
initDebugBridge();
|
||||
final boolean[] result = new boolean[1];
|
||||
try {
|
||||
if (device.isOnline()) {
|
||||
device.executeShellCommand(buildStartServerShellCommand(port),
|
||||
new BooleanResultReader(result));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return result[0];
|
||||
}
|
||||
|
||||
public static boolean stopViewServer(Device device) {
|
||||
initDebugBridge();
|
||||
final boolean[] result = new boolean[1];
|
||||
try {
|
||||
if (device.isOnline()) {
|
||||
device.executeShellCommand(buildStopServerShellCommand(),
|
||||
new BooleanResultReader(result));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return result[0];
|
||||
}
|
||||
|
||||
public static void terminate() {
|
||||
AndroidDebugBridge.terminate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up a just-connected device to work with the view server.
|
||||
* <p/>This starts a port forwarding between a local port and a port on the device.
|
||||
* @param device
|
||||
*/
|
||||
public static void setupDeviceForward(Device device) {
|
||||
synchronized (devicePortMap) {
|
||||
if (device.getState() == Device.DeviceState.ONLINE) {
|
||||
int localPort = nextLocalPort++;
|
||||
device.createForward(localPort, Configuration.DEFAULT_SERVER_PORT);
|
||||
devicePortMap.put(device, localPort);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void removeDeviceForward(Device device) {
|
||||
synchronized (devicePortMap) {
|
||||
final Integer localPort = devicePortMap.get(device);
|
||||
if (localPort != null) {
|
||||
device.removeForward(localPort, Configuration.DEFAULT_SERVER_PORT);
|
||||
devicePortMap.remove(device);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static int getDeviceLocalPort(Device device) {
|
||||
synchronized (devicePortMap) {
|
||||
Integer port = devicePortMap.get(device);
|
||||
if (port != null) {
|
||||
return port;
|
||||
}
|
||||
|
||||
Log.e("hierarchy", "Missing forwarded port for " + device.getSerialNumber());
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static String buildStartServerShellCommand(int port) {
|
||||
return String.format("service call window %d i32 %d",
|
||||
Configuration.SERVICE_CODE_START_SERVER, port);
|
||||
}
|
||||
|
||||
private static String buildStopServerShellCommand() {
|
||||
return String.format("service call window %d", Configuration.SERVICE_CODE_STOP_SERVER);
|
||||
}
|
||||
|
||||
private static String buildIsServerRunningShellCommand() {
|
||||
return String.format("service call window %d",
|
||||
Configuration.SERVICE_CODE_IS_SERVER_RUNNING);
|
||||
}
|
||||
|
||||
private static class BooleanResultReader extends MultiLineReceiver {
|
||||
private final boolean[] mResult;
|
||||
|
||||
public BooleanResultReader(boolean[] result) {
|
||||
mResult = result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processNewLines(String[] strings) {
|
||||
if (strings.length > 0) {
|
||||
Pattern pattern = Pattern.compile(".*?\\([0-9]{8} ([0-9]{8}).*");
|
||||
Matcher matcher = pattern.matcher(strings[0]);
|
||||
if (matcher.matches()) {
|
||||
if (Integer.parseInt(matcher.group(1)) == 1) {
|
||||
mResult[0] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isCancelled() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.hierarchyviewer.device;
|
||||
|
||||
public class Window {
|
||||
public static final Window FOCUSED_WINDOW = new Window("<Focused Window>", -1);
|
||||
|
||||
private String title;
|
||||
private int hashCode;
|
||||
|
||||
public Window(String title, int hashCode) {
|
||||
this.title = title;
|
||||
this.hashCode = hashCode;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public int getHashCode() {
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
public String encode() {
|
||||
return Integer.toHexString(hashCode);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return title;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.hierarchyviewer.laf;
|
||||
|
||||
import javax.swing.border.AbstractBorder;
|
||||
import java.awt.*;
|
||||
|
||||
public class UnifiedContentBorder extends AbstractBorder {
|
||||
private static final Color BORDER_TOP_COLOR1 = new Color(0x575757);
|
||||
private static final Color BORDER_BOTTOM_COLOR1 = new Color(0x404040);
|
||||
private static final Color BORDER_BOTTOM_COLOR2 = new Color(0xd8d8d8);
|
||||
|
||||
public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
|
||||
g.setColor(BORDER_TOP_COLOR1);
|
||||
g.drawLine(x, y, x + width, y);
|
||||
g.setColor(BORDER_BOTTOM_COLOR1);
|
||||
g.drawLine(x, y + height - 2, x + width, y + height - 2);
|
||||
g.setColor(BORDER_BOTTOM_COLOR2);
|
||||
g.drawLine(x, y + height - 1, x + width, y + height - 1);
|
||||
}
|
||||
|
||||
public Insets getBorderInsets(Component component) {
|
||||
return new Insets(1, 0, 2, 0);
|
||||
}
|
||||
|
||||
public boolean isBorderOpaque() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.hierarchyviewer.scene;
|
||||
|
||||
import com.android.ddmlib.Device;
|
||||
import com.android.hierarchyviewer.device.Configuration;
|
||||
import com.android.hierarchyviewer.device.Window;
|
||||
import com.android.hierarchyviewer.device.DeviceBridge;
|
||||
|
||||
import java.awt.Image;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
public class CaptureLoader {
|
||||
public static Image loadCapture(Device device, Window window, String params) {
|
||||
Socket socket = null;
|
||||
BufferedInputStream in = null;
|
||||
BufferedWriter out = null;
|
||||
|
||||
try {
|
||||
socket = new Socket();
|
||||
socket.connect(new InetSocketAddress("127.0.0.1",
|
||||
DeviceBridge.getDeviceLocalPort(device)));
|
||||
|
||||
out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
|
||||
in = new BufferedInputStream(socket.getInputStream());
|
||||
|
||||
out.write("CAPTURE " + window.encode() + " " + params);
|
||||
out.newLine();
|
||||
out.flush();
|
||||
|
||||
return ImageIO.read(in);
|
||||
} catch (IOException e) {
|
||||
// Empty
|
||||
} finally {
|
||||
try {
|
||||
if (out != null) {
|
||||
out.close();
|
||||
}
|
||||
if (in != null) {
|
||||
in.close();
|
||||
}
|
||||
if (socket != null) {
|
||||
socket.close();
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.hierarchyviewer.scene;
|
||||
|
||||
import com.android.ddmlib.Device;
|
||||
import com.android.hierarchyviewer.device.DeviceBridge;
|
||||
import com.android.hierarchyviewer.device.Window;
|
||||
|
||||
import org.openide.util.Exceptions;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Stack;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class ViewHierarchyLoader {
|
||||
@SuppressWarnings("empty-statement")
|
||||
public static ViewHierarchyScene loadScene(Device device, Window window) {
|
||||
ViewHierarchyScene scene = new ViewHierarchyScene();
|
||||
|
||||
// Read the views tree
|
||||
Socket socket = null;
|
||||
BufferedReader in = null;
|
||||
BufferedWriter out = null;
|
||||
|
||||
String line;
|
||||
|
||||
try {
|
||||
System.out.println("==> Starting client");
|
||||
|
||||
socket = new Socket();
|
||||
socket.connect(new InetSocketAddress("127.0.0.1",
|
||||
DeviceBridge.getDeviceLocalPort(device)));
|
||||
|
||||
out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
|
||||
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
||||
|
||||
System.out.println("==> DUMP");
|
||||
|
||||
out.write("DUMP " + window.encode());
|
||||
out.newLine();
|
||||
out.flush();
|
||||
|
||||
Stack<ViewNode> stack = new Stack<ViewNode>();
|
||||
|
||||
boolean setRoot = true;
|
||||
ViewNode lastNode = null;
|
||||
int lastWhitespaceCount = Integer.MAX_VALUE;
|
||||
|
||||
while ((line = in.readLine()) != null) {
|
||||
if ("DONE.".equalsIgnoreCase(line)) {
|
||||
break;
|
||||
}
|
||||
|
||||
int whitespaceCount = countFrontWhitespace(line);
|
||||
if (lastWhitespaceCount < whitespaceCount) {
|
||||
stack.push(lastNode);
|
||||
} else if (!stack.isEmpty()) {
|
||||
final int count = lastWhitespaceCount - whitespaceCount;
|
||||
for (int i = 0; i < count; i++) {
|
||||
stack.pop();
|
||||
}
|
||||
}
|
||||
|
||||
lastWhitespaceCount = whitespaceCount;
|
||||
line = line.trim();
|
||||
int index = line.indexOf(' ');
|
||||
|
||||
lastNode = new ViewNode();
|
||||
lastNode.name = line.substring(0, index);
|
||||
|
||||
line = line.substring(index + 1);
|
||||
loadProperties(lastNode, line);
|
||||
|
||||
scene.addNode(lastNode);
|
||||
|
||||
if (setRoot) {
|
||||
scene.setRoot(lastNode);
|
||||
setRoot = false;
|
||||
}
|
||||
|
||||
if (!stack.isEmpty()) {
|
||||
final ViewNode parent = stack.peek();
|
||||
final String edge = parent.name + lastNode.name;
|
||||
scene.addEdge(edge);
|
||||
scene.setEdgeSource(edge, parent);
|
||||
scene.setEdgeTarget(edge, lastNode);
|
||||
lastNode.parent = parent;
|
||||
parent.children.add(lastNode);
|
||||
}
|
||||
}
|
||||
|
||||
updateIndices(scene.getRoot());
|
||||
|
||||
} catch (IOException ex) {
|
||||
Exceptions.printStackTrace(ex);
|
||||
} finally {
|
||||
try {
|
||||
if (out != null) {
|
||||
out.close();
|
||||
}
|
||||
if (in != null) {
|
||||
in.close();
|
||||
}
|
||||
socket.close();
|
||||
} catch (IOException ex) {
|
||||
Exceptions.printStackTrace(ex);
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println("==> DONE");
|
||||
|
||||
return scene;
|
||||
}
|
||||
|
||||
private static void updateIndices(ViewNode root) {
|
||||
if (root == null) return;
|
||||
|
||||
root.computeIndex();
|
||||
|
||||
for (ViewNode node : root.children) {
|
||||
updateIndices(node);
|
||||
}
|
||||
}
|
||||
|
||||
private static int countFrontWhitespace(String line) {
|
||||
int count = 0;
|
||||
while (line.charAt(count) == ' ') {
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private static void loadProperties(ViewNode node, String data) {
|
||||
int start = 0;
|
||||
boolean stop;
|
||||
|
||||
do {
|
||||
int index = data.indexOf('=', start);
|
||||
ViewNode.Property property = new ViewNode.Property();
|
||||
property.name = data.substring(start, index);
|
||||
|
||||
int index2 = data.indexOf(',', index + 1);
|
||||
int length = Integer.parseInt(data.substring(index + 1, index2));
|
||||
start = index2 + 1 + length;
|
||||
property.value = data.substring(index2 + 1, index2 + 1 + length);
|
||||
|
||||
node.properties.add(property);
|
||||
node.namedProperties.put(property.name, property);
|
||||
|
||||
stop = start >= data.length();
|
||||
if (!stop) {
|
||||
start += 1;
|
||||
}
|
||||
} while (!stop);
|
||||
|
||||
Collections.sort(node.properties, new Comparator<ViewNode.Property>() {
|
||||
public int compare(ViewNode.Property source, ViewNode.Property destination) {
|
||||
return source.name.compareTo(destination.name);
|
||||
}
|
||||
});
|
||||
|
||||
node.decode();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,262 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.hierarchyviewer.scene;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Font;
|
||||
import java.awt.GradientPaint;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.geom.Point2D;
|
||||
|
||||
import org.netbeans.api.visual.action.ActionFactory;
|
||||
import org.netbeans.api.visual.action.WidgetAction;
|
||||
import org.netbeans.api.visual.anchor.AnchorFactory;
|
||||
import org.netbeans.api.visual.border.BorderFactory;
|
||||
import org.netbeans.api.visual.graph.GraphScene;
|
||||
import org.netbeans.api.visual.layout.LayoutFactory;
|
||||
import org.netbeans.api.visual.model.ObjectState;
|
||||
import org.netbeans.api.visual.widget.ConnectionWidget;
|
||||
import org.netbeans.api.visual.widget.LabelWidget;
|
||||
import org.netbeans.api.visual.widget.LayerWidget;
|
||||
import org.netbeans.api.visual.widget.Widget;
|
||||
|
||||
public class ViewHierarchyScene extends GraphScene<ViewNode, String> {
|
||||
private ViewNode root;
|
||||
private LayerWidget widgetLayer;
|
||||
private LayerWidget connectionLayer;
|
||||
|
||||
private WidgetAction moveAction = ActionFactory.createMoveAction();
|
||||
|
||||
public ViewHierarchyScene() {
|
||||
widgetLayer = new LayerWidget(this);
|
||||
connectionLayer = new LayerWidget(this);
|
||||
|
||||
addChild(widgetLayer);
|
||||
addChild(connectionLayer);
|
||||
}
|
||||
|
||||
public ViewNode getRoot() {
|
||||
return root;
|
||||
}
|
||||
|
||||
void setRoot(ViewNode root) {
|
||||
this.root = root;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Widget attachNodeWidget(ViewNode node) {
|
||||
Widget widget = createBox(node, node.name, node.id);
|
||||
widget.getActions().addAction(createSelectAction());
|
||||
widget.getActions().addAction(moveAction);
|
||||
widgetLayer.addChild(widget);
|
||||
return widget;
|
||||
}
|
||||
|
||||
private Widget createBox(ViewNode node, String nodeName, String id) {
|
||||
final String shortName = getShortName(nodeName);
|
||||
node.setShortName(shortName);
|
||||
|
||||
GradientWidget box = new GradientWidget(this, node);
|
||||
box.setLayout(LayoutFactory.createVerticalFlowLayout());
|
||||
box.setBorder(BorderFactory.createLineBorder(2, Color.BLACK));
|
||||
box.setOpaque(true);
|
||||
|
||||
LabelWidget label = new LabelWidget(this);
|
||||
label.setFont(getDefaultFont().deriveFont(Font.PLAIN, 12.0f));
|
||||
label.setLabel(shortName);
|
||||
label.setBorder(BorderFactory.createEmptyBorder(6, 6, 0, 6));
|
||||
label.setAlignment(LabelWidget.Alignment.CENTER);
|
||||
|
||||
box.addChild(label);
|
||||
|
||||
label = new LabelWidget(this);
|
||||
label.setFont(getDefaultFont().deriveFont(Font.PLAIN, 10.0f));
|
||||
label.setLabel(getAddress(nodeName));
|
||||
label.setBorder(BorderFactory.createEmptyBorder(3, 6, 0, 6));
|
||||
label.setAlignment(LabelWidget.Alignment.CENTER);
|
||||
|
||||
box.addressWidget = label;
|
||||
|
||||
box.addChild(label);
|
||||
|
||||
label = new LabelWidget(this);
|
||||
label.setFont(getDefaultFont().deriveFont(Font.PLAIN, 10.0f));
|
||||
label.setLabel(id);
|
||||
label.setBorder(BorderFactory.createEmptyBorder(3, 6, 6, 6));
|
||||
label.setAlignment(LabelWidget.Alignment.CENTER);
|
||||
|
||||
box.addChild(label);
|
||||
|
||||
return box;
|
||||
}
|
||||
|
||||
private static String getAddress(String name) {
|
||||
String[] nameAndHashcode = name.split("@");
|
||||
return "@" + nameAndHashcode[1];
|
||||
}
|
||||
|
||||
private static String getShortName(String name) {
|
||||
String[] nameAndHashcode = name.split("@");
|
||||
String[] packages = nameAndHashcode[0].split("\\.");
|
||||
return packages[packages.length - 1];
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Widget attachEdgeWidget(String edge) {
|
||||
ConnectionWidget connectionWidget = new ConnectionWidget(this);
|
||||
connectionLayer.addChild(connectionWidget);
|
||||
return connectionWidget;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void attachEdgeSourceAnchor(String edge, ViewNode oldSourceNode, ViewNode sourceNode) {
|
||||
final ConnectionWidget connection = (ConnectionWidget) findWidget(edge);
|
||||
final Widget source = findWidget(sourceNode);
|
||||
connection.bringToBack();
|
||||
source.bringToFront();
|
||||
connection.setSourceAnchor(AnchorFactory.createRectangularAnchor(source));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void attachEdgeTargetAnchor(String edge, ViewNode oldTargetNode, ViewNode targetNode) {
|
||||
final ConnectionWidget connection = (ConnectionWidget) findWidget(edge);
|
||||
final Widget target = findWidget(targetNode);
|
||||
connection.bringToBack();
|
||||
target.bringToFront();
|
||||
connection.setTargetAnchor(AnchorFactory.createRectangularAnchor(target));
|
||||
}
|
||||
|
||||
private static class GradientWidget extends Widget implements ViewNode.StateListener {
|
||||
public static final GradientPaint BLUE_EXPERIENCE = new GradientPaint(
|
||||
new Point2D.Double(0, 0),
|
||||
new Color(168, 204, 241),
|
||||
new Point2D.Double(0, 1),
|
||||
new Color(44, 61, 146));
|
||||
public static final GradientPaint MAC_OSX_SELECTED = new GradientPaint(
|
||||
new Point2D.Double(0, 0),
|
||||
new Color(81, 141, 236),
|
||||
new Point2D.Double(0, 1),
|
||||
new Color(36, 96, 192));
|
||||
public static final GradientPaint MAC_OSX = new GradientPaint(
|
||||
new Point2D.Double(0, 0),
|
||||
new Color(167, 210, 250),
|
||||
new Point2D.Double(0, 1),
|
||||
new Color(99, 147, 206));
|
||||
public static final GradientPaint AERITH = new GradientPaint(
|
||||
new Point2D.Double(0, 0),
|
||||
Color.WHITE,
|
||||
new Point2D.Double(0, 1),
|
||||
new Color(64, 110, 161));
|
||||
public static final GradientPaint GRAY = new GradientPaint(
|
||||
new Point2D.Double(0, 0),
|
||||
new Color(226, 226, 226),
|
||||
new Point2D.Double(0, 1),
|
||||
new Color(250, 248, 248));
|
||||
public static final GradientPaint RED_XP = new GradientPaint(
|
||||
new Point2D.Double(0, 0),
|
||||
new Color(236, 81, 81),
|
||||
new Point2D.Double(0, 1),
|
||||
new Color(192, 36, 36));
|
||||
public static final GradientPaint NIGHT_GRAY = new GradientPaint(
|
||||
new Point2D.Double(0, 0),
|
||||
new Color(102, 111, 127),
|
||||
new Point2D.Double(0, 1),
|
||||
new Color(38, 45, 61));
|
||||
public static final GradientPaint NIGHT_GRAY_LIGHT = new GradientPaint(
|
||||
new Point2D.Double(0, 0),
|
||||
new Color(129, 138, 155),
|
||||
new Point2D.Double(0, 1),
|
||||
new Color(58, 66, 82));
|
||||
public static final GradientPaint NIGHT_GRAY_VERY_LIGHT = new GradientPaint(
|
||||
new Point2D.Double(0, 0),
|
||||
new Color(129, 138, 155, 60),
|
||||
new Point2D.Double(0, 1),
|
||||
new Color(58, 66, 82, 60));
|
||||
|
||||
private static Color UNSELECTED = Color.BLACK;
|
||||
private static Color SELECTED = Color.WHITE;
|
||||
|
||||
private final ViewNode node;
|
||||
|
||||
private LabelWidget addressWidget;
|
||||
|
||||
private boolean isSelected = false;
|
||||
private final GradientPaint selectedGradient = MAC_OSX_SELECTED;
|
||||
private final GradientPaint filteredGradient = RED_XP;
|
||||
private final GradientPaint focusGradient = NIGHT_GRAY_VERY_LIGHT;
|
||||
|
||||
public GradientWidget(ViewHierarchyScene scene, ViewNode node) {
|
||||
super(scene);
|
||||
this.node = node;
|
||||
node.setStateListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void notifyStateChanged(ObjectState previous, ObjectState state) {
|
||||
super.notifyStateChanged(previous, state);
|
||||
isSelected = state.isSelected() || state.isFocused() || state.isWidgetFocused();
|
||||
|
||||
pickChildrenColor();
|
||||
}
|
||||
|
||||
private void pickChildrenColor() {
|
||||
for (Widget child : getChildren()) {
|
||||
child.setForeground(isSelected || node.filtered ? SELECTED : UNSELECTED);
|
||||
}
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintBackground() {
|
||||
super.paintBackground();
|
||||
|
||||
Graphics2D g2 = getGraphics();
|
||||
Rectangle bounds = getBounds();
|
||||
|
||||
if (!isSelected) {
|
||||
if (!node.filtered) {
|
||||
if (!node.hasFocus) {
|
||||
g2.setColor(Color.WHITE);
|
||||
} else {
|
||||
g2.setPaint(new GradientPaint(bounds.x, bounds.y,
|
||||
focusGradient.getColor1(), bounds.x, bounds.x + bounds.height,
|
||||
focusGradient.getColor2()));
|
||||
}
|
||||
} else {
|
||||
g2.setPaint(new GradientPaint(bounds.x, bounds.y, filteredGradient.getColor1(),
|
||||
bounds.x, bounds.x + bounds.height, filteredGradient.getColor2()));
|
||||
}
|
||||
} else {
|
||||
g2.setPaint(new GradientPaint(bounds.x, bounds.y, selectedGradient.getColor1(),
|
||||
bounds.x, bounds.x + bounds.height, selectedGradient.getColor2()));
|
||||
}
|
||||
g2.fillRect(bounds.x, bounds.y, bounds.width, bounds.height);
|
||||
}
|
||||
|
||||
public void nodeStateChanged(ViewNode node) {
|
||||
pickChildrenColor();
|
||||
}
|
||||
|
||||
public void nodeIndexChanged(ViewNode node) {
|
||||
if (addressWidget != null) {
|
||||
addressWidget.setLabel("#" + node.index + addressWidget.getLabel());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.hierarchyviewer.scene;
|
||||
|
||||
import com.android.ddmlib.Device;
|
||||
import com.android.hierarchyviewer.device.Window;
|
||||
import com.android.hierarchyviewer.device.DeviceBridge;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
|
||||
public class ViewManager {
|
||||
public static void invalidate(Device device, Window window, String params) {
|
||||
sendCommand("INVALIDATE", device, window, params);
|
||||
}
|
||||
|
||||
public static void requestLayout(Device device, Window window, String params) {
|
||||
sendCommand("REQUEST_LAYOUT", device, window, params);
|
||||
}
|
||||
|
||||
private static void sendCommand(String command, Device device, Window window, String params) {
|
||||
Socket socket = null;
|
||||
BufferedWriter out = null;
|
||||
|
||||
try {
|
||||
socket = new Socket();
|
||||
socket.connect(new InetSocketAddress("127.0.0.1",
|
||||
DeviceBridge.getDeviceLocalPort(device)));
|
||||
|
||||
out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
|
||||
|
||||
out.write(command + " " + window.encode() + " " + params);
|
||||
out.newLine();
|
||||
out.flush();
|
||||
} catch (IOException e) {
|
||||
// Empty
|
||||
} finally {
|
||||
try {
|
||||
if (out != null) {
|
||||
out.close();
|
||||
}
|
||||
if (socket != null) {
|
||||
socket.close();
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.hierarchyviewer.scene;
|
||||
|
||||
import java.awt.Image;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class ViewNode {
|
||||
public String id;
|
||||
public String name;
|
||||
|
||||
public List<Property> properties = new ArrayList<Property>();
|
||||
public Map<String, Property> namedProperties = new HashMap<String, Property>();
|
||||
|
||||
public ViewNode parent;
|
||||
public List<ViewNode> children = new ArrayList<ViewNode>();
|
||||
|
||||
public Image image;
|
||||
|
||||
public int left;
|
||||
public int top;
|
||||
public int width;
|
||||
public int height;
|
||||
public int scrollX;
|
||||
public int scrollY;
|
||||
public int paddingLeft;
|
||||
public int paddingRight;
|
||||
public int paddingTop;
|
||||
public int paddingBottom;
|
||||
public int marginLeft;
|
||||
public int marginRight;
|
||||
public int marginTop;
|
||||
public int marginBottom;
|
||||
public int baseline;
|
||||
public boolean willNotDraw;
|
||||
public boolean hasMargins;
|
||||
|
||||
boolean hasFocus;
|
||||
int index;
|
||||
|
||||
public boolean decoded;
|
||||
public boolean filtered;
|
||||
|
||||
private String shortName;
|
||||
private StateListener listener;
|
||||
|
||||
void decode() {
|
||||
id = namedProperties.get("mID").value;
|
||||
|
||||
left = getInt("mLeft", 0);
|
||||
top = getInt("mTop", 0);
|
||||
width = getInt("getWidth()", 0);
|
||||
height = getInt("getHeight()", 0);
|
||||
scrollX = getInt("mScrollX", 0);
|
||||
scrollY = getInt("mScrollY", 0);
|
||||
paddingLeft = getInt("mPaddingLeft", 0);
|
||||
paddingRight = getInt("mPaddingRight", 0);
|
||||
paddingTop = getInt("mPaddingTop", 0);
|
||||
paddingBottom = getInt("mPaddingBottom", 0);
|
||||
marginLeft = getInt("layout_leftMargin", Integer.MIN_VALUE);
|
||||
marginRight = getInt("layout_rightMargin", Integer.MIN_VALUE);
|
||||
marginTop = getInt("layout_topMargin", Integer.MIN_VALUE);
|
||||
marginBottom = getInt("layout_bottomMargin", Integer.MIN_VALUE);
|
||||
baseline = getInt("getBaseline()", 0);
|
||||
willNotDraw = getBoolean("willNotDraw()", false);
|
||||
hasFocus = getBoolean("hasFocus()", false);
|
||||
|
||||
hasMargins = marginLeft != Integer.MIN_VALUE &&
|
||||
marginRight != Integer.MIN_VALUE &&
|
||||
marginTop != Integer.MIN_VALUE &&
|
||||
marginBottom != Integer.MIN_VALUE;
|
||||
|
||||
decoded = true;
|
||||
}
|
||||
|
||||
private boolean getBoolean(String name, boolean defaultValue) {
|
||||
Property p = namedProperties.get(name);
|
||||
if (p != null) {
|
||||
try {
|
||||
return Boolean.parseBoolean(p.value);
|
||||
} catch (NumberFormatException e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
private int getInt(String name, int defaultValue) {
|
||||
Property p = namedProperties.get(name);
|
||||
if (p != null) {
|
||||
try {
|
||||
return Integer.parseInt(p.value);
|
||||
} catch (NumberFormatException e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public void filter(Pattern pattern) {
|
||||
if (pattern == null || pattern.pattern().length() == 0) {
|
||||
filtered = false;
|
||||
} else {
|
||||
filtered = pattern.matcher(shortName).find() || pattern.matcher(id).find();
|
||||
}
|
||||
listener.nodeStateChanged(this);
|
||||
}
|
||||
|
||||
void computeIndex() {
|
||||
index = parent == null ? 0 : parent.children.indexOf(this);
|
||||
listener.nodeIndexChanged(this);
|
||||
}
|
||||
|
||||
void setShortName(String shortName) {
|
||||
this.shortName = shortName;
|
||||
}
|
||||
|
||||
void setStateListener(StateListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"StringEquality"})
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final ViewNode other = (ViewNode) obj;
|
||||
return !(this.name != other.name && (this.name == null || !this.name.equals(other.name)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 5;
|
||||
hash = 67 * hash + (this.name != null ? this.name.hashCode() : 0);
|
||||
return hash;
|
||||
}
|
||||
|
||||
public static class Property {
|
||||
public String name;
|
||||
public String value;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name + '=' + value;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"StringEquality"})
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final Property other = (Property) obj;
|
||||
if (this.name != other.name && (this.name == null || !this.name.equals(other.name))) {
|
||||
return false;
|
||||
}
|
||||
return !(this.value != other.value && (this.value == null || !this.value.equals(other.value)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 5;
|
||||
hash = 61 * hash + (this.name != null ? this.name.hashCode() : 0);
|
||||
hash = 61 * hash + (this.value != null ? this.value.hashCode() : 0);
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
|
||||
interface StateListener {
|
||||
void nodeStateChanged(ViewNode node);
|
||||
void nodeIndexChanged(ViewNode node);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.hierarchyviewer.scene;
|
||||
|
||||
import com.android.ddmlib.Device;
|
||||
import com.android.hierarchyviewer.device.DeviceBridge;
|
||||
import com.android.hierarchyviewer.device.Window;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class WindowsLoader {
|
||||
public static Window[] loadWindows(Device device) {
|
||||
Socket socket = null;
|
||||
BufferedReader in = null;
|
||||
BufferedWriter out = null;
|
||||
|
||||
try {
|
||||
ArrayList<Window> windows = new ArrayList<Window>();
|
||||
|
||||
socket = new Socket();
|
||||
socket.connect(new InetSocketAddress("127.0.0.1",
|
||||
DeviceBridge.getDeviceLocalPort(device)));
|
||||
|
||||
out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
|
||||
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
||||
|
||||
out.write("LIST");
|
||||
out.newLine();
|
||||
out.flush();
|
||||
|
||||
String line;
|
||||
while ((line = in.readLine()) != null) {
|
||||
if ("DONE.".equalsIgnoreCase(line)) {
|
||||
break;
|
||||
}
|
||||
|
||||
int index = line.indexOf(' ');
|
||||
if (index != -1) {
|
||||
Window w = new Window(line.substring(index + 1),
|
||||
Integer.parseInt(line.substring(0, index), 16));
|
||||
windows.add(w);
|
||||
}
|
||||
}
|
||||
|
||||
return windows.toArray(new Window[windows.size()]);
|
||||
} catch (IOException e) {
|
||||
// Empty
|
||||
} finally {
|
||||
try {
|
||||
if (out != null) {
|
||||
out.close();
|
||||
}
|
||||
if (in != null) {
|
||||
in.close();
|
||||
}
|
||||
if (socket != null) {
|
||||
socket.close();
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
return new Window[0];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.hierarchyviewer.ui;
|
||||
|
||||
import com.android.hierarchyviewer.scene.ViewNode;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
class CaptureRenderer extends JLabel {
|
||||
private ViewNode node;
|
||||
private boolean showExtras;
|
||||
|
||||
CaptureRenderer(ImageIcon icon, ViewNode node) {
|
||||
super(icon);
|
||||
this.node = node;
|
||||
setBackground(Color.BLACK);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getPreferredSize() {
|
||||
Dimension d = super.getPreferredSize();
|
||||
|
||||
if (node.hasMargins) {
|
||||
d.width += node.marginLeft + node.marginRight;
|
||||
d.height += node.marginTop + node.marginBottom;
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
public void setShowExtras(boolean showExtras) {
|
||||
this.showExtras = showExtras;
|
||||
repaint();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
Icon icon = getIcon();
|
||||
int width = icon.getIconWidth();
|
||||
int height = icon.getIconHeight();
|
||||
|
||||
int x = (getWidth() - width) / 2;
|
||||
int y = (getHeight() - height) / 2;
|
||||
|
||||
icon.paintIcon(this, g, x, y);
|
||||
|
||||
if (showExtras) {
|
||||
g.translate(x, y);
|
||||
g.setXORMode(Color.WHITE);
|
||||
if ((node.paddingBottom | node.paddingLeft |
|
||||
node.paddingTop | node.paddingRight) != 0) {
|
||||
g.setColor(Color.RED);
|
||||
g.drawRect(node.paddingLeft, node.paddingTop,
|
||||
width - node.paddingRight - node.paddingLeft,
|
||||
height - node.paddingBottom - node.paddingTop);
|
||||
}
|
||||
if (node.baseline != -1) {
|
||||
g.setColor(Color.BLUE);
|
||||
g.drawLine(0, node.baseline, width, node.baseline);
|
||||
}
|
||||
if (node.hasMargins && (node.marginLeft | node.marginBottom |
|
||||
node.marginRight | node.marginRight) != 0) {
|
||||
g.setColor(Color.BLACK);
|
||||
g.drawRect(-node.marginLeft, -node.marginTop,
|
||||
node.marginLeft + width + node.marginRight,
|
||||
node.marginTop + height + node.marginBottom);
|
||||
}
|
||||
g.translate(-x, -y);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.hierarchyviewer.ui;
|
||||
|
||||
import com.android.hierarchyviewer.scene.ViewHierarchyScene;
|
||||
import com.android.hierarchyviewer.scene.ViewNode;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.BorderFactory;
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Insets;
|
||||
import java.util.Set;
|
||||
|
||||
class LayoutRenderer extends JComponent {
|
||||
private static final int EMULATED_SCREEN_WIDTH = 320;
|
||||
private static final int EMULATED_SCREEN_HEIGHT = 480;
|
||||
private static final int SCREEN_MARGIN = 24;
|
||||
|
||||
private boolean showExtras;
|
||||
private ViewHierarchyScene scene;
|
||||
|
||||
LayoutRenderer(ViewHierarchyScene scene) {
|
||||
this.scene = scene;
|
||||
|
||||
setOpaque(true);
|
||||
setBorder(BorderFactory.createEmptyBorder(0, 0, 12, 0));
|
||||
setBackground(Color.BLACK);
|
||||
setForeground(Color.WHITE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getPreferredSize() {
|
||||
return new Dimension(EMULATED_SCREEN_WIDTH + SCREEN_MARGIN,
|
||||
EMULATED_SCREEN_HEIGHT + SCREEN_MARGIN);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
g.setColor(getBackground());
|
||||
g.fillRect(0, 0, getWidth(), getHeight());
|
||||
|
||||
Insets insets = getInsets();
|
||||
g.clipRect(insets.left, insets.top,
|
||||
getWidth() - insets.left - insets.right,
|
||||
getHeight() - insets.top - insets.bottom);
|
||||
|
||||
if (scene == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ViewNode root = scene.getRoot();
|
||||
if (root == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
int x = (getWidth() - insets.left - insets.right - root.width) / 2;
|
||||
int y = (getHeight() - insets.top - insets.bottom - root.height) / 2;
|
||||
g.translate(insets.left + x, insets.top + y);
|
||||
|
||||
g.setColor(getForeground());
|
||||
g.drawRect(root.left, root.top, root.width - 1, root.height - 1);
|
||||
g.clipRect(root.left - 1, root.top - 1, root.width + 1, root.height + 1);
|
||||
drawChildren(g, root, -root.scrollX, -root.scrollY);
|
||||
|
||||
Set<?> selection = scene.getSelectedObjects();
|
||||
if (selection.size() > 0) {
|
||||
ViewNode node = (ViewNode) selection.iterator().next();
|
||||
g.setColor(Color.RED);
|
||||
Graphics s = g.create();
|
||||
ViewNode p = node.parent;
|
||||
while (p != null) {
|
||||
s.translate(p.left - p.scrollX, p.top - p.scrollY);
|
||||
p = p.parent;
|
||||
}
|
||||
if (showExtras && node.image != null) {
|
||||
s.drawImage(node.image, node.left, node.top, null);
|
||||
}
|
||||
s.drawRect(node.left, node.top, node.width - 1, node.height - 1);
|
||||
s.dispose();
|
||||
}
|
||||
|
||||
g.translate(-insets.left - x, -insets.top - y);
|
||||
}
|
||||
|
||||
private void drawChildren(Graphics g, ViewNode root, int x, int y) {
|
||||
g.translate(x, y);
|
||||
for (ViewNode node : root.children) {
|
||||
if (!node.willNotDraw) {
|
||||
g.drawRect(node.left, node.top, node.width - 1, node.height - 1);
|
||||
}
|
||||
|
||||
if (node.children.size() > 0) {
|
||||
drawChildren(g, node,
|
||||
node.left - node.parent.scrollX,
|
||||
node.top - node.parent.scrollY);
|
||||
}
|
||||
}
|
||||
g.translate(-x, -y);
|
||||
}
|
||||
|
||||
public void setShowExtras(boolean showExtras) {
|
||||
this.showExtras = showExtras;
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,732 @@
|
||||
package com.android.hierarchyviewer.ui;
|
||||
|
||||
import com.android.ddmlib.Device;
|
||||
import com.android.ddmlib.RawImage;
|
||||
import com.android.hierarchyviewer.util.WorkerThread;
|
||||
import com.android.hierarchyviewer.scene.ViewNode;
|
||||
import com.android.hierarchyviewer.ui.util.PngFileFilter;
|
||||
import com.android.hierarchyviewer.ui.util.IconLoader;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.Timer;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JSlider;
|
||||
import javax.swing.Box;
|
||||
import javax.swing.JCheckBox;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JFileChooser;
|
||||
import javax.swing.event.ChangeListener;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import org.jdesktop.swingworker.SwingWorker;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.File;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Color;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.Point;
|
||||
import java.awt.GridBagLayout;
|
||||
import java.awt.GridBagConstraints;
|
||||
import java.awt.Insets;
|
||||
import java.awt.FlowLayout;
|
||||
import java.awt.AlphaComposite;
|
||||
import java.awt.RenderingHints;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseMotionAdapter;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
class ScreenViewer extends JPanel implements ActionListener {
|
||||
private final Workspace workspace;
|
||||
private final Device device;
|
||||
|
||||
private GetScreenshotTask task;
|
||||
private BufferedImage image;
|
||||
private int[] scanline;
|
||||
private volatile boolean isLoading;
|
||||
|
||||
private BufferedImage overlay;
|
||||
private AlphaComposite overlayAlpha = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.3f);
|
||||
|
||||
private ScreenViewer.LoupeStatus status;
|
||||
private ScreenViewer.LoupeViewer loupe;
|
||||
private ScreenViewer.Crosshair crosshair;
|
||||
|
||||
private int zoom = 8;
|
||||
private int y = 0;
|
||||
|
||||
private Timer timer;
|
||||
private ViewNode node;
|
||||
|
||||
ScreenViewer(Workspace workspace, Device device, int spacing) {
|
||||
setLayout(new BorderLayout());
|
||||
setOpaque(false);
|
||||
|
||||
this.workspace = workspace;
|
||||
this.device = device;
|
||||
|
||||
timer = new Timer(5000, this);
|
||||
timer.setInitialDelay(0);
|
||||
timer.setRepeats(true);
|
||||
|
||||
JPanel panel = buildViewerAndControls();
|
||||
add(panel, BorderLayout.WEST);
|
||||
|
||||
JPanel loupePanel = buildLoupePanel(spacing);
|
||||
add(loupePanel, BorderLayout.CENTER);
|
||||
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
timer.start();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private JPanel buildLoupePanel(int spacing) {
|
||||
loupe = new LoupeViewer();
|
||||
CrosshairPanel crosshairPanel = new CrosshairPanel(loupe);
|
||||
|
||||
JPanel loupePanel = new JPanel(new BorderLayout());
|
||||
loupePanel.add(crosshairPanel);
|
||||
status = new LoupeStatus();
|
||||
loupePanel.add(status, BorderLayout.SOUTH);
|
||||
|
||||
loupePanel.setBorder(BorderFactory.createEmptyBorder(0, spacing, 0, 0));
|
||||
return loupePanel;
|
||||
}
|
||||
|
||||
private JPanel buildViewerAndControls() {
|
||||
JPanel panel = new JPanel(new GridBagLayout());
|
||||
crosshair = new Crosshair(new ScreenshotViewer());
|
||||
panel.add(crosshair,
|
||||
new GridBagConstraints(0, y++, 2, 1, 1.0f, 0.0f,
|
||||
GridBagConstraints.FIRST_LINE_START, GridBagConstraints.NONE,
|
||||
new Insets(0, 0, 0, 0), 0, 0));
|
||||
buildSlider(panel, "Overlay:", "0%", "100%", 0, 100, 30, 1).addChangeListener(
|
||||
new ChangeListener() {
|
||||
public void stateChanged(ChangeEvent event) {
|
||||
float opacity = ((JSlider) event.getSource()).getValue() / 100.0f;
|
||||
overlayAlpha = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, opacity);
|
||||
repaint();
|
||||
}
|
||||
});
|
||||
buildOverlayExtraControls(panel);
|
||||
buildSlider(panel, "Refresh Rate:", "1s", "40s", 1, 40, 5, 1).addChangeListener(
|
||||
new ChangeListener() {
|
||||
public void stateChanged(ChangeEvent event) {
|
||||
int rate = ((JSlider) event.getSource()).getValue() * 1000;
|
||||
timer.setDelay(rate);
|
||||
timer.setInitialDelay(0);
|
||||
timer.restart();
|
||||
}
|
||||
});
|
||||
buildSlider(panel, "Zoom:", "2x", "24x", 2, 24, 8, 2).addChangeListener(
|
||||
new ChangeListener() {
|
||||
public void stateChanged(ChangeEvent event) {
|
||||
zoom = ((JSlider) event.getSource()).getValue();
|
||||
loupe.clearGrid = true;
|
||||
loupe.moveToPoint(crosshair.crosshair.x, crosshair.crosshair.y);
|
||||
repaint();
|
||||
}
|
||||
});
|
||||
panel.add(Box.createVerticalGlue(),
|
||||
new GridBagConstraints(0, y++, 2, 1, 1.0f, 1.0f,
|
||||
GridBagConstraints.FIRST_LINE_START, GridBagConstraints.NONE,
|
||||
new Insets(0, 0, 0, 0), 0, 0));
|
||||
return panel;
|
||||
}
|
||||
|
||||
private void buildOverlayExtraControls(JPanel panel) {
|
||||
JPanel extras = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0));
|
||||
|
||||
JButton loadOverlay = new JButton("Load...");
|
||||
loadOverlay.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent event) {
|
||||
SwingWorker<?, ?> worker = openOverlay();
|
||||
if (worker != null) {
|
||||
worker.execute();
|
||||
}
|
||||
}
|
||||
});
|
||||
extras.add(loadOverlay);
|
||||
|
||||
JCheckBox showInLoupe = new JCheckBox("Show in Loupe");
|
||||
showInLoupe.setSelected(false);
|
||||
showInLoupe.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent event) {
|
||||
loupe.showOverlay = ((JCheckBox) event.getSource()).isSelected();
|
||||
loupe.repaint();
|
||||
}
|
||||
});
|
||||
extras.add(showInLoupe);
|
||||
|
||||
panel.add(extras, new GridBagConstraints(1, y++, 1, 1, 1.0f, 0.0f,
|
||||
GridBagConstraints.LINE_START, GridBagConstraints.NONE,
|
||||
new Insets(0, 0, 0, 0), 0, 0));
|
||||
}
|
||||
|
||||
public SwingWorker<?, ?> openOverlay() {
|
||||
JFileChooser chooser = new JFileChooser();
|
||||
chooser.setFileFilter(new PngFileFilter());
|
||||
int choice = chooser.showOpenDialog(this);
|
||||
if (choice == JFileChooser.APPROVE_OPTION) {
|
||||
return new OpenOverlayTask(chooser.getSelectedFile());
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private JSlider buildSlider(JPanel panel, String title, String minName, String maxName,
|
||||
int min, int max, int value, int tick) {
|
||||
panel.add(new JLabel(title), new GridBagConstraints(0, y, 1, 1, 1.0f, 0.0f,
|
||||
GridBagConstraints.LINE_END, GridBagConstraints.NONE,
|
||||
new Insets(0, 0, 0, 6), 0, 0));
|
||||
JPanel sliderPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0));
|
||||
sliderPanel.add(new JLabel(minName));
|
||||
JSlider slider = new JSlider(min, max, value);
|
||||
slider.setMinorTickSpacing(tick);
|
||||
slider.setMajorTickSpacing(tick);
|
||||
slider.setSnapToTicks(true);
|
||||
sliderPanel.add(slider);
|
||||
sliderPanel.add(new JLabel(maxName));
|
||||
panel.add(sliderPanel, new GridBagConstraints(1, y++, 1, 1, 1.0f, 0.0f,
|
||||
GridBagConstraints.FIRST_LINE_START, GridBagConstraints.NONE,
|
||||
new Insets(0, 0, 0, 0), 0, 0));
|
||||
return slider;
|
||||
}
|
||||
|
||||
void stop() {
|
||||
timer.stop();
|
||||
}
|
||||
|
||||
void start() {
|
||||
timer.start();
|
||||
}
|
||||
|
||||
void select(ViewNode node) {
|
||||
this.node = node;
|
||||
repaint();
|
||||
}
|
||||
|
||||
class LoupeViewer extends JComponent {
|
||||
private final Color lineColor = new Color(1.0f, 1.0f, 1.0f, 0.3f);
|
||||
|
||||
private int width;
|
||||
private int height;
|
||||
private BufferedImage grid;
|
||||
private int left;
|
||||
private int top;
|
||||
public boolean clearGrid;
|
||||
|
||||
private final Rectangle clip = new Rectangle();
|
||||
private boolean showOverlay = false;
|
||||
|
||||
LoupeViewer() {
|
||||
addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mousePressed(MouseEvent event) {
|
||||
moveToPoint(event);
|
||||
}
|
||||
});
|
||||
addMouseMotionListener(new MouseMotionAdapter() {
|
||||
@Override
|
||||
public void mouseDragged(MouseEvent event) {
|
||||
moveToPoint(event);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
if (isLoading) {
|
||||
return;
|
||||
}
|
||||
|
||||
g.translate(-left, -top);
|
||||
|
||||
if (image != null) {
|
||||
Graphics2D g2 = (Graphics2D) g.create();
|
||||
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
|
||||
RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
|
||||
g2.scale(zoom, zoom);
|
||||
g2.drawImage(image, 0, 0, null);
|
||||
if (overlay != null && showOverlay) {
|
||||
g2.setComposite(overlayAlpha);
|
||||
g2.drawImage(overlay, 0, image.getHeight() - overlay.getHeight(), null);
|
||||
}
|
||||
g2.dispose();
|
||||
}
|
||||
|
||||
int width = getWidth();
|
||||
int height = getHeight();
|
||||
|
||||
Graphics2D g2 = null;
|
||||
if (width != this.width || height != this.height) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
|
||||
grid = new BufferedImage(width + zoom + 1, height + zoom + 1,
|
||||
BufferedImage.TYPE_INT_ARGB);
|
||||
clearGrid = true;
|
||||
g2 = grid.createGraphics();
|
||||
} else if (clearGrid) {
|
||||
g2 = grid.createGraphics();
|
||||
g2.setComposite(AlphaComposite.Clear);
|
||||
g2.fillRect(0, 0, grid.getWidth(), grid.getHeight());
|
||||
g2.setComposite(AlphaComposite.SrcOver);
|
||||
}
|
||||
|
||||
if (clearGrid) {
|
||||
clearGrid = false;
|
||||
|
||||
g2.setColor(lineColor);
|
||||
width += zoom;
|
||||
height += zoom;
|
||||
|
||||
for (int x = zoom; x <= width; x += zoom) {
|
||||
g2.drawLine(x, 0, x, height);
|
||||
}
|
||||
|
||||
for (int y = 0; y <= height; y += zoom) {
|
||||
g2.drawLine(0, y, width, y);
|
||||
}
|
||||
|
||||
g2.dispose();
|
||||
}
|
||||
|
||||
if (image != null) {
|
||||
g.getClipBounds(clip);
|
||||
g.clipRect(0, 0, image.getWidth() * zoom + 1, image.getHeight() * zoom + 1);
|
||||
g.drawImage(grid, clip.x - clip.x % zoom, clip.y - clip.y % zoom, null);
|
||||
}
|
||||
|
||||
g.translate(left, top);
|
||||
}
|
||||
|
||||
void moveToPoint(MouseEvent event) {
|
||||
int x = Math.max(0, Math.min((event.getX() + left) / zoom, image.getWidth() - 1));
|
||||
int y = Math.max(0, Math.min((event.getY() + top) / zoom, image.getHeight() - 1));
|
||||
moveToPoint(x, y);
|
||||
crosshair.moveToPoint(x, y);
|
||||
}
|
||||
|
||||
void moveToPoint(int x, int y) {
|
||||
left = x * zoom - width / 2 + zoom / 2;
|
||||
top = y * zoom - height / 2 + zoom / 2;
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
class LoupeStatus extends JPanel {
|
||||
private JLabel xLabel;
|
||||
private JLabel yLabel;
|
||||
private JLabel rLabel;
|
||||
private JLabel gLabel;
|
||||
private JLabel bLabel;
|
||||
private JLabel hLabel;
|
||||
private ScreenViewer.LoupeStatus.ColoredSquare square;
|
||||
private Color color;
|
||||
|
||||
LoupeStatus() {
|
||||
setOpaque(true);
|
||||
setLayout(new GridBagLayout());
|
||||
setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));
|
||||
|
||||
square = new ColoredSquare();
|
||||
add(square, new GridBagConstraints(0, 0, 1, 2, 0.0f, 0.0f,
|
||||
GridBagConstraints.LINE_START, GridBagConstraints.NONE,
|
||||
new Insets(0, 0, 0, 12), 0, 0 ));
|
||||
|
||||
JLabel label;
|
||||
|
||||
add(label = new JLabel("#ffffff"), new GridBagConstraints(0, 2, 1, 1, 0.0f, 0.0f,
|
||||
GridBagConstraints.LINE_START, GridBagConstraints.NONE,
|
||||
new Insets(0, 0, 0, 12), 0, 0 ));
|
||||
label.setForeground(Color.WHITE);
|
||||
hLabel = label;
|
||||
|
||||
add(label = new JLabel("R:"), new GridBagConstraints(1, 0, 1, 1, 0.0f, 0.0f,
|
||||
GridBagConstraints.LINE_START, GridBagConstraints.NONE,
|
||||
new Insets(0, 6, 0, 6), 0, 0 ));
|
||||
label.setForeground(Color.WHITE);
|
||||
add(label = new JLabel("255"), new GridBagConstraints(2, 0, 1, 1, 0.0f, 0.0f,
|
||||
GridBagConstraints.LINE_START, GridBagConstraints.NONE,
|
||||
new Insets(0, 0, 0, 12), 0, 0 ));
|
||||
label.setForeground(Color.WHITE);
|
||||
rLabel = label;
|
||||
|
||||
add(label = new JLabel("G:"), new GridBagConstraints(1, 1, 1, 1, 0.0f, 0.0f,
|
||||
GridBagConstraints.LINE_START, GridBagConstraints.NONE,
|
||||
new Insets(0, 6, 0, 6), 0, 0 ));
|
||||
label.setForeground(Color.WHITE);
|
||||
add(label = new JLabel("255"), new GridBagConstraints(2, 1, 1, 1, 0.0f, 0.0f,
|
||||
GridBagConstraints.LINE_START, GridBagConstraints.NONE,
|
||||
new Insets(0, 0, 0, 12), 0, 0 ));
|
||||
label.setForeground(Color.WHITE);
|
||||
gLabel = label;
|
||||
|
||||
add(label = new JLabel("B:"), new GridBagConstraints(1, 2, 1, 1, 0.0f, 0.0f,
|
||||
GridBagConstraints.LINE_START, GridBagConstraints.NONE,
|
||||
new Insets(0, 6, 0, 6), 0, 0 ));
|
||||
label.setForeground(Color.WHITE);
|
||||
add(label = new JLabel("255"), new GridBagConstraints(2, 2, 1, 1, 0.0f, 0.0f,
|
||||
GridBagConstraints.LINE_START, GridBagConstraints.NONE,
|
||||
new Insets(0, 0, 0, 12), 0, 0 ));
|
||||
label.setForeground(Color.WHITE);
|
||||
bLabel = label;
|
||||
|
||||
add(label = new JLabel("X:"), new GridBagConstraints(3, 0, 1, 1, 0.0f, 0.0f,
|
||||
GridBagConstraints.LINE_START, GridBagConstraints.NONE,
|
||||
new Insets(0, 6, 0, 6), 0, 0 ));
|
||||
label.setForeground(Color.WHITE);
|
||||
add(label = new JLabel("0 px"), new GridBagConstraints(4, 0, 1, 1, 0.0f, 0.0f,
|
||||
GridBagConstraints.LINE_START, GridBagConstraints.NONE,
|
||||
new Insets(0, 0, 0, 12), 0, 0 ));
|
||||
label.setForeground(Color.WHITE);
|
||||
xLabel = label;
|
||||
|
||||
add(label = new JLabel("Y:"), new GridBagConstraints(3, 1, 1, 1, 0.0f, 0.0f,
|
||||
GridBagConstraints.LINE_START, GridBagConstraints.NONE,
|
||||
new Insets(0, 6, 0, 6), 0, 0 ));
|
||||
label.setForeground(Color.WHITE);
|
||||
add(label = new JLabel("0 px"), new GridBagConstraints(4, 1, 1, 1, 0.0f, 0.0f,
|
||||
GridBagConstraints.LINE_START, GridBagConstraints.NONE,
|
||||
new Insets(0, 0, 0, 12), 0, 0 ));
|
||||
label.setForeground(Color.WHITE);
|
||||
yLabel = label;
|
||||
|
||||
add(Box.createHorizontalGlue(), new GridBagConstraints(5, 0, 1, 1, 1.0f, 0.0f,
|
||||
GridBagConstraints.LINE_START, GridBagConstraints.BOTH,
|
||||
new Insets(0, 0, 0, 0), 0, 0 ));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
g.setColor(Color.BLACK);
|
||||
g.fillRect(0, 0, getWidth(), getHeight());
|
||||
}
|
||||
|
||||
void showPixel(int x, int y) {
|
||||
xLabel.setText(x + " px");
|
||||
yLabel.setText(y + " px");
|
||||
|
||||
int pixel = image.getRGB(x, y);
|
||||
color = new Color(pixel);
|
||||
hLabel.setText("#" + Integer.toHexString(pixel));
|
||||
rLabel.setText(String.valueOf((pixel >> 16) & 0xff));
|
||||
gLabel.setText(String.valueOf((pixel >> 8) & 0xff));
|
||||
bLabel.setText(String.valueOf((pixel ) & 0xff));
|
||||
|
||||
square.repaint();
|
||||
}
|
||||
|
||||
private class ColoredSquare extends JComponent {
|
||||
@Override
|
||||
public Dimension getPreferredSize() {
|
||||
Dimension d = super.getPreferredSize();
|
||||
d.width = 60;
|
||||
d.height = 30;
|
||||
return d;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
g.setColor(color);
|
||||
g.fillRect(0, 0, getWidth(), getHeight());
|
||||
|
||||
g.setColor(Color.WHITE);
|
||||
g.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Crosshair extends JPanel {
|
||||
// magenta = 0xff5efe
|
||||
private final Color crosshairColor = new Color(0x00ffff);
|
||||
Point crosshair = new Point();
|
||||
private int width;
|
||||
private int height;
|
||||
|
||||
Crosshair(ScreenshotViewer screenshotViewer) {
|
||||
setOpaque(true);
|
||||
setLayout(new BorderLayout());
|
||||
add(screenshotViewer);
|
||||
addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mousePressed(MouseEvent event) {
|
||||
moveToPoint(event);
|
||||
}
|
||||
});
|
||||
addMouseMotionListener(new MouseMotionAdapter() {
|
||||
@Override
|
||||
public void mouseDragged(MouseEvent event) {
|
||||
moveToPoint(event);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void moveToPoint(int x, int y) {
|
||||
crosshair.x = x;
|
||||
crosshair.y = y;
|
||||
status.showPixel(crosshair.x, crosshair.y);
|
||||
repaint();
|
||||
}
|
||||
|
||||
private void moveToPoint(MouseEvent event) {
|
||||
crosshair.x = Math.max(0, Math.min(image.getWidth() - 1, event.getX()));
|
||||
crosshair.y = Math.max(0, Math.min(image.getHeight() - 1, event.getY()));
|
||||
loupe.moveToPoint(crosshair.x, crosshair.y);
|
||||
status.showPixel(crosshair.x, crosshair.y);
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paint(Graphics g) {
|
||||
super.paint(g);
|
||||
|
||||
if (crosshair == null || width != getWidth() || height != getHeight()) {
|
||||
width = getWidth();
|
||||
height = getHeight();
|
||||
crosshair = new Point(width / 2, height / 2);
|
||||
}
|
||||
|
||||
g.setColor(crosshairColor);
|
||||
|
||||
g.drawLine(crosshair.x, 0, crosshair.x, height);
|
||||
g.drawLine(0, crosshair.y, width, crosshair.y);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
super.paintComponent(g);
|
||||
g.setColor(Color.BLACK);
|
||||
g.fillRect(0, 0, getWidth(), getHeight());
|
||||
}
|
||||
}
|
||||
|
||||
class ScreenshotViewer extends JComponent {
|
||||
private final Color boundsColor = new Color(0xff5efe);
|
||||
|
||||
ScreenshotViewer() {
|
||||
setOpaque(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
g.setColor(Color.BLACK);
|
||||
g.fillRect(0, 0, getWidth(), getHeight());
|
||||
|
||||
if (isLoading) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (image != null) {
|
||||
g.drawImage(image, 0, 0, null);
|
||||
if (overlay != null) {
|
||||
Graphics2D g2 = (Graphics2D) g.create();
|
||||
g2.setComposite(overlayAlpha);
|
||||
g2.drawImage(overlay, 0, image.getHeight() - overlay.getHeight(), null);
|
||||
}
|
||||
}
|
||||
|
||||
if (node != null) {
|
||||
Graphics s = g.create();
|
||||
s.setColor(boundsColor);
|
||||
ViewNode p = node.parent;
|
||||
while (p != null) {
|
||||
s.translate(p.left - p.scrollX, p.top - p.scrollY);
|
||||
p = p.parent;
|
||||
}
|
||||
s.drawRect(node.left, node.top, node.width - 1, node.height - 1);
|
||||
s.translate(node.left, node.top);
|
||||
|
||||
s.setXORMode(Color.WHITE);
|
||||
if ((node.paddingBottom | node.paddingLeft |
|
||||
node.paddingTop | node.paddingRight) != 0) {
|
||||
s.setColor(Color.BLACK);
|
||||
s.drawRect(node.paddingLeft, node.paddingTop,
|
||||
node.width - node.paddingRight - node.paddingLeft - 1,
|
||||
node.height - node.paddingBottom - node.paddingTop - 1);
|
||||
}
|
||||
if (node.hasMargins && (node.marginLeft | node.marginBottom |
|
||||
node.marginRight | node.marginRight) != 0) {
|
||||
s.setColor(Color.BLACK);
|
||||
s.drawRect(-node.marginLeft, -node.marginTop,
|
||||
node.marginLeft + node.width + node.marginRight - 1,
|
||||
node.marginTop + node.height + node.marginBottom - 1);
|
||||
}
|
||||
|
||||
s.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getPreferredSize() {
|
||||
if (image == null) {
|
||||
return new Dimension(320, 480);
|
||||
}
|
||||
return new Dimension(image.getWidth(), image.getHeight());
|
||||
}
|
||||
}
|
||||
|
||||
private class CrosshairPanel extends JPanel {
|
||||
private final Color crosshairColor = new Color(0xff5efe);
|
||||
private final Insets insets = new Insets(0, 0, 0, 0);
|
||||
|
||||
CrosshairPanel(LoupeViewer loupe) {
|
||||
setLayout(new BorderLayout());
|
||||
add(loupe);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paint(Graphics g) {
|
||||
super.paint(g);
|
||||
|
||||
g.setColor(crosshairColor);
|
||||
|
||||
int width = getWidth();
|
||||
int height = getHeight();
|
||||
|
||||
getInsets(insets);
|
||||
|
||||
int x = (width - insets.left - insets.right) / 2;
|
||||
int y = (height - insets.top - insets.bottom) / 2;
|
||||
|
||||
g.drawLine(insets.left + x, insets.top, insets.left + x, height - insets.bottom);
|
||||
g.drawLine(insets.left, insets.top + y, width - insets.right, insets.top + y);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
g.setColor(Color.BLACK);
|
||||
Insets insets = getInsets();
|
||||
g.fillRect(insets.left, insets.top, getWidth() - insets.left - insets.right,
|
||||
getHeight() - insets.top - insets.bottom);
|
||||
}
|
||||
}
|
||||
|
||||
public void actionPerformed(ActionEvent event) {
|
||||
if (task != null && !task.isDone()) {
|
||||
return;
|
||||
}
|
||||
task = new GetScreenshotTask();
|
||||
task.execute();
|
||||
}
|
||||
|
||||
private class GetScreenshotTask extends SwingWorker<Boolean, Void> {
|
||||
private GetScreenshotTask() {
|
||||
workspace.beginTask();
|
||||
}
|
||||
|
||||
@Override
|
||||
@WorkerThread
|
||||
protected Boolean doInBackground() throws Exception {
|
||||
RawImage rawImage;
|
||||
try {
|
||||
rawImage = device.getScreenshot();
|
||||
} catch (IOException ioe) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean resize = false;
|
||||
isLoading = true;
|
||||
try {
|
||||
if (rawImage != null && rawImage.bpp == 16) {
|
||||
if (image == null || rawImage.width != image.getWidth() ||
|
||||
rawImage.height != image.getHeight()) {
|
||||
image = new BufferedImage(rawImage.width, rawImage.height,
|
||||
BufferedImage.TYPE_INT_ARGB);
|
||||
scanline = new int[rawImage.width];
|
||||
resize = true;
|
||||
}
|
||||
|
||||
byte[] buffer = rawImage.data;
|
||||
int index = 0;
|
||||
for (int y = 0 ; y < rawImage.height ; y++) {
|
||||
for (int x = 0 ; x < rawImage.width ; x++) {
|
||||
int value = buffer[index++] & 0x00FF;
|
||||
value |= (buffer[index++] << 8) & 0x0FF00;
|
||||
|
||||
int r = ((value >> 11) & 0x01F) << 3;
|
||||
int g = ((value >> 5) & 0x03F) << 2;
|
||||
int b = ((value ) & 0x01F) << 3;
|
||||
|
||||
scanline[x] = 0xFF << 24 | r << 16 | g << 8 | b;
|
||||
}
|
||||
image.setRGB(0, y, rawImage.width, 1, scanline,
|
||||
0, rawImage.width);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
isLoading = false;
|
||||
}
|
||||
|
||||
return resize;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done() {
|
||||
workspace.endTask();
|
||||
try {
|
||||
if (get()) {
|
||||
validate();
|
||||
crosshair.crosshair = new Point(image.getWidth() / 2,
|
||||
image.getHeight() / 2);
|
||||
status.showPixel(image.getWidth() / 2, image.getHeight() / 2);
|
||||
loupe.moveToPoint(image.getWidth() / 2, image.getHeight() / 2);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
} catch (ExecutionException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
private class OpenOverlayTask extends SwingWorker<BufferedImage, Void> {
|
||||
private File file;
|
||||
|
||||
private OpenOverlayTask(File file) {
|
||||
this.file = file;
|
||||
workspace.beginTask();
|
||||
}
|
||||
|
||||
@Override
|
||||
@WorkerThread
|
||||
protected BufferedImage doInBackground() {
|
||||
try {
|
||||
return IconLoader.toCompatibleImage(ImageIO.read(file));
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done() {
|
||||
try {
|
||||
overlay = get();
|
||||
repaint();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
} catch (ExecutionException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
workspace.endTask();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.hierarchyviewer.ui.action;
|
||||
|
||||
import org.jdesktop.swingworker.SwingWorker;
|
||||
|
||||
import javax.swing.AbstractAction;
|
||||
|
||||
public abstract class BackgroundAction extends AbstractAction {
|
||||
protected void executeBackgroundTask(SwingWorker<?, ?> worker) {
|
||||
if (worker != null) {
|
||||
worker.execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.hierarchyviewer.ui.action;
|
||||
|
||||
import com.android.hierarchyviewer.ui.Workspace;
|
||||
|
||||
import javax.swing.KeyStroke;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.Toolkit;
|
||||
|
||||
public class CaptureNodeAction extends BackgroundAction {
|
||||
public static final String ACTION_NAME = "captureNode";
|
||||
private Workspace mWorkspace;
|
||||
|
||||
public CaptureNodeAction(Workspace workspace) {
|
||||
putValue(NAME, "Display View");
|
||||
putValue(SHORT_DESCRIPTION, "Display View");
|
||||
putValue(LONG_DESCRIPTION, "Display View");
|
||||
putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_D,
|
||||
Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
|
||||
this.mWorkspace = workspace;
|
||||
}
|
||||
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
executeBackgroundTask(mWorkspace.showNodeCapture());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.hierarchyviewer.ui.action;
|
||||
|
||||
import com.android.hierarchyviewer.ui.Workspace;
|
||||
import com.android.hierarchyviewer.device.DeviceBridge;
|
||||
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.KeyStroke;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.Toolkit;
|
||||
|
||||
public class ExitAction extends AbstractAction {
|
||||
public static final String ACTION_NAME = "exit";
|
||||
private Workspace mWorkspace;
|
||||
|
||||
public ExitAction(Workspace workspace) {
|
||||
putValue(NAME, "Quit");
|
||||
putValue(SHORT_DESCRIPTION, "Quit");
|
||||
putValue(LONG_DESCRIPTION, "Quit");
|
||||
putValue(MNEMONIC_KEY, KeyEvent.VK_Q);
|
||||
putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_Q,
|
||||
Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
|
||||
this.mWorkspace = workspace;
|
||||
}
|
||||
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
mWorkspace.cleanupDevices();
|
||||
mWorkspace.dispose();
|
||||
DeviceBridge.terminate();
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.hierarchyviewer.ui.action;
|
||||
|
||||
import com.android.hierarchyviewer.ui.Workspace;
|
||||
|
||||
import javax.swing.KeyStroke;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.Toolkit;
|
||||
|
||||
public class InvalidateAction extends BackgroundAction {
|
||||
public static final String ACTION_NAME = "invalidate";
|
||||
private Workspace mWorkspace;
|
||||
|
||||
public InvalidateAction(Workspace workspace) {
|
||||
putValue(NAME, "Invalidate");
|
||||
putValue(SHORT_DESCRIPTION, "Invalidate");
|
||||
putValue(LONG_DESCRIPTION, "Invalidate");
|
||||
putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_I,
|
||||
Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
|
||||
this.mWorkspace = workspace;
|
||||
}
|
||||
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
executeBackgroundTask(mWorkspace.invalidateView());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.hierarchyviewer.ui.action;
|
||||
|
||||
import com.android.hierarchyviewer.ui.Workspace;
|
||||
|
||||
import javax.swing.KeyStroke;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.Toolkit;
|
||||
|
||||
public class LoadGraphAction extends BackgroundAction {
|
||||
public static final String ACTION_NAME = "loadGraph";
|
||||
private Workspace mWorkspace;
|
||||
|
||||
public LoadGraphAction(Workspace workspace) {
|
||||
putValue(NAME, "Load View Hierarchy");
|
||||
putValue(SHORT_DESCRIPTION, "Load");
|
||||
putValue(LONG_DESCRIPTION, "Load View Hierarchy");
|
||||
putValue(MNEMONIC_KEY, KeyEvent.VK_L);
|
||||
putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_L,
|
||||
Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
|
||||
this.mWorkspace = workspace;
|
||||
}
|
||||
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
executeBackgroundTask(mWorkspace.loadGraph());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.hierarchyviewer.ui.action;
|
||||
|
||||
import com.android.hierarchyviewer.ui.Workspace;
|
||||
|
||||
import javax.swing.KeyStroke;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.ActionEvent;
|
||||
|
||||
public class RefreshWindowsAction extends BackgroundAction {
|
||||
public static final String ACTION_NAME = "refreshWindows";
|
||||
private Workspace mWorkspace;
|
||||
|
||||
public RefreshWindowsAction(Workspace workspace) {
|
||||
putValue(NAME, "Refresh Windows");
|
||||
putValue(SHORT_DESCRIPTION, "Refresh");
|
||||
putValue(LONG_DESCRIPTION, "Refresh Windows");
|
||||
putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_F7, 0));
|
||||
this.mWorkspace = workspace;
|
||||
}
|
||||
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
executeBackgroundTask(mWorkspace.loadWindows());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.hierarchyviewer.ui.action;
|
||||
|
||||
import com.android.hierarchyviewer.ui.Workspace;
|
||||
|
||||
import javax.swing.KeyStroke;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.Toolkit;
|
||||
|
||||
public class RequestLayoutAction extends BackgroundAction {
|
||||
public static final String ACTION_NAME = "requestLayout";
|
||||
private Workspace mWorkspace;
|
||||
|
||||
public RequestLayoutAction(Workspace workspace) {
|
||||
putValue(NAME, "Request Layout");
|
||||
putValue(SHORT_DESCRIPTION, "Request Layout");
|
||||
putValue(LONG_DESCRIPTION, "Request Layout");
|
||||
putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_R,
|
||||
Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
|
||||
this.mWorkspace = workspace;
|
||||
}
|
||||
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
executeBackgroundTask(mWorkspace.requestLayout());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.hierarchyviewer.ui.action;
|
||||
|
||||
import com.android.hierarchyviewer.ui.Workspace;
|
||||
|
||||
import javax.swing.KeyStroke;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.Toolkit;
|
||||
|
||||
public class SaveSceneAction extends BackgroundAction {
|
||||
public static final String ACTION_NAME = "saveScene";
|
||||
private Workspace mWorkspace;
|
||||
|
||||
public SaveSceneAction(Workspace workspace) {
|
||||
mWorkspace = workspace;
|
||||
putValue(NAME, "Save as PNG...");
|
||||
putValue(SHORT_DESCRIPTION, "Save");
|
||||
putValue(LONG_DESCRIPTION, "Save as PNG...");
|
||||
putValue(MNEMONIC_KEY, KeyEvent.VK_S);
|
||||
putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_S,
|
||||
Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
|
||||
}
|
||||
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
executeBackgroundTask(mWorkspace.saveSceneAsImage());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.hierarchyviewer.ui.action;
|
||||
|
||||
import com.android.hierarchyviewer.ui.Workspace;
|
||||
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.KeyStroke;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.Toolkit;
|
||||
|
||||
public class ShowDevicesAction extends AbstractAction {
|
||||
public static final String ACTION_NAME = "showDevices";
|
||||
private Workspace mWorkspace;
|
||||
|
||||
public ShowDevicesAction(Workspace workspace) {
|
||||
putValue(NAME, "Devices");
|
||||
putValue(SHORT_DESCRIPTION, "Devices");
|
||||
putValue(LONG_DESCRIPTION, "Show Devices");
|
||||
putValue(MNEMONIC_KEY, KeyEvent.VK_D);
|
||||
putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_D,
|
||||
Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
|
||||
this.mWorkspace = workspace;
|
||||
}
|
||||
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
mWorkspace.showDevicesSelector();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.hierarchyviewer.ui.action;
|
||||
|
||||
import com.android.hierarchyviewer.ui.Workspace;
|
||||
|
||||
import javax.swing.KeyStroke;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.ActionEvent;
|
||||
|
||||
public class StartServerAction extends BackgroundAction {
|
||||
public static final String ACTION_NAME = "startServer";
|
||||
private Workspace mWorkspace;
|
||||
|
||||
public StartServerAction(Workspace workspace) {
|
||||
putValue(NAME, "Start Server");
|
||||
putValue(SHORT_DESCRIPTION, "Start");
|
||||
putValue(LONG_DESCRIPTION, "Start Server");
|
||||
putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_F5, 0));
|
||||
this.mWorkspace = workspace;
|
||||
}
|
||||
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
executeBackgroundTask(mWorkspace.startServer());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.hierarchyviewer.ui.action;
|
||||
|
||||
import com.android.hierarchyviewer.ui.Workspace;
|
||||
|
||||
import javax.swing.KeyStroke;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.ActionEvent;
|
||||
|
||||
public class StopServerAction extends BackgroundAction {
|
||||
public static final String ACTION_NAME = "stopServer";
|
||||
private Workspace mWorkspace;
|
||||
|
||||
public StopServerAction(Workspace workspace) {
|
||||
putValue(NAME, "Stop Server");
|
||||
putValue(SHORT_DESCRIPTION, "Stop");
|
||||
putValue(LONG_DESCRIPTION, "Stop Server");
|
||||
putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_F6, 0));
|
||||
this.mWorkspace = workspace;
|
||||
}
|
||||
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
executeBackgroundTask(mWorkspace.stopServer());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.hierarchyviewer.ui.model;
|
||||
|
||||
import com.android.hierarchyviewer.scene.ViewNode;
|
||||
|
||||
import javax.swing.table.DefaultTableModel;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class PropertiesTableModel extends DefaultTableModel {
|
||||
private List<ViewNode.Property> properties;
|
||||
private List<ViewNode.Property> privateProperties = new ArrayList<ViewNode.Property>();
|
||||
|
||||
public PropertiesTableModel(ViewNode node) {
|
||||
properties = node.properties;
|
||||
loadPrivateProperties(node);
|
||||
}
|
||||
|
||||
private void loadPrivateProperties(ViewNode node) {
|
||||
int x = node.left;
|
||||
int y = node.top;
|
||||
ViewNode p = node.parent;
|
||||
while (p != null) {
|
||||
x += p.left - p.scrollX;
|
||||
y += p.top - p.scrollY;
|
||||
p = p.parent;
|
||||
}
|
||||
|
||||
ViewNode.Property property = new ViewNode.Property();
|
||||
property.name = "absolute_x";
|
||||
property.value = String.valueOf(x);
|
||||
privateProperties.add(property);
|
||||
|
||||
property = new ViewNode.Property();
|
||||
property.name = "absolute_y";
|
||||
property.value = String.valueOf(y);
|
||||
privateProperties.add(property);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRowCount() {
|
||||
return (privateProperties == null ? 0 : privateProperties.size()) +
|
||||
(properties == null ? 0 : properties.size());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValueAt(int row, int column) {
|
||||
ViewNode.Property property;
|
||||
|
||||
if (row < privateProperties.size()) {
|
||||
property = privateProperties.get(row);
|
||||
} else {
|
||||
property = properties.get(row - privateProperties.size());
|
||||
}
|
||||
|
||||
return column == 0 ? property.name : property.value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnCount() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getColumnName(int column) {
|
||||
return column == 0 ? "Property" : "Value";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCellEditable(int arg0, int arg1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValueAt(Object arg0, int arg1, int arg2) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.hierarchyviewer.ui.model;
|
||||
|
||||
import com.android.hierarchyviewer.scene.ViewNode;
|
||||
|
||||
import javax.swing.tree.TreeModel;
|
||||
import javax.swing.tree.TreePath;
|
||||
import javax.swing.event.TreeModelListener;
|
||||
|
||||
public class ViewsTreeModel implements TreeModel {
|
||||
private final ViewNode root;
|
||||
|
||||
public ViewsTreeModel(ViewNode root) {
|
||||
this.root = root;
|
||||
}
|
||||
|
||||
public Object getRoot() {
|
||||
return root;
|
||||
}
|
||||
|
||||
public Object getChild(Object o, int i) {
|
||||
return ((ViewNode) o).children.get(i);
|
||||
}
|
||||
|
||||
public int getChildCount(Object o) {
|
||||
return ((ViewNode) o).children.size();
|
||||
}
|
||||
|
||||
public boolean isLeaf(Object child) {
|
||||
ViewNode node = (ViewNode) child;
|
||||
return node.children == null || node.children.size() == 0;
|
||||
}
|
||||
|
||||
public void valueForPathChanged(TreePath treePath, Object child) {
|
||||
}
|
||||
|
||||
public int getIndexOfChild(Object parent, Object child) {
|
||||
//noinspection SuspiciousMethodCalls
|
||||
return ((ViewNode) parent).children.indexOf(child);
|
||||
}
|
||||
|
||||
public void addTreeModelListener(TreeModelListener treeModelListener) {
|
||||
}
|
||||
|
||||
public void removeTreeModelListener(TreeModelListener treeModelListener) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.android.hierarchyviewer.ui.util;
|
||||
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.imageio.ImageIO;
|
||||
import java.io.IOException;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.GraphicsEnvironment;
|
||||
import java.awt.GraphicsConfiguration;
|
||||
|
||||
public class IconLoader {
|
||||
public static Icon load(Class<?> klass, String path) {
|
||||
try {
|
||||
return new ImageIcon(ImageIO.read(klass.getResourceAsStream(path)));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static GraphicsConfiguration getGraphicsConfiguration() {
|
||||
GraphicsEnvironment environment = GraphicsEnvironment.getLocalGraphicsEnvironment();
|
||||
return environment.getDefaultScreenDevice().getDefaultConfiguration();
|
||||
}
|
||||
|
||||
private static boolean isHeadless() {
|
||||
return GraphicsEnvironment.isHeadless();
|
||||
}
|
||||
|
||||
public static BufferedImage toCompatibleImage(BufferedImage image) {
|
||||
if (isHeadless()) {
|
||||
return image;
|
||||
}
|
||||
|
||||
if (image.getColorModel().equals(
|
||||
getGraphicsConfiguration().getColorModel())) {
|
||||
return image;
|
||||
}
|
||||
|
||||
BufferedImage compatibleImage = getGraphicsConfiguration().createCompatibleImage(
|
||||
image.getWidth(), image.getHeight(), image.getTransparency());
|
||||
Graphics g = compatibleImage.getGraphics();
|
||||
g.drawImage(image, 0, 0, null);
|
||||
g.dispose();
|
||||
|
||||
return compatibleImage;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.hierarchyviewer.ui.util;
|
||||
|
||||
import javax.swing.filechooser.FileFilter;
|
||||
import java.io.File;
|
||||
|
||||
public class PngFileFilter extends FileFilter {
|
||||
@Override
|
||||
public boolean accept(File f) {
|
||||
return f.isDirectory() || f.getName().toLowerCase().endsWith(".png");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "PNG Image (*.png)";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.hierarchyviewer.util;
|
||||
|
||||
public class OS {
|
||||
private static boolean macOs;
|
||||
private static boolean leopard;
|
||||
private static boolean linux;
|
||||
private static boolean windows;
|
||||
|
||||
static {
|
||||
String osName = System.getProperty("os.name");
|
||||
macOs = "Mac OS X".startsWith(osName);
|
||||
linux = "Linux".startsWith(osName);
|
||||
windows = "Windows".startsWith(osName);
|
||||
|
||||
String version = System.getProperty("os.version");
|
||||
final String[] parts = version.split("\\.");
|
||||
leopard = Integer.parseInt(parts[0]) >= 10 && Integer.parseInt(parts[1]) >= 5;
|
||||
}
|
||||
|
||||
public static boolean isMacOsX() {
|
||||
return macOs;
|
||||
}
|
||||
|
||||
public static boolean isLeopardOrLater() {
|
||||
return leopard;
|
||||
}
|
||||
|
||||
public static boolean isLinux() {
|
||||
return linux;
|
||||
}
|
||||
|
||||
public static boolean isWindows() {
|
||||
return windows;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.hierarchyviewer.util;
|
||||
|
||||
import java.lang.annotation.Target;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* Simple utility class used only to mark methods that are not executed on the UI thread
|
||||
* (or Event Dispatch Thread in Swing/AWT.) This annotation's sole purpose is to help
|
||||
* reading the source code. It has no additional effect.
|
||||
*/
|
||||
@Target({ ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface WorkerThread {
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 749 B |
BIN
tools/hierarchyviewer/src/resources/images/icon-graph-view.png
Normal file
BIN
tools/hierarchyviewer/src/resources/images/icon-graph-view.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 747 B |
Binary file not shown.
|
After Width: | Height: | Size: 734 B |
Binary file not shown.
|
After Width: | Height: | Size: 733 B |
Reference in New Issue
Block a user