blob: 7d1eaa3cba975f1d0755a135f3025c47e9288ab4 [file] [log] [blame]
/*
* Copyright (C) 2012 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.uiautomator.actions;
import com.android.uiautomator.UiAutomatorModel;
import com.android.uiautomator.UiAutomatorViewer;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.resource.ImageDescriptor;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
public class ScreenshotAction extends Action {
UiAutomatorViewer mViewer;
public ScreenshotAction(UiAutomatorViewer viewer) {
super("&Device Screenshot");
mViewer = viewer;
}
@Override
public ImageDescriptor getImageDescriptor() {
return ImageHelper.loadImageDescriptorFromResource("images/screenshot.png");
}
@Override
public void run() {
ProgressMonitorDialog dialog = new ProgressMonitorDialog(mViewer.getShell());
try {
dialog.run(true, false, new IRunnableWithProgress() {
private void showError(final String msg, final Throwable t,
IProgressMonitor monitor) {
monitor.done();
mViewer.getShell().getDisplay().syncExec(new Runnable() {
@Override
public void run() {
Status s = new Status(IStatus.ERROR, "Screenshot", msg, t);
ErrorDialog.openError(
mViewer.getShell(), "Error", "Cannot take screenshot", s);
}
});
}
@Override
public void run(IProgressMonitor monitor) throws InvocationTargetException,
InterruptedException {
ProcRunner procRunner = null;
String serial = System.getenv("ANDROID_SERIAL");
File tmpDir = null;
File xmlDumpFile = null;
File screenshotFile = null;
int retCode = -1;
try {
tmpDir = File.createTempFile("uiautomatorviewer_", "");
tmpDir.delete();
if (!tmpDir.mkdirs())
throw new IOException("Failed to mkdir");
xmlDumpFile = File.createTempFile("dump_", ".xml", tmpDir);
screenshotFile = File.createTempFile("screenshot_", ".png", tmpDir);
} catch (IOException e) {
e.printStackTrace();
showError("Cannot get temp directory", e, monitor);
return;
}
UiAutomatorModel.getModel().registerTempDirectory(tmpDir);
// boiler plates to do a bunch of adb stuff to take XML snapshot and screenshot
monitor.beginTask("Getting UI status dump from device...",
IProgressMonitor.UNKNOWN);
monitor.subTask("Detecting device...");
procRunner = getAdbRunner(serial, "shell", "ls", "/system/bin/uiautomator");
try {
retCode = procRunner.run(30000);
} catch (IOException e) {
e.printStackTrace();
showError("Failed to detect device", e, monitor);
return;
}
if (retCode != 0) {
showError("No device or multiple devices connected. "
+ "Use ANDROID_SERIAL environment variable "
+ "if you have multiple devices", null, monitor);
return;
}
if (procRunner.getOutputBlob().indexOf("No such file or directory") != -1) {
showError("/system/bin/uiautomator not found on device", null, monitor);
return;
}
monitor.subTask("Deleting old UI XML snapshot ...");
procRunner = getAdbRunner(serial,
"shell", "rm", "/sdcard/uidump.xml");
try {
retCode = procRunner.run(30000);
if (retCode != 0) {
throw new IOException(
"Non-zero return code from \"rm\" xml dump command:\n"
+ procRunner.getOutputBlob());
}
} catch (IOException e) {
e.printStackTrace();
showError("Failed to execute \"rm\" xml dump command.", e, monitor);
return;
}
monitor.subTask("Taking UI XML snapshot...");
procRunner = getAdbRunner(serial,
"shell", "/system/bin/uiautomator", "dump", "/sdcard/uidump.xml");
try {
retCode = procRunner.run(30000);
if (retCode != 0) {
throw new IOException("Non-zero return code from dump command:\n"
+ procRunner.getOutputBlob());
}
} catch (IOException e) {
e.printStackTrace();
showError("Failed to execute dump command.", e, monitor);
return;
}
procRunner = getAdbRunner(serial,
"pull", "/sdcard/uidump.xml", xmlDumpFile.getAbsolutePath());
try {
retCode = procRunner.run(30000);
if (retCode != 0) {
throw new IOException("Non-zero return code from pull command:\n"
+ procRunner.getOutputBlob());
}
} catch (IOException e) {
e.printStackTrace();
showError("Failed to pull dump file.", e, monitor);
return;
}
monitor.subTask("Deleting old device screenshot...");
procRunner = getAdbRunner(serial,
"shell", "rm", "/sdcard/screenshot.png");
try {
retCode = procRunner.run(30000);
if (retCode != 0) {
throw new IOException(
"Non-zero return code from \"rm\" screenshot command:\n"
+ procRunner.getOutputBlob());
}
} catch (IOException e) {
e.printStackTrace();
showError("Failed to execute \"rm\" screenshot command.", e, monitor);
return;
}
monitor.subTask("Taking device screenshot...");
procRunner = getAdbRunner(serial,
"shell", "screencap", "-p", "/sdcard/screenshot.png");
try {
retCode = procRunner.run(30000);
if (retCode != 0) {
throw new IOException("Non-zero return code from screenshot command:\n"
+ procRunner.getOutputBlob());
}
} catch (IOException e) {
e.printStackTrace();
showError("Failed to execute screenshot command.", e, monitor);
return;
}
procRunner = getAdbRunner(serial,
"pull", "/sdcard/screenshot.png", screenshotFile.getAbsolutePath());
try {
retCode = procRunner.run(30000);
if (retCode != 0) {
throw new IOException("Non-zero return code from pull command:\n"
+ procRunner.getOutputBlob());
}
} catch (IOException e) {
e.printStackTrace();
showError("Failed to pull dump file.", e, monitor);
return;
}
final File png = screenshotFile, xml = xmlDumpFile;
if(png.length() == 0) {
showError("Screenshot file size is 0", null, monitor);
return;
} else {
mViewer.getShell().getDisplay().syncExec(new Runnable() {
@Override
public void run() {
UiAutomatorModel.getModel().loadScreenshotAndXmlDump(png, xml);
}
});
}
monitor.done();
}
});
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/*
* Convenience function to construct an 'adb' command, e.g. use 'adb' or 'adb -s NNN'
*/
private ProcRunner getAdbRunner(String serial, String... command) {
List<String> cmd = new ArrayList<String>();
cmd.add("adb");
if (serial != null) {
cmd.add("-s");
cmd.add(serial);
}
for (String s : command) {
cmd.add(s);
}
return new ProcRunner(cmd);
}
/**
* Convenience class to run external process.
*
* Always redirects stderr into stdout, has timeout control
*
*/
private static class ProcRunner {
ProcessBuilder mProcessBuilder;
List<String> mOutput = new ArrayList<String>();
public ProcRunner(List<String> command) {
mProcessBuilder = new ProcessBuilder(command).redirectErrorStream(true);
}
public int run(long timeout) throws IOException {
final Process p = mProcessBuilder.start();
Thread t = new Thread() {
@Override
public void run() {
String line;
mOutput.clear();
try {
BufferedReader br = new BufferedReader(new InputStreamReader(
p.getInputStream()));
while ((line = br.readLine()) != null) {
mOutput.add(line);
}
br.close();
} catch (IOException e) {
e.printStackTrace();
}
};
};
t.start();
try {
t.join(timeout);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (t.isAlive()) {
throw new IOException("external process not terminating.");
}
try {
return p.waitFor();
} catch (InterruptedException e) {
e.printStackTrace();
throw new IOException(e);
}
}
public String getOutputBlob() {
StringBuilder sb = new StringBuilder();
for (String line : mOutput) {
sb.append(line);
sb.append(System.getProperty("line.separator"));
}
return sb.toString();
}
}
}