blob: 8ead9de8c927b3efd970f56240ba4eb3e9e32a7b [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;
import com.android.ddmlib.CollectingOutputReceiver;
import com.android.ddmlib.IDevice;
import com.android.ddmlib.RawImage;
import com.android.ddmlib.SyncService;
import com.android.uiautomator.tree.BasicTreeNode;
import com.android.uiautomator.tree.RootWindowNode;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.ImageLoader;
import org.eclipse.swt.graphics.PaletteData;
import org.eclipse.swt.widgets.Display;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
public class UiAutomatorHelper {
public static final int UIAUTOMATOR_MIN_API_LEVEL = 16;
private static final String UIAUTOMATOR = "/system/bin/uiautomator"; //$NON-NLS-1$
private static final String UIAUTOMATOR_DUMP_COMMAND = "dump"; //$NON-NLS-1$
private static final String UIDUMP_DEVICE_PATH = "/data/local/tmp/uidump.xml"; //$NON-NLS-1$
private static final int XML_CAPTURE_TIMEOUT_SEC = 40;
private static boolean supportsUiAutomator(IDevice device) {
String apiLevelString = device.getProperty(IDevice.PROP_BUILD_API_LEVEL);
int apiLevel;
try {
apiLevel = Integer.parseInt(apiLevelString);
} catch (NumberFormatException e) {
apiLevel = UIAUTOMATOR_MIN_API_LEVEL;
}
return apiLevel >= UIAUTOMATOR_MIN_API_LEVEL;
}
private static void getUiHierarchyFile(IDevice device, File dst, IProgressMonitor monitor) {
if (monitor == null) {
monitor = new NullProgressMonitor();
}
monitor.subTask("Deleting old UI XML snapshot ...");
String command = "rm " + UIDUMP_DEVICE_PATH;
try {
CountDownLatch commandCompleteLatch = new CountDownLatch(1);
device.executeShellCommand(command,
new CollectingOutputReceiver(commandCompleteLatch));
commandCompleteLatch.await(5, TimeUnit.SECONDS);
} catch (Exception e1) {
// ignore exceptions while deleting stale files
}
monitor.subTask("Taking UI XML snapshot...");
command = String.format("%s %s %s", UIAUTOMATOR,
UIAUTOMATOR_DUMP_COMMAND,
UIDUMP_DEVICE_PATH);
CountDownLatch commandCompleteLatch = new CountDownLatch(1);
try {
device.executeShellCommand(
command,
new CollectingOutputReceiver(commandCompleteLatch),
XML_CAPTURE_TIMEOUT_SEC * 1000);
commandCompleteLatch.await(XML_CAPTURE_TIMEOUT_SEC, TimeUnit.SECONDS);
monitor.subTask("Pull UI XML snapshot from device...");
device.getSyncService().pullFile(UIDUMP_DEVICE_PATH,
dst.getAbsolutePath(), SyncService.getNullProgressMonitor());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static UiAutomatorResult takeSnapshot(IDevice device, IProgressMonitor monitor)
throws UiAutomatorException {
if (monitor == null) {
monitor = new NullProgressMonitor();
}
monitor.subTask("Checking if device support UI Automator");
if (!supportsUiAutomator(device)) {
String msg = "UI Automator requires a device with API Level "
+ UIAUTOMATOR_MIN_API_LEVEL;
throw new UiAutomatorException(msg, null);
}
monitor.subTask("Creating temporary files for uiautomator results.");
File tmpDir = null;
File xmlDumpFile = null;
File screenshotFile = null;
try {
tmpDir = File.createTempFile("uiautomatorviewer_", "");
tmpDir.delete();
if (!tmpDir.mkdirs())
throw new IOException("Failed to mkdir");
xmlDumpFile = File.createTempFile("dump_", ".uix", tmpDir);
screenshotFile = File.createTempFile("screenshot_", ".png", tmpDir);
} catch (Exception e) {
String msg = "Error while creating temporary file to save snapshot: "
+ e.getMessage();
throw new UiAutomatorException(msg, e);
}
tmpDir.deleteOnExit();
xmlDumpFile.deleteOnExit();
screenshotFile.deleteOnExit();
monitor.subTask("Obtaining UI hierarchy");
try {
UiAutomatorHelper.getUiHierarchyFile(device, xmlDumpFile, monitor);
} catch (Exception e) {
String msg = "Error while obtaining UI hierarchy XML file: " + e.getMessage();
throw new UiAutomatorException(msg, e);
}
UiAutomatorModel model;
try {
model = new UiAutomatorModel(xmlDumpFile);
} catch (Exception e) {
String msg = "Error while parsing UI hierarchy XML file: " + e.getMessage();
throw new UiAutomatorException(msg, e);
}
monitor.subTask("Obtaining device screenshot");
RawImage rawImage;
try {
rawImage = device.getScreenshot();
} catch (Exception e) {
String msg = "Error taking device screenshot: " + e.getMessage();
throw new UiAutomatorException(msg, e);
}
// rotate the screen shot per device rotation
BasicTreeNode root = model.getXmlRootNode();
if (root instanceof RootWindowNode) {
for (int i = 0; i < ((RootWindowNode)root).getRotation(); i++) {
rawImage = rawImage.getRotated();
}
}
PaletteData palette = new PaletteData(
rawImage.getRedMask(),
rawImage.getGreenMask(),
rawImage.getBlueMask());
ImageData imageData = new ImageData(rawImage.width, rawImage.height,
rawImage.bpp, palette, 1, rawImage.data);
ImageLoader loader = new ImageLoader();
loader.data = new ImageData[] { imageData };
loader.save(screenshotFile.getAbsolutePath(), SWT.IMAGE_PNG);
Image screenshot = new Image(Display.getDefault(), imageData);
return new UiAutomatorResult(xmlDumpFile, model, screenshot);
}
@SuppressWarnings("serial")
public static class UiAutomatorException extends Exception {
public UiAutomatorException(String msg, Throwable t) {
super(msg, t);
}
}
public static class UiAutomatorResult {
public final File uiHierarchy;
public final UiAutomatorModel model;
public final Image screenshot;
public UiAutomatorResult(File uiXml, UiAutomatorModel m, Image s) {
uiHierarchy = uiXml;
model = m;
screenshot = s;
}
}
}