upload android base code part9

This commit is contained in:
August 2018-08-08 20:16:11 +08:00
parent 5425409085
commit 071cdf34cd
2679 changed files with 329442 additions and 0 deletions

View file

@ -0,0 +1,8 @@
This version of HierarchyViewer is deprecated and obsolete.
The sources are left here just for reference. Please do not change them.
HierarchyViewer2 is located in /tools/swt/hierarchyviewer2.
This is the version that ships in the SDK.
--- RM 20130409

View file

@ -0,0 +1,20 @@
# 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_PREBUILT_EXECUTABLES := hierarchyviewer1
LOCAL_MODULE_TAGS := debug
include $(BUILD_HOST_PREBUILT)

View file

@ -0,0 +1,63 @@
#!/bin/sh
# Copyright 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.
# Set up prog to be the path of this script, including following symlinks,
# and set up progdir to be the fully-qualified pathname of its directory.
prog="$0"
while [ -h "${prog}" ]; do
newProg=`/bin/ls -ld "${prog}"`
newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
if expr "x${newProg}" : 'x/' >/dev/null; then
prog="${newProg}"
else
progdir=`dirname "${prog}"`
prog="${progdir}/${newProg}"
fi
done
oldwd=`pwd`
progdir=`dirname "${prog}"`
cd "${progdir}"
progdir=`pwd`
prog="${progdir}"/`basename "${prog}"`
cd "${oldwd}"
jarfile=hierarchyviewer.jar
frameworkdir="$progdir"
if [ ! -r "$frameworkdir/$jarfile" ]
then
frameworkdir=`dirname "$progdir"`/tools/lib
libdir=`dirname "$progdir"`/tools/lib
fi
if [ ! -r "$frameworkdir/$jarfile" ]
then
frameworkdir=`dirname "$progdir"`/framework
libdir=`dirname "$progdir"`/lib
fi
if [ ! -r "$frameworkdir/$jarfile" ]
then
echo `basename "$prog"`": can't find $jarfile"
exit 1
fi
if [ "$OSTYPE" = "cygwin" ] ; then
jarpath=`cygpath -w "$frameworkdir/$jarfile"`
progdir=`cygpath -w "$progdir"`
else
jarpath="$frameworkdir/$jarfile"
fi
# need to use "java.ext.dirs" because "-jar" causes classpath to be ignored
# might need more memory, e.g. -Xmx128M
exec java -Xmx512M -Djava.ext.dirs="$frameworkdir" -Dhierarchyviewer.adb="$progdir" -jar "$jarpath" "$@"

View file

@ -0,0 +1,46 @@
@echo off
rem Copyright (C) 2008 The Android Open Source Project
rem
rem Licensed under the Apache License, Version 2.0 (the "License");
rem you may not use this file except in compliance with the License.
rem You may obtain a copy of the License at
rem
rem http://www.apache.org/licenses/LICENSE-2.0
rem
rem Unless required by applicable law or agreed to in writing, software
rem distributed under the License is distributed on an "AS IS" BASIS,
rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
rem See the License for the specific language governing permissions and
rem limitations under the License.
rem don't modify the caller's environment
setlocal
rem Set up prog to be the path of this script, including following symlinks,
rem and set up progdir to be the fully-qualified pathname of its directory.
set prog=%~f0
rem Change current directory and drive to where the script is, to avoid
rem issues with directories containing whitespaces.
cd /d %~dp0
rem Check we have a valid Java.exe in the path.
set java_exe=
call lib\find_java.bat
if not defined java_exe goto :EOF
set jarfile=hierarchyviewer.jar
set frameworkdir=.
set libdir=
if exist %frameworkdir%\%jarfile% goto JarFileOk
set frameworkdir=lib
if exist %frameworkdir%\%jarfile% goto JarFileOk
set frameworkdir=..\framework
:JarFileOk
set jarpath=%frameworkdir%\%jarfile%
call "%java_exe%" -Xmx512m "-Djava.ext.dirs=%frameworkdir%" -Dhierarchyviewer.adb= -jar %jarpath% %*

View file

@ -0,0 +1,2 @@
Main-Class: com.android.hierarchyviewer.HierarchyViewer
Class-Path: ddmlib.jar org-openide-util.jar org-netbeans-api-visual.jar

View file

@ -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.
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-prebuilt \
org-openide-util \
org-netbeans-api-visual
LOCAL_MODULE := hierarchyviewer
LOCAL_MODULE_TAGS := debug
include $(BUILD_HOST_JAVA_LIBRARY)

View file

@ -0,0 +1,162 @@
/*
* 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.ddmlib.IDevice;
import com.android.hierarchyviewer.device.Window;
import com.android.hierarchyviewer.scene.CaptureLoader;
import com.android.hierarchyviewer.ui.Workspace;
import com.android.hierarchyviewer.device.DeviceBridge;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import java.io.File;
public class HierarchyViewer {
private static final CharSequence OS_WINDOWS = "Windows";
private static final CharSequence OS_MACOSX = "Mac OS X";
private static boolean sProfilingEnabled = true;
public static boolean isProfilingEnabled() {
return sProfilingEnabled;
}
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();
}
}
private static void listDevices() {
System.out.println("List of devices attached");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (IDevice device : DeviceBridge.getDevices()) {
printDevice(device);
}
DeviceBridge.terminate();
}
private static void printDevice(IDevice device) {
System.out.println(device.toString() + "\t\t" +
(device.isEmulator() ? "emulator" : "device"));
}
private static void outputPsd(String deviceName, String file) {
IDevice device = selectDevice(deviceName);
if (device != null) {
if (DeviceBridge.isViewServerRunning(device)) {
DeviceBridge.stopViewServer(device);
}
DeviceBridge.startViewServer(device);
DeviceBridge.setupDeviceForward(device);
System.out.println("Capturing layers to " + file);
CaptureLoader.saveLayers(device, Window.FOCUSED_WINDOW, new File(file));
} else {
System.out.println("The selected device does not exist");
}
DeviceBridge.terminate();
}
private static IDevice selectDevice(String deviceName) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (DeviceBridge.getDevices() == null) return null;
if (deviceName == null) return DeviceBridge.getDevices()[0];
for (IDevice device : DeviceBridge.getDevices()) {
if (device.getSerialNumber().equalsIgnoreCase(deviceName)) {
return device;
}
}
return null;
}
public static void main(String[] args) {
DeviceBridge.initDebugBridge();
if (args.length > 0) {
for (int i = 0; i < args.length; i++) {
String arg = args[i];
if ("--help".equalsIgnoreCase(arg)) {
System.out.println("Usage: hierarchyviewer1 [options]\n");
System.out.println("Options:");
System.out.println(" --help\t\t\t Show this help message and exit");
System.out.println(" --no-profiling\t Disable views profiling");
System.out.println(" --devices\t\t\t Show the list of available devices");
System.out.println(" --psd [device] <file>\t Export psd and exit");
System.exit(0);
} else if ("--no-profiling".equalsIgnoreCase(arg)) {
sProfilingEnabled = false;
} else if ("--devices".equalsIgnoreCase(arg)) {
listDevices();
System.exit(0);
} else if ("--psd".equalsIgnoreCase(arg)) {
if (i == args.length - 1) {
System.out.println("You must specify at least an output file with --psd");
System.exit(1);
}
String device = null;
String file = null;
if (i < args.length - 2) {
device = args[++i];
}
if (i < args.length - 1) {
file = args[++i];
}
outputPsd(device, file);
System.exit(0);
}
}
}
initUserInterface();
SwingUtilities.invokeLater(new Runnable() {
public void run() {
Workspace workspace = new Workspace();
workspace.setDefaultCloseOperation(Workspace.EXIT_ON_CLOSE);
workspace.setLocationRelativeTo(null);
workspace.setVisible(true);
}
});
}
}

View file

@ -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;
}

View file

@ -0,0 +1,237 @@
/*
* 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.AdbCommandRejectedException;
import com.android.ddmlib.AndroidDebugBridge;
import com.android.ddmlib.IDevice;
import com.android.ddmlib.Log;
import com.android.ddmlib.MultiLineReceiver;
import com.android.ddmlib.ShellCommandUnresponsiveException;
import com.android.ddmlib.TimeoutException;
import com.android.hierarchyviewer.scene.VersionLoader;
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<IDevice, Integer> devicePortMap = new HashMap<IDevice, 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 IDevice[] getDevices() {
return bridge.getDevices();
}
public static boolean isViewServerRunning(IDevice device) {
initDebugBridge();
final boolean[] result = new boolean[1];
try {
if (device.isOnline()) {
device.executeShellCommand(buildIsServerRunningShellCommand(),
new BooleanResultReader(result));
if (!result[0]) {
if (VersionLoader.loadProtocolVersion(device) > 2) {
result[0] = true;
}
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
} catch (AdbCommandRejectedException e) {
e.printStackTrace();
} catch (ShellCommandUnresponsiveException e) {
e.printStackTrace();
}
return result[0];
}
public static boolean startViewServer(IDevice device) {
return startViewServer(device, Configuration.DEFAULT_SERVER_PORT);
}
public static boolean startViewServer(IDevice 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();
} catch (TimeoutException e) {
e.printStackTrace();
} catch (AdbCommandRejectedException e) {
e.printStackTrace();
} catch (ShellCommandUnresponsiveException e) {
e.printStackTrace();
}
return result[0];
}
public static boolean stopViewServer(IDevice device) {
initDebugBridge();
final boolean[] result = new boolean[1];
try {
if (device.isOnline()) {
device.executeShellCommand(buildStopServerShellCommand(),
new BooleanResultReader(result));
}
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
} catch (AdbCommandRejectedException e) {
e.printStackTrace();
} catch (ShellCommandUnresponsiveException 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(IDevice device) {
synchronized (devicePortMap) {
if (device.getState() == IDevice.DeviceState.ONLINE) {
int localPort = nextLocalPort++;
try {
device.createForward(localPort, Configuration.DEFAULT_SERVER_PORT);
devicePortMap.put(device, localPort);
} catch (TimeoutException e) {
Log.e("hierarchy", "Timeout setting up port forwarding for " + device);
} catch (AdbCommandRejectedException e) {
Log.e("hierarchy", String.format(
"Adb rejected forward command for device %1$s: %2$s",
device, e.getMessage()));
} catch (IOException e) {
Log.e("hierarchy", String.format(
"Failed to create forward for device %1$s: %2$s",
device, e.getMessage()));
}
}
}
}
public static void removeDeviceForward(IDevice device) {
synchronized (devicePortMap) {
final Integer localPort = devicePortMap.get(device);
if (localPort != null) {
try {
device.removeForward(localPort, Configuration.DEFAULT_SERVER_PORT);
devicePortMap.remove(device);
} catch (TimeoutException e) {
Log.e("hierarchy", "Timeout removing port forwarding for " + device);
} catch (AdbCommandRejectedException e) {
Log.e("hierarchy", String.format(
"Adb rejected remove-forward command for device %1$s: %2$s",
device, e.getMessage()));
} catch (IOException e) {
Log.e("hierarchy", String.format(
"Failed to remove forward for device %1$s: %2$s",
device, e.getMessage()));
}
}
}
}
public static int getDeviceLocalPort(IDevice 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;
}
}
}

View file

@ -0,0 +1,46 @@
/*
* 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);
}
@Override
public String toString() {
return title;
}
}

View file

@ -0,0 +1,46 @@
/*
* 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);
@Override
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);
}
@Override
public Insets getBorderInsets(Component component) {
return new Insets(1, 0, 2, 0);
}
@Override
public boolean isBorderOpaque() {
return true;
}
}

View file

@ -0,0 +1,167 @@
/*
* 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.IDevice;
import com.android.hierarchyviewer.device.Window;
import com.android.hierarchyviewer.device.DeviceBridge;
import com.android.hierarchyviewer.ui.util.PsdFile;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.BufferedWriter;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileOutputStream;
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 boolean saveLayers(IDevice device, Window window, File file) {
Socket socket = null;
DataInputStream in = null;
BufferedWriter out = null;
boolean result = false;
try {
socket = new Socket();
socket.connect(new InetSocketAddress("127.0.0.1",
DeviceBridge.getDeviceLocalPort(device)));
out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
in = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
out.write("CAPTURE_LAYERS " + window.encode());
out.newLine();
out.flush();
int width = in.readInt();
int height = in.readInt();
PsdFile psd = new PsdFile(width, height);
while (readLayer(in, psd)) {
}
psd.write(new FileOutputStream(file));
result = true;
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (out != null) {
out.close();
}
if (in != null) {
in.close();
}
if (socket != null) {
socket.close();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
return result;
}
private static boolean readLayer(DataInputStream in, PsdFile psd) {
try {
if (in.read() == 2) {
System.out.println("Found end of layers list");
return false;
}
String name = in.readUTF();
System.out.println("name = " + name);
boolean visible = in.read() == 1;
int x = in.readInt();
int y = in.readInt();
int dataSize = in.readInt();
byte[] data = new byte[dataSize];
int read = 0;
while (read < dataSize) {
read += in.read(data, read, dataSize - read);
}
ByteArrayInputStream arrayIn = new ByteArrayInputStream(data);
BufferedImage chunk = ImageIO.read(arrayIn);
// Ensure the image is in the right format
BufferedImage image = new BufferedImage(chunk.getWidth(), chunk.getHeight(),
BufferedImage.TYPE_INT_ARGB);
Graphics2D g = image.createGraphics();
g.drawImage(chunk, null, 0, 0);
g.dispose();
psd.addLayer(name, image, new Point(x, y), visible);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public static Image loadCapture(IDevice 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;
}
}

View file

@ -0,0 +1,82 @@
/*
* Copyright (C) 2009 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.IDevice;
import com.android.hierarchyviewer.HierarchyViewer;
import com.android.hierarchyviewer.device.Window;
import com.android.hierarchyviewer.device.DeviceBridge;
import java.net.Socket;
import java.net.InetSocketAddress;
import java.io.BufferedWriter;
import java.io.OutputStreamWriter;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class ProfilesLoader {
public static double[] loadProfiles(IDevice device, Window window, String params) {
if (!HierarchyViewer.isProfilingEnabled()) {
return new double[] { 0.0, 0.0, 0.0 };
}
Socket socket = null;
BufferedReader 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 BufferedReader(new InputStreamReader(socket.getInputStream()));
out.write("PROFILE " + window.encode() + " " + params);
out.newLine();
out.flush();
String response = in.readLine();
String[] data = response.split(" ");
double[] profiles = new double[data.length];
for (int i = 0; i < data.length; i++) {
profiles[i] = (Long.parseLong(data[i]) / 1000.0) / 1000.0; // convert to ms
}
return profiles;
} 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;
}
}

View file

@ -0,0 +1,79 @@
/*
* Copyright (C) 2009 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.IDevice;
import com.android.hierarchyviewer.device.DeviceBridge;
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;
public class VersionLoader {
public static int loadServerVersion(IDevice device) {
return loadVersion(device, "SERVER");
}
public static int loadProtocolVersion(IDevice device) {
return loadVersion(device, "PROTOCOL");
}
private static int loadVersion(IDevice device, String command) {
Socket socket = null;
BufferedReader 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 BufferedReader(new InputStreamReader(socket.getInputStream()));
out.write(command);
out.newLine();
out.flush();
return Integer.parseInt(in.readLine());
} catch (Exception e) {
// Empty
} finally {
try {
if (out != null) {
out.close();
}
if (in != null) {
in.close();
}
if (socket != null) {
socket.close();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
// Versioning of the protocol and server was added with version 2
return 2;
}
}

View file

@ -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.scene;
import com.android.ddmlib.IDevice;
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;
public class ViewHierarchyLoader {
@SuppressWarnings("empty-statement")
public static ViewHierarchyScene loadScene(IDevice 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(), "utf-8"));
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 colonIndex = property.name.indexOf(':');
if (colonIndex != -1) {
property.name = property.name.substring(colonIndex + 1);
}
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();
}
}

View file

@ -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());
}
}
}
}

View file

@ -0,0 +1,71 @@
/*
* 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.IDevice;
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(IDevice device, Window window, String params) {
sendCommand("INVALIDATE", device, window, params);
}
public static void requestLayout(IDevice device, Window window, String params) {
sendCommand("REQUEST_LAYOUT", device, window, params);
}
public static void outputDisplayList(IDevice device, Window window, String params) {
sendCommand("OUTPUT_DISPLAYLIST", device, window, params);
}
private static void sendCommand(String command, IDevice 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();
}
}
}
}

View file

@ -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);
}
}

View file

@ -0,0 +1,96 @@
/*
* 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.IDevice;
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(IDevice device, int protocol, int server) {
Socket socket = null;
BufferedReader in = null;
BufferedWriter out = null;
System.out.println("protocol = " + protocol);
System.out.println("version = " + server);
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) {
String windowId = line.substring(0, index);
int id;
if (server > 2) {
id = (int) Long.parseLong(windowId, 16);
} else {
id = Integer.parseInt(windowId, 16);
}
Window w = new Window(line.substring(index + 1), id);
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];
}
}

View file

@ -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);
}
}
}

View file

@ -0,0 +1,177 @@
/*
* 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.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
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;
private JComponent sceneView;
LayoutRenderer(ViewHierarchyScene scene, JComponent sceneView) {
this.scene = scene;
this.sceneView = sceneView;
setOpaque(true);
setBorder(BorderFactory.createEmptyBorder(0, 0, 12, 0));
setBackground(Color.BLACK);
setForeground(Color.WHITE);
addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent event) {
selectChild(event.getX(), event.getY());
}
});
}
@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();
}
private void selectChild(int x, int y) {
if (scene == null) {
return;
}
ViewNode root = scene.getRoot();
if (root == null) {
return;
}
Insets insets = getInsets();
int xoffset = (getWidth() - insets.left - insets.right - root.width) / 2 + insets.left + 1;
int yoffset = (getHeight() - insets.top - insets.bottom - root.height) / 2 + insets.top + 1;
x -= xoffset;
y -= yoffset;
if (x >= 0 && x < EMULATED_SCREEN_WIDTH && y >= 0 && y < EMULATED_SCREEN_HEIGHT) {
ViewNode hit = findChild(root, root, x, y);
scene.setFocusedObject(hit);
sceneView.repaint();
}
}
private ViewNode findChild(ViewNode root, ViewNode besthit, int x, int y) {
ViewNode hit = besthit;
for (ViewNode node : root.children) {
if (node.left <= x && x < node.left + node.width &&
node.top <= y && y < node.top + node.height) {
if (node.width <= hit.width && node.height <= hit.height) {
hit = node;
}
}
if (node.children.size() > 0) {
hit = findChild(node, hit,
x - (node.left - node.parent.scrollX),
y - (node.top - node.parent.scrollY));
}
}
return hit;
}
}

View file

@ -0,0 +1,831 @@
package com.android.hierarchyviewer.ui;
import com.android.ddmlib.IDevice;
import com.android.ddmlib.RawImage;
import com.android.hierarchyviewer.scene.ViewNode;
import com.android.hierarchyviewer.ui.util.IconLoader;
import com.android.hierarchyviewer.ui.util.PngFileFilter;
import com.android.hierarchyviewer.util.WorkerThread;
import java.awt.AlphaComposite;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSlider;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.Timer;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
class ScreenViewer extends JPanel implements ActionListener {
private final Workspace workspace;
private final IDevice 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;
private JSlider zoomSlider;
ScreenViewer(Workspace workspace, IDevice device, int spacing) {
setLayout(new GridBagLayout());
setOpaque(false);
this.workspace = workspace;
this.device = device;
timer = new Timer(5000, this);
timer.setInitialDelay(0);
timer.setRepeats(true);
JPanel panel = buildViewerAndControls();
add(panel, new GridBagConstraints(0, 0, 1, 1, 0.3f, 1.0f,
GridBagConstraints.FIRST_LINE_START, GridBagConstraints.BOTH,
new Insets(0, 0, 0, 0), 0, 0));
JPanel loupePanel = buildLoupePanel(spacing);
add(loupePanel, new GridBagConstraints(1, 0, 1, 1, 0.7f, 1.0f,
GridBagConstraints.FIRST_LINE_START, GridBagConstraints.BOTH,
new Insets(0, 0, 0, 0), 0, 0));
SwingUtilities.invokeLater(new Runnable() {
public void run() {
timer.start();
}
});
}
private JPanel buildLoupePanel(int spacing) {
loupe = new LoupeViewer();
loupe.addMouseWheelListener(new WheelZoomListener());
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 class WheelZoomListener implements MouseWheelListener {
public void mouseWheelMoved(MouseWheelEvent e) {
if (zoomSlider != null) {
int val = zoomSlider.getValue();
val -= e.getWheelRotation() * 2;
zoomSlider.setValue(val);
}
}
}
private JPanel buildViewerAndControls() {
JPanel panel = new JPanel(new GridBagLayout());
crosshair = new Crosshair(new ScreenshotViewer());
crosshair.addMouseWheelListener(new WheelZoomListener());
JScrollPane scroller = new JScrollPane(crosshair);
scroller.setPreferredSize(new Dimension(320, 480));
scroller.setBorder(null);
panel.add(scroller,
new GridBagConstraints(0, y++, 2, 1, 1.0f, 1.0f,
GridBagConstraints.FIRST_LINE_START, GridBagConstraints.BOTH,
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();
}
});
zoomSlider = buildSlider(panel, "Zoom:", "2x", "24x", 2, 24, 8, 2);
zoomSlider.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;
private final ScreenshotViewer screenshotViewer;
Crosshair(ScreenshotViewer screenshotViewer) {
this.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 Dimension getPreferredSize() {
return screenshotViewer.getPreferredSize();
}
@Override
public Dimension getMaximumSize() {
return screenshotViewer.getPreferredSize();
}
@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) {
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;
}
switch (rawImage.bpp) {
case 16:
rawImage16toARGB(rawImage);
break;
case 32:
rawImage32toARGB(rawImage);
break;
}
}
} finally {
isLoading = false;
}
return resize;
}
private int getMask(int length) {
int res = 0;
for (int i = 0 ; i < length ; i++) {
res = (res << 1) + 1;
}
return res;
}
private void rawImage32toARGB(RawImage rawImage) {
byte[] buffer = rawImage.data;
int index = 0;
final int redOffset = rawImage.red_offset;
final int redLength = rawImage.red_length;
final int redMask = getMask(redLength);
final int greenOffset = rawImage.green_offset;
final int greenLength = rawImage.green_length;
final int greenMask = getMask(greenLength);
final int blueOffset = rawImage.blue_offset;
final int blueLength = rawImage.blue_length;
final int blueMask = getMask(blueLength);
final int alphaLength = rawImage.alpha_length;
final int alphaOffset = rawImage.alpha_offset;
final int alphaMask = getMask(alphaLength);
for (int y = 0 ; y < rawImage.height ; y++) {
for (int x = 0 ; x < rawImage.width ; x++) {
int value = buffer[index++] & 0x00FF;
value |= (buffer[index++] & 0x00FF) << 8;
value |= (buffer[index++] & 0x00FF) << 16;
value |= (buffer[index++] & 0x00FF) << 24;
int r = ((value >>> redOffset) & redMask) << (8 - redLength);
int g = ((value >>> greenOffset) & greenMask) << (8 - greenLength);
int b = ((value >>> blueOffset) & blueMask) << (8 - blueLength);
int a = 0xFF;
if (alphaLength != 0) {
a = ((value >>> alphaOffset) & alphaMask) << (8 - alphaLength);
}
scanline[x] = a << 24 | r << 16 | g << 8 | b;
}
image.setRGB(0, y, rawImage.width, 1, scanline,
0, rawImage.width);
}
}
private void rawImage16toARGB(RawImage rawImage) {
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);
}
}
@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

View file

@ -0,0 +1,28 @@
/*
* 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 javax.swing.AbstractAction;
import javax.swing.SwingWorker;
public abstract class BackgroundAction extends AbstractAction {
protected void executeBackgroundTask(SwingWorker<?, ?> worker) {
if (worker != null) {
worker.execute();
}
}
}

View file

@ -0,0 +1,42 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.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 CaptureLayersAction extends BackgroundAction {
public static final String ACTION_NAME = "captureLayers";
private Workspace mWorkspace;
public CaptureLayersAction(Workspace workspace) {
putValue(NAME, "Capture PSD");
putValue(SHORT_DESCRIPTION, "Capture PSD");
putValue(LONG_DESCRIPTION, "Capture current window into a Photoshop PSD file");
putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_P,
Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
this.mWorkspace = workspace;
}
public void actionPerformed(ActionEvent e) {
executeBackgroundTask(mWorkspace.captureLayers());
}
}

View file

@ -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());
}
}

View file

@ -0,0 +1,39 @@
/*
* Copyright (C) 2011 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 DumpDisplayListAction extends BackgroundAction {
public static final String ACTION_NAME = "dumpDisplayList";
private Workspace mWorkspace;
public DumpDisplayListAction(Workspace workspace) {
putValue(NAME, "Dump DisplayList");
putValue(SHORT_DESCRIPTION, "Dump DisplayList");
putValue(LONG_DESCRIPTION, "Dump DisplayList");
this.mWorkspace = workspace;
}
public void actionPerformed(ActionEvent e) {
executeBackgroundTask(mWorkspace.outputDisplayList());
}
}

View file

@ -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);
}
}

View file

@ -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());
}
}

View file

@ -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());
}
}

View file

@ -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());
}
}

View file

@ -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());
}
}

View file

@ -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());
}
}

View file

@ -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();
}
}

View file

@ -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());
}
}

View file

@ -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());
}
}

View file

@ -0,0 +1,68 @@
/*
* Copyright (C) 2009 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 javax.swing.table.DefaultTableModel;
import java.text.NumberFormat;
public class ProfilesTableModel extends DefaultTableModel {
private static final String[] NAMES = { "measure", "layout", "draw" };
private final double[] profiles;
private final NumberFormat formatter;
public ProfilesTableModel(double[] profiles) {
this.profiles = profiles;
formatter = NumberFormat.getNumberInstance();
}
@Override
public int getRowCount() {
return profiles == null ? 0 : profiles.length;
}
@Override
public Object getValueAt(int row, int column) {
if (profiles == null) return "";
if (column == 0) {
return NAMES[row];
}
return formatter.format(profiles[row]) + "";
}
@Override
public int getColumnCount() {
return 2;
}
@Override
public String getColumnName(int column) {
return column == 0 ? "Operation" : "Duration (ms)";
}
@Override
public boolean isCellEditable(int arg0, int arg1) {
return false;
}
@Override
public void setValueAt(Object arg0, int arg1, int arg2) {
}
}

View file

@ -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) {
}
}

View file

@ -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) {
}
}

View file

@ -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;
}
}

View file

@ -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)";
}
}

View file

@ -0,0 +1,442 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.hierarchyviewer.ui.util;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
/**
* Writes PSD file.
*
* Supports only 8 bits, RGB images with 4 channels.
*/
public class PsdFile {
private final Header mHeader;
private final ColorMode mColorMode;
private final ImageResources mImageResources;
private final LayersMasksInfo mLayersMasksInfo;
private final LayersInfo mLayersInfo;
private final BufferedImage mMergedImage;
private final Graphics2D mGraphics;
public PsdFile(int width, int height) {
mHeader = new Header(width, height);
mColorMode = new ColorMode();
mImageResources = new ImageResources();
mLayersMasksInfo = new LayersMasksInfo();
mLayersInfo = new LayersInfo();
mMergedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
mGraphics = mMergedImage.createGraphics();
}
public void addLayer(String name, BufferedImage image, Point offset) {
addLayer(name, image, offset, true);
}
public void addLayer(String name, BufferedImage image, Point offset, boolean visible) {
mLayersInfo.addLayer(name, image, offset, visible);
if (visible) mGraphics.drawImage(image, null, offset.x, offset.y);
}
public void write(OutputStream stream) {
mLayersMasksInfo.setLayersInfo(mLayersInfo);
DataOutputStream out = new DataOutputStream(new BufferedOutputStream(stream));
try {
mHeader.write(out);
out.flush();
mColorMode.write(out);
mImageResources.write(out);
mLayersMasksInfo.write(out);
mLayersInfo.write(out);
out.flush();
mLayersInfo.writeImageData(out);
out.flush();
writeImage(mMergedImage, out, false);
out.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private static void writeImage(BufferedImage image, DataOutputStream out, boolean split)
throws IOException {
if (!split) out.writeShort(0);
int width = image.getWidth();
int height = image.getHeight();
final int length = width * height;
int[] pixels = new int[length];
image.getData().getDataElements(0, 0, width, height, pixels);
byte[] a = new byte[length];
byte[] r = new byte[length];
byte[] g = new byte[length];
byte[] b = new byte[length];
for (int i = 0; i < length; i++) {
final int pixel = pixels[i];
a[i] = (byte) ((pixel >> 24) & 0xFF);
r[i] = (byte) ((pixel >> 16) & 0xFF);
g[i] = (byte) ((pixel >> 8) & 0xFF);
b[i] = (byte) (pixel & 0xFF);
}
if (split) out.writeShort(0);
if (split) out.write(a);
if (split) out.writeShort(0);
out.write(r);
if (split) out.writeShort(0);
out.write(g);
if (split) out.writeShort(0);
out.write(b);
if (!split) out.write(a);
}
@SuppressWarnings({"UnusedDeclaration"})
static class Header {
static final short MODE_BITMAP = 0;
static final short MODE_GRAYSCALE = 1;
static final short MODE_INDEXED = 2;
static final short MODE_RGB = 3;
static final short MODE_CMYK = 4;
static final short MODE_MULTI_CHANNEL = 7;
static final short MODE_DUOTONE = 8;
static final short MODE_LAB = 9;
final byte[] mSignature = "8BPS".getBytes();
final short mVersion = 1;
final byte[] mReserved = new byte[6];
final short mChannelCount = 4;
final int mHeight;
final int mWidth;
final short mDepth = 8;
final short mMode = MODE_RGB;
Header(int width, int height) {
mWidth = width;
mHeight = height;
}
void write(DataOutputStream out) throws IOException {
out.write(mSignature);
out.writeShort(mVersion);
out.write(mReserved);
out.writeShort(mChannelCount);
out.writeInt(mHeight);
out.writeInt(mWidth);
out.writeShort(mDepth);
out.writeShort(mMode);
}
}
// Unused at the moment
@SuppressWarnings({"UnusedDeclaration"})
static class ColorMode {
final int mLength = 0;
void write(DataOutputStream out) throws IOException {
out.writeInt(mLength);
}
}
// Unused at the moment
@SuppressWarnings({"UnusedDeclaration"})
static class ImageResources {
static final short RESOURCE_RESOLUTION_INFO = 0x03ED;
int mLength = 0;
final byte[] mSignature = "8BIM".getBytes();
final short mResourceId = RESOURCE_RESOLUTION_INFO;
final short mPad = 0;
final int mDataLength = 16;
final short mHorizontalDisplayUnit = 0x48; // 72 dpi
final int mHorizontalResolution = 1;
final short mWidthDisplayUnit = 1;
final short mVerticalDisplayUnit = 0x48; // 72 dpi
final int mVerticalResolution = 1;
final short mHeightDisplayUnit = 1;
ImageResources() {
mLength = mSignature.length;
mLength += 2;
mLength += 2;
mLength += 4;
mLength += 8;
mLength += 8;
}
void write(DataOutputStream out) throws IOException {
out.writeInt(mLength);
out.write(mSignature);
out.writeShort(mResourceId);
out.writeShort(mPad);
out.writeInt(mDataLength);
out.writeShort(mHorizontalDisplayUnit);
out.writeInt(mHorizontalResolution);
out.writeShort(mWidthDisplayUnit);
out.writeShort(mVerticalDisplayUnit);
out.writeInt(mVerticalResolution);
out.writeShort(mHeightDisplayUnit);
}
}
@SuppressWarnings({"UnusedDeclaration"})
static class LayersMasksInfo {
int mMiscLength;
int mLayerInfoLength;
void setLayersInfo(LayersInfo layersInfo) {
mLayerInfoLength = layersInfo.getLength();
// Round to the next multiple of 2
if ((mLayerInfoLength & 0x1) == 0x1) mLayerInfoLength++;
mMiscLength = mLayerInfoLength + 8;
}
void write(DataOutputStream out) throws IOException {
out.writeInt(mMiscLength);
out.writeInt(mLayerInfoLength);
}
}
@SuppressWarnings({"UnusedDeclaration"})
static class LayersInfo {
final List<Layer> mLayers = new ArrayList<Layer>();
void addLayer(String name, BufferedImage image, Point offset, boolean visible) {
mLayers.add(new Layer(name, image, offset, visible));
}
int getLength() {
int length = 2;
for (Layer layer : mLayers) {
length += layer.getLength();
}
return length;
}
void write(DataOutputStream out) throws IOException {
out.writeShort((short) -mLayers.size());
for (Layer layer : mLayers) {
layer.write(out);
}
}
void writeImageData(DataOutputStream out) throws IOException {
for (Layer layer : mLayers) {
layer.writeImageData(out);
}
// Global layer mask info length
out.writeInt(0);
}
}
@SuppressWarnings({"UnusedDeclaration"})
static class Layer {
static final byte OPACITY_TRANSPARENT = 0x0;
static final byte OPACITY_OPAQUE = (byte) 0xFF;
static final byte CLIPPING_BASE = 0x0;
static final byte CLIPPING_NON_BASE = 0x1;
static final byte FLAG_TRANSPARENCY_PROTECTED = 0x1;
static final byte FLAG_INVISIBLE = 0x2;
final int mTop;
final int mLeft;
final int mBottom;
final int mRight;
final short mChannelCount = 4;
final Channel[] mChannelInfo = new Channel[mChannelCount];
final byte[] mBlendSignature = "8BIM".getBytes();
final byte[] mBlendMode = "norm".getBytes();
final byte mOpacity = OPACITY_OPAQUE;
final byte mClipping = CLIPPING_BASE;
byte mFlags = 0x0;
final byte mFiller = 0x0;
int mExtraSize = 4 + 4;
final int mMaskDataLength = 0;
final int mBlendRangeDataLength = 0;
final byte[] mName;
final byte[] mLayerExtraSignature = "8BIM".getBytes();
final byte[] mLayerExtraKey = "luni".getBytes();
int mLayerExtraLength;
final String mOriginalName;
private BufferedImage mImage;
Layer(String name, BufferedImage image, Point offset, boolean visible) {
final int height = image.getHeight();
final int width = image.getWidth();
final int length = width * height;
mChannelInfo[0] = new Channel(Channel.ID_ALPHA, length);
mChannelInfo[1] = new Channel(Channel.ID_RED, length);
mChannelInfo[2] = new Channel(Channel.ID_GREEN, length);
mChannelInfo[3] = new Channel(Channel.ID_BLUE, length);
mTop = offset.y;
mLeft = offset.x;
mBottom = offset.y + height;
mRight = offset.x + width;
mOriginalName = name;
byte[] data = name.getBytes();
try {
mLayerExtraLength = 4 + mOriginalName.getBytes("UTF-16").length;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
final byte[] nameData = new byte[data.length + 1];
nameData[0] = (byte) (data.length & 0xFF);
System.arraycopy(data, 0, nameData, 1, data.length);
// This could be done in the same pass as above
if (nameData.length % 4 != 0) {
data = new byte[nameData.length + 4 - (nameData.length % 4)];
System.arraycopy(nameData, 0, data, 0, nameData.length);
mName = data;
} else {
mName = nameData;
}
mExtraSize += mName.length;
mExtraSize += mLayerExtraLength + 4 + mLayerExtraKey.length +
mLayerExtraSignature.length;
mImage = image;
if (!visible) {
mFlags |= FLAG_INVISIBLE;
}
}
int getLength() {
int length = 4 * 4 + 2;
for (Channel channel : mChannelInfo) {
length += channel.getLength();
}
length += mBlendSignature.length;
length += mBlendMode.length;
length += 4;
length += 4;
length += mExtraSize;
return length;
}
void write(DataOutputStream out) throws IOException {
out.writeInt(mTop);
out.writeInt(mLeft);
out.writeInt(mBottom);
out.writeInt(mRight);
out.writeShort(mChannelCount);
for (Channel channel : mChannelInfo) {
channel.write(out);
}
out.write(mBlendSignature);
out.write(mBlendMode);
out.write(mOpacity);
out.write(mClipping);
out.write(mFlags);
out.write(mFiller);
out.writeInt(mExtraSize);
out.writeInt(mMaskDataLength);
out.writeInt(mBlendRangeDataLength);
out.write(mName);
out.write(mLayerExtraSignature);
out.write(mLayerExtraKey);
out.writeInt(mLayerExtraLength);
out.writeInt(mOriginalName.length() + 1);
out.write(mOriginalName.getBytes("UTF-16"));
}
void writeImageData(DataOutputStream out) throws IOException {
writeImage(mImage, out, true);
}
}
@SuppressWarnings({"UnusedDeclaration"})
static class Channel {
static final short ID_RED = 0;
static final short ID_GREEN = 1;
static final short ID_BLUE = 2;
static final short ID_ALPHA = -1;
static final short ID_LAYER_MASK = -2;
final short mId;
final int mDataLength;
Channel(short id, int dataLength) {
mId = id;
mDataLength = dataLength + 2;
}
int getLength() {
return 2 + 4 + mDataLength;
}
void write(DataOutputStream out) throws IOException {
out.writeShort(mId);
out.writeInt(mDataLength);
}
}
}

View file

@ -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 PsdFileFilter extends FileFilter {
@Override
public boolean accept(File f) {
return f.isDirectory() || f.getName().toLowerCase().endsWith(".psd");
}
@Override
public String getDescription() {
return "Photoshop Document (*.psd)";
}
}

View file

@ -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;
}
}

View file

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

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