最近开发了总有同事抱怨UiAutomator有些界面的空间无法识别,以至于部分功能自动化脚本开发被Block,对此我研究了腾讯之前发布的一个adbui库,这个库是python版的,其中有一个通过ocr的方式来解决UIA开发过程中控件不能识别的问题,于是就想把这个功能移植到UIA的公共库里面。
【注意事项】
1. 该接口需要发送http请求和腾讯的云服务器进行通信,因此,使用该接口时,需保证数据流量或者Wi-Fi连接。
2. 该接口容易受到网速等不可逆因素影响,导致响应速度变慢,所以不适合大规模使用。
3. 该接口如果使用数据流量,会产生一定程度上流量费用。
长话短说,上代码.
首先生成签名工具类:
public class TencentSign {
/**
* 生成 Authorization 签名字段
*
* @param appId
* @param secretId
* @param secretKey
* @param expired
* @return
* @throws Exception
*/
public static String appSign(long appId, String secretId, String secretKey,
long expired) throws Exception {
long now = System.currentTimeMillis() / 1000;
int rdm = Math.abs(new Random().nextInt());
String plainText = String.format("a=%d&k=%s&e=%d&t=%d&r=%d&u=%s&f=", appId,
secretId, expired, now, rdm, "xx");
byte[] hmacDigest = HmacSha1(plainText, secretKey);
byte[] signContent = new byte[hmacDigest.length + plainText.getBytes().length];
System.arraycopy(hmacDigest, 0, signContent, 0, hmacDigest.length);
System.arraycopy(plainText.getBytes(), 0, signContent, hmacDigest.length,
plainText.getBytes().length);
return Base64Encode(signContent);
}
/**
* 生成 base64 编码
*
* @param binaryData
* @return
*/
public static String Base64Encode(byte[] binaryData) {
return Base64.encodeToString(binaryData, Base64.NO_WRAP);
}
/**
* 生成 hmacsha1 签名
*
* @param binaryData
* @param key
* @return
* @throws Exception
*/
public static byte[] HmacSha1(byte[] binaryData, String key) throws Exception {
Mac mac = Mac.getInstance("HmacSHA1");
SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(), "HmacSHA1");
mac.init(secretKey);
byte[] HmacSha1Digest = mac.doFinal(binaryData);
return HmacSha1Digest;
}
/**
* 生成 hmacsha1 签名
*
* @param plainText
* @param key
* @return
* @throws Exception
*/
public static byte[] HmacSha1(String plainText, String key) throws Exception {
return HmacSha1(plainText.getBytes(), key);
}
}
Http发送请求,根据adbui python库中定义的header以及data的格式。
public class HttpUtils {
/**
* post方式请求服务器(https协议)
*
* @param url
* 请求地址
* @param content
* 参数
* @return
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
* @throws IOException
*/
public static String post(String url, String sign, String content)
throws NoSuchAlgorithmException, KeyManagementException, IOException {
URL console = new URL(url);
HttpURLConnection conn = (HttpURLConnection) console.openConnection();
conn.setDoInput(true);
conn.setDoOutput(true);
conn.setRequestMethod("POST");
conn.setUseCaches(false);
conn.setInstanceFollowRedirects(true);
//根据adbui python库填写的Header参数
conn.addRequestProperty("Content-Type", "text/json");
conn.setRequestProperty("Authorization", sign);
conn.connect();
DataOutputStream out = new DataOutputStream(conn.getOutputStream());
out.write(content.getBytes("UTF-8"));
out.flush();
out.close();
if (conn.getResponseCode() != HttpURLConnection.HTTP_OK)
throw new RuntimeException("Request url failed ...");
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream(),"UTF-8"));
String result = "";
String getLine;
while ((getLine = in.readLine()) != null)
result += getLine;
in.close();
return result;
}
}
获取UI控件位置的的实现,实现过程中在base64编码那块入坑多次
public class TencentOCR {
public static List getUisByOcr(String text) throws Exception {
Common.takeScreenshot("ocrshot");
StringBuffer path = new StringBuffer(Environment.getExternalStorageDirectory().getPath());
path.append("/screenshot/ocrshot.png");
String result = getTextFromImage(path.toString());
JSONObject ocrResult = new JSONObject(result);
List<UI> uis = new ArrayList();
JSONArray items = ocrResult.getJSONArray("items");
for (int i = 0; i < items.length(); i++) {
int sameCount = 0;
JSONObject item = items.getJSONObject(i);
String itemString = item.getString("itemstring");
if (itemString.contains(text)) {
// print("itemString-->" + itemString);
JSONObject itemcoord = item.getJSONObject("itemcoord");
UI ui = new UI(itemcoord.getInt("x"), itemcoord.getInt("y"),
itemcoord.getInt("x") + itemcoord.getInt("width"),
itemcoord.getInt("y") + itemcoord.getInt("height"),
itemcoord.getInt("width"), itemcoord.getInt("height"));
uis.add(ui);
}
}
return uis;
}
/**
* 查找指定text的UI位置
*
* @param text 需要查找的text
* @return 返回UI位置
*/
public static UI getUiByOcr(String text) throws Exception {
List<UI> uilist = getUisByOcr(text);
if(!uilist.isEmpty())
return uilist.get(0);
return null;
}
public static UI getUiByOcr(String text, int index) throws Exception {
List<UI> uilist = getUisByOcr(text);
if(!uilist.isEmpty())
return uilist.get(index);
return null;
}
/**
* 识别图中文字信息
*
* @param imagePath 图片路径
* @return 返回ocr识别结果
*/
public static String getTextFromImage(String imagePath) throws Exception {
if (TextUtils.isEmpty(imagePath))
return null;
long expired = System.currentTimeMillis() / 1000 + 2592000;
//得到Authorization
String sign = TencentSign.appSign(YouTuHttpContants.APP_ID,
YouTuHttpContants.SECRET_ID,
YouTuHttpContants.SECRET_KEY,
expired);
String image = image2Base64(imagePath);
JSONObject jsonObject = new JSONObject();
jsonObject.put("app_id", String.valueOf(YouTuHttpContants.APP_ID));
jsonObject.put("session_id", "");
jsonObject.put("image", image);
String result = HttpUtils.post(YouTuHttpContants.OCR_URL, sign,
jsonObject.toString());
return result;
}
/**
* 将图中进行base64格式编码
*
* @param imagePath 图片路径
* @return
*/
public static String image2Base64(String imagePath) {
InputStream is = null;
byte[] data = null;
String result = null;
try {
is = new FileInputStream(imagePath);
data = new byte[is.available()];
is.read(data);
result = Base64.encodeToString(data, Base64.DEFAULT);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return result;
}
public static class UI {
private int x1;
private int y1;
private int x2;
private int y2;
private int width;
private int height;
private String text;
public UI(int x1, int y1, int x2, int y2, int width, int height) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
this.width = width;
this.height = height;
this.text = null;
}
public void setText(String text) {
this.text = text;
}
public String getText() {
return text;
}
public void click() {
int x = x1 + width / 2;
int y = y1 + height / 2;
mDevice.click(x, y);
}
}
}
|