org.sikuli.android.ADBDevice Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sikulixapi Show documentation
Show all versions of sikulixapi Show documentation
... for visual testing and automation
/*
* Copyright (c) 2010-2016, Sikuli.org, sikulix.com
* Released under the MIT License.
*
*/
package org.sikuli.android;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.imgproc.Imgproc;
import org.sikuli.basics.Debug;
import org.sikuli.basics.FileManager;
import org.sikuli.script.RunTime;
import org.sikuli.script.ScreenImage;
import se.vidstige.jadb.JadbDevice;
import se.vidstige.jadb.JadbException;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class ADBDevice {
private static int lvl = 3;
private static void log(int level, String message, Object... args) {
Debug.logx(level, "ADBDevice: " + message, args);
}
private JadbDevice device = null;
private int devW = -1;
private int devH = -1;
private ADBRobot robot = null;
private ADBScreen screen = null;
private List deviceProps = new ArrayList<>();
private int deviceVersion = -1;
private String sDeviceVersion = "???";
private static ADBDevice adbDevice = null;
public static int KEY_HOME = 3;
public static int KEY_BACK = 4;
public static int KEY_MENU = 82;
public static int KEY_POWER = 26;
private ADBDevice() {
}
public static ADBDevice init() {
if (adbDevice == null) {
adbDevice = new ADBDevice();
adbDevice.device = ADBClient.getDevice();
if (adbDevice.device == null) {
adbDevice = null;
} else {
adbDevice.deviceProps = Arrays.asList(adbDevice.exec("getprop").split("\n"));
//[ro.build.version.release]: [6.0.1]
//[ro.product.brand]: [google]
//[ro.product.manufacturer]: [asus]
//[ro.product.model]: [Nexus 7]
//[ro.product.name]: [razor]
//[ro.serialno]: [094da986]
Pattern pProp = Pattern.compile("\\[(.*?)\\]:.*?\\[(.*)\\]");
Matcher mProp = null;
String val = "";
String key = "";
for (String prop : adbDevice.deviceProps) {
if (!prop.startsWith("[ro.")) continue;
mProp = pProp.matcher(prop);
if (mProp.find()) {
key = mProp.group(1);
if (key.contains("build.version.release")) {
val = mProp.group(2);
try {
adbDevice.deviceVersion = Integer.parseInt(val.split("\\.")[0]);
adbDevice.sDeviceVersion = val;
} catch (Exception e) {
}
}
}
}
log(lvl, "init: %s", adbDevice.toString());
}
}
return adbDevice;
}
public static void reset() {
adbDevice = null;
ADBClient.reset();
}
public String toString() {
return String.format("attached device: serial(%s) display(%dx%d) version(%s)",
getDeviceSerial(), getBounds().width, getBounds().height, sDeviceVersion);
}
public ADBRobot getRobot(ADBScreen screen) {
if (robot == null) {
this.screen = screen;
robot = new ADBRobot(screen, this);
}
return robot;
}
public String getDeviceSerial() {
return device.getSerial();
}
public Rectangle getBounds() {
if (devW < 0) {
Dimension dim = getDisplayDimension();
devW = (int) dim.getWidth();
devH = (int) dim.getHeight();
}
return new Rectangle(0, 0, devW, devH);
}
public ScreenImage captureScreen() {
BufferedImage bimg = captureDeviceScreen();
return new ScreenImage(getBounds(), bimg);
}
public ScreenImage captureScreen(Rectangle rect) {
BufferedImage bimg = captureDeviceScreen(rect.x, rect.y, rect.width, rect.height);
return new ScreenImage(rect, bimg);
}
public BufferedImage captureDeviceScreen() {
return captureDeviceScreen(0, 0, devW, devH);
}
public BufferedImage captureDeviceScreen(int y, int _h) {
return captureDeviceScreen(0, y, devW, _h);
}
public BufferedImage captureDeviceScreen(int x, int y, int w, int h) {
Mat matImage = captureDeviceScreenMat(x, y, w, h);
BufferedImage bImage = null;
if (matImage != null) {
bImage = new BufferedImage(matImage.width(), matImage.height(), BufferedImage.TYPE_3BYTE_BGR);
byte[] bImageData = ((DataBufferByte) bImage.getRaster().getDataBuffer()).getData();
matImage.get(0, 0, bImageData);
}
return bImage;
}
public Mat captureDeviceScreenMat(int x, int y, int w, int h) {
byte[] imagePrefix = new byte[12];
byte[] image = new byte[0];
int actW = w;
if (x + w > devW) {
actW = devW - x;
}
int actH = h;
if (y + h > devH) {
actH = devH - y;
}
Debug timer = Debug.startTimer();
try {
InputStream stdout = device.executeShell("screencap");
stdout.read(imagePrefix);
if (imagePrefix[8] != 0x01) {
log(-1, "captureDeviceScreenMat: image type not RGBA");
return null;
}
if (byte2int(imagePrefix, 0, 4) != devW || byte2int(imagePrefix, 4, 4) != devH) {
log(-1, "captureDeviceScreenMat: width or height differ from device values");
return null;
}
image = new byte[actW * actH * 4];
int lenRow = devW * 4;
byte[] row = new byte[lenRow];
for (int count = 0; count < y; count++) {
stdout.read(row);
}
boolean shortRow = x + actW < devW;
for (int count = 0; count < actH; count++) {
if (shortRow) {
stdout.read(row);
System.arraycopy(row, x * 4, image, count * actW * 4, actW * 4);
} else {
stdout.read(image, count * actW * 4, actW * 4);
}
}
long duration = timer.end();
log(lvl, "captureDeviceScreenMat:[%d,%d %dx%d] %d", x, y, actW, actH, duration);
} catch (IOException | JadbException e) {
log(-1, "captureDeviceScreenMat: [%d,%d %dx%d] %s", x, y, actW, actH, e);
}
Mat matOrg = new Mat(actH, actW, CvType.CV_8UC4);
matOrg.put(0, 0, image);
Mat matImage = new Mat();
Imgproc.cvtColor(matOrg, matImage, Imgproc.COLOR_RGBA2BGR, 3);
return matImage;
}
private int byte2int(byte[] bytes, int start, int len) {
int val = 0;
int fact = 1;
for (int i = start; i < start + len; i++) {
int b = bytes[i] & 0xff;
val += b * fact;
fact *= 256;
}
return val;
}
private Dimension getDisplayDimension() {
String dump = dumpsys("display");
String token = "mDefaultViewport= ... deviceWidth=1200, deviceHeight=1920}";
Dimension dim = null;
Pattern displayDimension = Pattern.compile(
"mDefaultViewport.*?=.*?deviceWidth=(\\d*).*?deviceHeight=(\\d*)");
Matcher match = displayDimension.matcher(dump);
if (match.find()) {
int w = Integer.parseInt(match.group(1));
int h = Integer.parseInt(match.group(2));
dim = new Dimension(w, h);
} else {
log(-1, "getDisplayDimension: dumpsys display: token not found: %s", token);
}
return dim;
}
public String exec(String command, String... args) {
InputStream stdout = null;
String out = "";
try {
stdout = device.executeShell(command, args);
out = inputStreamToString(stdout, "UTF-8");
} catch (IOException | JadbException e) {
log(-1, "exec: %s: %s", command, e);
}
return out;
}
public String dumpsys(String component) {
InputStream stdout = null;
String out = "";
try {
if (component == null || component.isEmpty()) {
component = "power";
}
if (component.toLowerCase().contains("all")) {
stdout = device.executeShell("dumpsys");
} else {
stdout = device.executeShell("dumpsys", component);
}
out = inputStreamToString(stdout, "UTF-8");
} catch (IOException | JadbException e) {
log(-1, "dumpsys: %s: %s", component, e);
}
return out;
}
public String printDump(String component) {
String dump = dumpsys(component);
if (!dump.isEmpty()) {
System.out.println("***** Android device dump: " + component);
System.out.println(dump);
}
return dump;
}
public String printDump() {
String dump = dumpsys("all");
if (!dump.isEmpty()) {
File out = new File(RunTime.get().fSikulixStore, "android_dump_" + getDeviceSerial() + ".txt");
System.out.println("***** Android device dump all services");
System.out.println("written to file: " + out.getAbsolutePath());
FileManager.writeStringToFile(dump, out);
}
return dump;
}
private static final int BUFFER_SIZE = 4 * 1024;
private static String inputStreamToString(InputStream inputStream, String charsetName) {
StringBuilder builder = new StringBuilder();
InputStreamReader reader = null;
try {
reader = new InputStreamReader(inputStream, charsetName);
char[] buffer = new char[BUFFER_SIZE];
int length;
while ((length = reader.read(buffer)) != -1) {
builder.append(buffer, 0, length);
}
return builder.toString();
} catch (Exception e) {
return "";
}
}
public void wakeUp(int seconds) {
int times = seconds * 4;
try {
if (null == isDisplayOn()) {
log(-1, "wakeUp: not possible - see log");
return;
}
device.executeShell("input", "keyevent", "26");
while (0 < times--) {
if (isDisplayOn()) {
return;
} else {
RunTime.pause(0.25f);
}
}
} catch (Exception e) {
log(-1, "wakeUp: did not work: %s", e);
}
log(-1, "wakeUp: timeout: %d seconds", seconds);
}
public Boolean isDisplayOn() {
// deviceidle | grep mScreenOn=true|false
// v < 5: power | grep mScreenOn=true|false
// v > 4: power | grep Display Power: state=ON|OFF
String dump = dumpsys("power");
Pattern displayOn = Pattern.compile("mScreenOn=(..)");
String isOn = "tr";
if (deviceVersion > 4) {
displayOn = Pattern.compile("Display Power: state=(..)");
isOn = "ON";
}
Matcher match = displayOn.matcher(dump);
if (match.find()) {
if (match.group(1).contains(isOn)) {
return true;
}
return false;
} else {
log(-1, "isDisplayOn: (Android version %d) dumpsys power: pattern not found: %s", deviceVersion, displayOn);
}
return null;
}
public void inputKeyEvent(int key) {
try {
device.executeShell("input", "keyevent", Integer.toString(key));
} catch (Exception e) {
log(-1, "inputKeyEvent: %d did not work: %s", e.getMessage());
}
}
public void tap(int x, int y) {
try {
device.executeShell("input tap", Integer.toString(x), Integer.toString(y));
} catch (IOException | JadbException e) {
log(-1, "tap: %s", e);
}
}
public void swipe(int x1, int y1, int x2, int y2) {
try {
device.executeShell("input swipe", Integer.toString(x1), Integer.toString(y1),
Integer.toString(x2), Integer.toString(y2));
} catch (IOException | JadbException e) {
log(-1, "swipe: %s", e);
}
}
private String textBuffer = "";
private boolean typing = false;
public synchronized boolean typeStarts() {
if (!typing) {
textBuffer = "";
typing = true;
return true;
}
return false;
}
public synchronized void typeEnds() {
if (typing) {
input(textBuffer);
typing = false;
}
}
public void typeChar(char character) {
if (typing) {
textBuffer += character;
}
}
public static float inputDelay = 0.05f;
public void input(String text) {
try {
device.executeShell("input text ", text);
RunTime.pause(text.length() * inputDelay);
} catch (Exception e) {
log(-1, "input: %s", e);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy