version:1.6.0
fix: update:使用ocr进行坐标点击
This commit is contained in:
@@ -1,9 +1,13 @@
|
||||
package com.xxpatx.os.activity.callwechat;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.media.projection.MediaProjectionManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
@@ -18,7 +22,12 @@ import com.xxpatx.os.utils.AccessibilityUtils;
|
||||
|
||||
public class CallWechatActivity extends BaseMvvmActivity<CallWechatViewModel, ActivityWechatCallBinding> {
|
||||
|
||||
private static final String TAG = "CallWechatActivity";
|
||||
|
||||
private Contact mContact;
|
||||
private int mCallType = 0;
|
||||
|
||||
private static final int REQUEST_CODE_SCREEN_CAPTURE = 1874;
|
||||
|
||||
@Override
|
||||
public boolean setfitWindow() {
|
||||
@@ -67,6 +76,29 @@ public class CallWechatActivity extends BaseMvvmActivity<CallWechatViewModel, Ac
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
Log.e(TAG, "onActivityResult: requestCode = " + requestCode);
|
||||
Log.e(TAG, "onActivityResult: resultCode = " + resultCode);
|
||||
|
||||
if (requestCode == REQUEST_CODE_SCREEN_CAPTURE && resultCode == RESULT_OK) {
|
||||
// 启动屏幕捕获服务
|
||||
Intent intent = new Intent(CallWechatActivity.this, WeAccessibilityService.class);
|
||||
intent.putExtra("WechatInfo", mContact);
|
||||
intent.putExtra("call_type", mCallType);
|
||||
intent.putExtra("resultCode", resultCode);
|
||||
intent.putExtra("resultData", data);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
startForegroundService(intent);
|
||||
} else {
|
||||
startService(intent);
|
||||
}
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkSettings() {
|
||||
boolean accessibility = AccessibilityUtils.isAccessibilitySettingsOn(this);
|
||||
if (!accessibility) {
|
||||
@@ -89,29 +121,39 @@ public class CallWechatActivity extends BaseMvvmActivity<CallWechatViewModel, Ac
|
||||
public void callWechatVideo(View view) {
|
||||
if (TextUtils.isEmpty(mContact.getTag())) {
|
||||
Toaster.show("没有设置标签,无法拨打微信视频");
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
if (checkSettings()) {
|
||||
Intent intent = new Intent(CallWechatActivity.this, WeAccessibilityService.class);
|
||||
intent.putExtra("WechatInfo", mContact);
|
||||
intent.putExtra("call_type", WeAccessibilityService.TYPE_VIDEO);
|
||||
startService(intent);
|
||||
mCallType = WeAccessibilityService.TYPE_VIDEO;
|
||||
|
||||
// 1. 获取 MediaProjectionManager 实例
|
||||
MediaProjectionManager projectionManager = (MediaProjectionManager)
|
||||
getSystemService(Context.MEDIA_PROJECTION_SERVICE);
|
||||
Intent captureIntent = projectionManager.createScreenCaptureIntent();
|
||||
startActivityForResult(captureIntent, REQUEST_CODE_SCREEN_CAPTURE);
|
||||
|
||||
// // 2. 系统应用可通过反射绕过用户确认
|
||||
// Intent intent = new Intent("android.media.action.GET_SCREEN_CAPTURE");
|
||||
// intent.putExtra("extra_screen_capture_allowed", true);
|
||||
// startActivityForResult(intent, REQUEST_CODE_SCREEN_CAPTURE);
|
||||
}
|
||||
finish();
|
||||
}
|
||||
|
||||
public void callWechatVoice(View view) {
|
||||
if (TextUtils.isEmpty(mContact.getTag())) {
|
||||
Toaster.show("没有设置标签,无法拨打微信语音");
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
if (checkSettings()) {
|
||||
Intent intent = new Intent(CallWechatActivity.this, WeAccessibilityService.class);
|
||||
intent.putExtra("WechatInfo", mContact);
|
||||
intent.putExtra("call_type", WeAccessibilityService.TYPE_VOICE);
|
||||
startService(intent);
|
||||
mCallType = WeAccessibilityService.TYPE_VOICE;
|
||||
MediaProjectionManager projectionManager = (MediaProjectionManager)
|
||||
getSystemService(Context.MEDIA_PROJECTION_SERVICE);
|
||||
Intent captureIntent = projectionManager.createScreenCaptureIntent();
|
||||
startActivityForResult(captureIntent, REQUEST_CODE_SCREEN_CAPTURE);
|
||||
|
||||
}
|
||||
finish();
|
||||
}
|
||||
|
||||
public void exit(View view) {
|
||||
|
||||
@@ -74,6 +74,7 @@ import com.xxpatx.os.fragment.settings.SettingsFragment;
|
||||
import com.xxpatx.os.manager.DesktopIconManager;
|
||||
import com.xxpatx.os.manager.SpeechManager;
|
||||
import com.xxpatx.os.service.NotificationService;
|
||||
import com.xxpatx.os.service.WeAccessibilityService;
|
||||
import com.xxpatx.os.utils.ApkUtils;
|
||||
import com.xxpatx.os.utils.AppUsedTimeUtils;
|
||||
import com.xxpatx.os.utils.ContactsUtils;
|
||||
@@ -832,7 +833,7 @@ public class MainActivity extends BaseMvvmActivity<MainViewModel, ActivityMainBi
|
||||
// 去开启 监听通知权限
|
||||
startActivity(new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"));
|
||||
}
|
||||
|
||||
sendBroadcast(new Intent(WeAccessibilityService.STOP_RECORD_ACTION));
|
||||
setDockApp();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
package com.xxpatx.os.ocr.paddle;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class OCRPredictorNative {
|
||||
|
||||
private static final AtomicBoolean isSOLoaded = new AtomicBoolean();
|
||||
|
||||
public static void loadLibrary() throws RuntimeException {
|
||||
if (!isSOLoaded.get() && isSOLoaded.compareAndSet(false, true)) {
|
||||
try {
|
||||
System.loadLibrary("Native");
|
||||
} catch (Throwable e) {
|
||||
RuntimeException exception = new RuntimeException(
|
||||
"Load libNative.so failed, please check it exists in apk file.", e);
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Config config;
|
||||
|
||||
private long nativePointer = 0;
|
||||
|
||||
public OCRPredictorNative(Config config) {
|
||||
this.config = config;
|
||||
loadLibrary();
|
||||
nativePointer = init(config.detModelFilename, config.recModelFilename, config.clsModelFilename, config.useOpencl,
|
||||
config.cpuThreadNum, config.cpuPower);
|
||||
Log.i("OCRPredictorNative", "load success " + nativePointer);
|
||||
|
||||
}
|
||||
|
||||
|
||||
public ArrayList<OcrResultModel> runImage(Bitmap originalImage, int max_size_len, int run_det, int run_cls, int run_rec) {
|
||||
Log.i("OCRPredictorNative", "begin to run image ");
|
||||
float[] rawResults = forward(nativePointer, originalImage, max_size_len, run_det, run_cls, run_rec);
|
||||
ArrayList<OcrResultModel> results = postprocess(rawResults);
|
||||
return results;
|
||||
}
|
||||
|
||||
public static class Config {
|
||||
public int useOpencl;
|
||||
public int cpuThreadNum;
|
||||
public String cpuPower;
|
||||
public String detModelFilename;
|
||||
public String recModelFilename;
|
||||
public String clsModelFilename;
|
||||
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
if (nativePointer != 0) {
|
||||
release(nativePointer);
|
||||
nativePointer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
protected native long init(String detModelPath, String recModelPath, String clsModelPath, int useOpencl, int threadNum, String cpuMode);
|
||||
|
||||
protected native float[] forward(long pointer, Bitmap originalImage,int max_size_len, int run_det, int run_cls, int run_rec);
|
||||
|
||||
protected native void release(long pointer);
|
||||
|
||||
private ArrayList<OcrResultModel> postprocess(float[] raw) {
|
||||
ArrayList<OcrResultModel> results = new ArrayList<OcrResultModel>();
|
||||
int begin = 0;
|
||||
|
||||
while (begin < raw.length) {
|
||||
int point_num = Math.round(raw[begin]);
|
||||
int word_num = Math.round(raw[begin + 1]);
|
||||
OcrResultModel res = parse(raw, begin + 2, point_num, word_num);
|
||||
begin += 2 + 1 + point_num * 2 + word_num + 2;
|
||||
results.add(res);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
private OcrResultModel parse(float[] raw, int begin, int pointNum, int wordNum) {
|
||||
int current = begin;
|
||||
OcrResultModel res = new OcrResultModel();
|
||||
res.setConfidence(raw[current]);
|
||||
current++;
|
||||
for (int i = 0; i < pointNum; i++) {
|
||||
res.addPoints(Math.round(raw[current + i * 2]), Math.round(raw[current + i * 2 + 1]));
|
||||
}
|
||||
current += (pointNum * 2);
|
||||
for (int i = 0; i < wordNum; i++) {
|
||||
int index = Math.round(raw[current + i]);
|
||||
res.addWordIndex(index);
|
||||
}
|
||||
current += wordNum;
|
||||
res.setClsIdx(raw[current]);
|
||||
res.setClsConfidence(raw[current + 1]);
|
||||
Log.i("OCRPredictorNative", "word finished " + wordNum);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package com.xxpatx.os.ocr.paddle;
|
||||
|
||||
import android.graphics.Point;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class OcrResultModel {
|
||||
private List<Point> points;
|
||||
private List<Integer> wordIndex;
|
||||
private String label;
|
||||
private float confidence;
|
||||
private float cls_idx;
|
||||
private String cls_label;
|
||||
private float cls_confidence;
|
||||
|
||||
public OcrResultModel() {
|
||||
super();
|
||||
points = new ArrayList<>();
|
||||
wordIndex = new ArrayList<>();
|
||||
}
|
||||
|
||||
public void addPoints(int x, int y) {
|
||||
Point point = new Point(x, y);
|
||||
points.add(point);
|
||||
}
|
||||
|
||||
public void addWordIndex(int index) {
|
||||
wordIndex.add(index);
|
||||
}
|
||||
|
||||
public List<Point> getPoints() {
|
||||
return points;
|
||||
}
|
||||
|
||||
public List<Integer> getWordIndex() {
|
||||
return wordIndex;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public void setLabel(String label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public float getConfidence() {
|
||||
return confidence;
|
||||
}
|
||||
|
||||
public void setConfidence(float confidence) {
|
||||
this.confidence = confidence;
|
||||
}
|
||||
|
||||
public float getClsIdx() {
|
||||
return cls_idx;
|
||||
}
|
||||
|
||||
public void setClsIdx(float idx) {
|
||||
this.cls_idx = idx;
|
||||
}
|
||||
|
||||
public String getClsLabel() {
|
||||
return cls_label;
|
||||
}
|
||||
|
||||
public void setClsLabel(String label) {
|
||||
this.cls_label = label;
|
||||
}
|
||||
|
||||
public float getClsConfidence() {
|
||||
return cls_confidence;
|
||||
}
|
||||
|
||||
public void setClsConfidence(float confidence) {
|
||||
this.cls_confidence = confidence;
|
||||
}
|
||||
}
|
||||
283
app/src/main/java/com/xxpatx/os/ocr/paddle/Predictor.java
Normal file
283
app/src/main/java/com/xxpatx/os/ocr/paddle/Predictor.java
Normal file
@@ -0,0 +1,283 @@
|
||||
package com.xxpatx.os.ocr.paddle;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.Point;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Vector;
|
||||
|
||||
public class Predictor {
|
||||
private static final String TAG = Predictor.class.getSimpleName();
|
||||
public boolean isLoaded = false;
|
||||
public int warmupIterNum = 1;
|
||||
public int inferIterNum = 1;
|
||||
public int cpuThreadNum = 6;
|
||||
public String cpuPowerMode = "LITE_POWER_HIGH";
|
||||
public String modelPath = "";
|
||||
public String modelName = "";
|
||||
protected OCRPredictorNative paddlePredictor = null;
|
||||
protected float inferenceTime = 0;
|
||||
// Only for object detection
|
||||
protected Vector<String> wordLabels = new Vector<String>();
|
||||
protected int detLongSize = 960;
|
||||
protected float scoreThreshold = 0.1f;
|
||||
protected Bitmap inputImage = null;
|
||||
protected Bitmap outputImage = null;
|
||||
protected volatile String outputResult = "";
|
||||
protected float postprocessTime = 0;
|
||||
protected ArrayList<OcrResultModel> mOcrResultModels;
|
||||
|
||||
public Predictor() {
|
||||
}
|
||||
|
||||
public boolean init(Context appCtx, String modelPath, String labelPath, int useOpencl, int cpuThreadNum, String cpuPowerMode) {
|
||||
isLoaded = loadModel(appCtx, modelPath, useOpencl, cpuThreadNum, cpuPowerMode);
|
||||
if (!isLoaded) {
|
||||
return false;
|
||||
}
|
||||
isLoaded = loadLabel(appCtx, labelPath);
|
||||
return isLoaded;
|
||||
}
|
||||
|
||||
|
||||
public boolean init(Context appCtx, String modelPath, String labelPath, int useOpencl, int cpuThreadNum, String cpuPowerMode,
|
||||
int detLongSize, float scoreThreshold) {
|
||||
boolean isLoaded = init(appCtx, modelPath, labelPath, useOpencl, cpuThreadNum, cpuPowerMode);
|
||||
if (!isLoaded) {
|
||||
return false;
|
||||
}
|
||||
this.detLongSize = detLongSize;
|
||||
this.scoreThreshold = scoreThreshold;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean loadModel(Context appCtx, String modelPath, int useOpencl, int cpuThreadNum, String cpuPowerMode) {
|
||||
// Release model if exists
|
||||
releaseModel();
|
||||
|
||||
// Load model
|
||||
if (modelPath.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
String realPath = modelPath;
|
||||
if (!modelPath.substring(0, 1).equals("/")) {
|
||||
// Read model files from custom path if the first character of mode path is '/'
|
||||
// otherwise copy model to cache from assets
|
||||
realPath = appCtx.getCacheDir() + "/" + modelPath;
|
||||
Utils.copyDirectoryFromAssets(appCtx, modelPath, realPath);
|
||||
}
|
||||
if (realPath.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
OCRPredictorNative.Config config = new OCRPredictorNative.Config();
|
||||
config.useOpencl = useOpencl;
|
||||
config.cpuThreadNum = cpuThreadNum;
|
||||
config.cpuPower = cpuPowerMode;
|
||||
config.detModelFilename = realPath + File.separator + "det_db.nb";
|
||||
config.recModelFilename = realPath + File.separator + "rec_crnn.nb";
|
||||
config.clsModelFilename = realPath + File.separator + "cls.nb";
|
||||
Log.i("Predictor", "model path" + config.detModelFilename + " ; " + config.recModelFilename + ";" + config.clsModelFilename);
|
||||
paddlePredictor = new OCRPredictorNative(config);
|
||||
|
||||
this.cpuThreadNum = cpuThreadNum;
|
||||
this.cpuPowerMode = cpuPowerMode;
|
||||
this.modelPath = realPath;
|
||||
this.modelName = realPath.substring(realPath.lastIndexOf("/") + 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void releaseModel() {
|
||||
if (paddlePredictor != null) {
|
||||
paddlePredictor.destroy();
|
||||
paddlePredictor = null;
|
||||
}
|
||||
isLoaded = false;
|
||||
cpuThreadNum = 1;
|
||||
cpuPowerMode = "LITE_POWER_HIGH";
|
||||
modelPath = "";
|
||||
modelName = "";
|
||||
}
|
||||
|
||||
protected boolean loadLabel(Context appCtx, String labelPath) {
|
||||
wordLabels.clear();
|
||||
wordLabels.add("black");
|
||||
// Load word labels from file
|
||||
try {
|
||||
InputStream assetsInputStream = appCtx.getAssets().open(labelPath);
|
||||
int available = assetsInputStream.available();
|
||||
byte[] lines = new byte[available];
|
||||
assetsInputStream.read(lines);
|
||||
assetsInputStream.close();
|
||||
String words = new String(lines);
|
||||
String[] contents = words.split("\n");
|
||||
for (String content : contents) {
|
||||
wordLabels.add(content);
|
||||
}
|
||||
wordLabels.add(" ");
|
||||
Log.i(TAG, "Word label size: " + wordLabels.size());
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, e.getMessage());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public boolean runModel(int run_det, int run_cls, int run_rec) {
|
||||
if (inputImage == null || !isLoaded()) {
|
||||
return false;
|
||||
}
|
||||
Log.d(TAG, "runModel: " + inputImage.getWidth() + "x" + inputImage.getHeight());
|
||||
// Warm up
|
||||
for (int i = 0; i < warmupIterNum; i++) {
|
||||
paddlePredictor.runImage(inputImage, detLongSize, run_det, run_cls, run_rec);
|
||||
}
|
||||
warmupIterNum = 0; // do not need warm
|
||||
// Run inference
|
||||
Date start = new Date();
|
||||
ArrayList<OcrResultModel> results = paddlePredictor.runImage(inputImage, detLongSize, run_det, run_cls, run_rec);
|
||||
Date end = new Date();
|
||||
inferenceTime = (end.getTime() - start.getTime()) / (float) inferIterNum;
|
||||
|
||||
results = postprocess(results);
|
||||
mOcrResultModels = results;
|
||||
Log.i(TAG, "[stat] Inference Time: " + inferenceTime + " ;Box Size " + results.size());
|
||||
drawResults(results);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public ArrayList<OcrResultModel> getOcrResultModels() {
|
||||
return mOcrResultModels;
|
||||
}
|
||||
|
||||
public boolean isLoaded() {
|
||||
return paddlePredictor != null && isLoaded;
|
||||
}
|
||||
|
||||
public String modelPath() {
|
||||
return modelPath;
|
||||
}
|
||||
|
||||
public String modelName() {
|
||||
return modelName;
|
||||
}
|
||||
|
||||
public int cpuThreadNum() {
|
||||
return cpuThreadNum;
|
||||
}
|
||||
|
||||
public String cpuPowerMode() {
|
||||
return cpuPowerMode;
|
||||
}
|
||||
|
||||
public float inferenceTime() {
|
||||
return inferenceTime;
|
||||
}
|
||||
|
||||
public Bitmap inputImage() {
|
||||
return inputImage;
|
||||
}
|
||||
|
||||
public Bitmap outputImage() {
|
||||
return outputImage;
|
||||
}
|
||||
|
||||
public String outputResult() {
|
||||
return outputResult;
|
||||
}
|
||||
|
||||
public float postprocessTime() {
|
||||
return postprocessTime;
|
||||
}
|
||||
|
||||
|
||||
public void setInputImage(Bitmap image) {
|
||||
if (image == null) {
|
||||
return;
|
||||
}
|
||||
this.inputImage = image.copy(Bitmap.Config.ARGB_8888, true);
|
||||
}
|
||||
|
||||
private ArrayList<OcrResultModel> postprocess(ArrayList<OcrResultModel> results) {
|
||||
for (OcrResultModel r : results) {
|
||||
StringBuffer word = new StringBuffer();
|
||||
for (int index : r.getWordIndex()) {
|
||||
if (index >= 0 && index < wordLabels.size()) {
|
||||
word.append(wordLabels.get(index));
|
||||
} else {
|
||||
Log.e(TAG, "Word index is not in label list:" + index);
|
||||
word.append("×");
|
||||
}
|
||||
}
|
||||
r.setLabel(word.toString());
|
||||
r.setClsLabel(r.getClsIdx() == 1 ? "180" : "0");
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
private void drawResults(ArrayList<OcrResultModel> results) {
|
||||
StringBuffer outputResultSb = new StringBuffer("");
|
||||
for (int i = 0; i < results.size(); i++) {
|
||||
OcrResultModel result = results.get(i);
|
||||
StringBuilder sb = new StringBuilder("");
|
||||
if (result.getPoints().size() > 0) {
|
||||
sb.append("Det: ");
|
||||
for (Point p : result.getPoints()) {
|
||||
sb.append("(").append(p.x).append(",").append(p.y).append(") ");
|
||||
}
|
||||
}
|
||||
if (result.getLabel().length() > 0) {
|
||||
sb.append("\n Rec: ").append(result.getLabel());
|
||||
sb.append(",").append(result.getConfidence());
|
||||
}
|
||||
if (result.getClsIdx() != -1) {
|
||||
sb.append(" Cls: ").append(result.getClsLabel());
|
||||
sb.append(",").append(result.getClsConfidence());
|
||||
}
|
||||
Log.i(TAG, sb.toString()); // show LOG in Logcat panel
|
||||
outputResultSb.append(i + 1).append(": ").append(sb.toString()).append("\n");
|
||||
}
|
||||
outputResult = outputResultSb.toString();
|
||||
outputImage = inputImage;
|
||||
Canvas canvas = new Canvas(outputImage);
|
||||
Paint paintFillAlpha = new Paint();
|
||||
paintFillAlpha.setStyle(Paint.Style.FILL);
|
||||
paintFillAlpha.setColor(Color.parseColor("#3B85F5"));
|
||||
paintFillAlpha.setAlpha(50);
|
||||
|
||||
Paint paint = new Paint();
|
||||
paint.setColor(Color.parseColor("#3B85F5"));
|
||||
paint.setStrokeWidth(5);
|
||||
paint.setStyle(Paint.Style.STROKE);
|
||||
|
||||
for (OcrResultModel result : results) {
|
||||
Path path = new Path();
|
||||
List<Point> points = result.getPoints();
|
||||
if (points.size() == 0) {
|
||||
continue;
|
||||
}
|
||||
path.moveTo(points.get(0).x, points.get(0).y);
|
||||
for (int i = points.size() - 1; i >= 0; i--) {
|
||||
Point p = points.get(i);
|
||||
path.lineTo(p.x, p.y);
|
||||
}
|
||||
canvas.drawPath(path, paint);
|
||||
canvas.drawPath(path, paintFillAlpha);
|
||||
}
|
||||
Log.d(TAG, "drawResults: " + outputImage.getWidth() + "x" + outputImage.getHeight());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
166
app/src/main/java/com/xxpatx/os/ocr/paddle/Utils.java
Normal file
166
app/src/main/java/com/xxpatx/os/ocr/paddle/Utils.java
Normal file
@@ -0,0 +1,166 @@
|
||||
package com.xxpatx.os.ocr.paddle;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Matrix;
|
||||
import android.media.ExifInterface;
|
||||
import android.os.Environment;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public class Utils {
|
||||
private static final String TAG = Utils.class.getSimpleName();
|
||||
|
||||
public static void copyFileFromAssets(Context appCtx, String srcPath, String dstPath) {
|
||||
if (srcPath.isEmpty() || dstPath.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
InputStream is = null;
|
||||
OutputStream os = null;
|
||||
try {
|
||||
is = new BufferedInputStream(appCtx.getAssets().open(srcPath));
|
||||
os = new BufferedOutputStream(new FileOutputStream(new File(dstPath)));
|
||||
byte[] buffer = new byte[1024];
|
||||
int length = 0;
|
||||
while ((length = is.read(buffer)) != -1) {
|
||||
os.write(buffer, 0, length);
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
try {
|
||||
os.close();
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void copyDirectoryFromAssets(Context appCtx, String srcDir, String dstDir) {
|
||||
if (srcDir.isEmpty() || dstDir.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (!new File(dstDir).exists()) {
|
||||
new File(dstDir).mkdirs();
|
||||
}
|
||||
for (String fileName : appCtx.getAssets().list(srcDir)) {
|
||||
String srcSubPath = srcDir + File.separator + fileName;
|
||||
String dstSubPath = dstDir + File.separator + fileName;
|
||||
if (new File(srcSubPath).isDirectory()) {
|
||||
copyDirectoryFromAssets(appCtx, srcSubPath, dstSubPath);
|
||||
} else {
|
||||
copyFileFromAssets(appCtx, srcSubPath, dstSubPath);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static float[] parseFloatsFromString(String string, String delimiter) {
|
||||
String[] pieces = string.trim().toLowerCase().split(delimiter);
|
||||
float[] floats = new float[pieces.length];
|
||||
for (int i = 0; i < pieces.length; i++) {
|
||||
floats[i] = Float.parseFloat(pieces[i].trim());
|
||||
}
|
||||
return floats;
|
||||
}
|
||||
|
||||
public static long[] parseLongsFromString(String string, String delimiter) {
|
||||
String[] pieces = string.trim().toLowerCase().split(delimiter);
|
||||
long[] longs = new long[pieces.length];
|
||||
for (int i = 0; i < pieces.length; i++) {
|
||||
longs[i] = Long.parseLong(pieces[i].trim());
|
||||
}
|
||||
return longs;
|
||||
}
|
||||
|
||||
public static String getSDCardDirectory() {
|
||||
return Environment.getExternalStorageDirectory().getAbsolutePath();
|
||||
}
|
||||
|
||||
public static boolean isSupportedNPU() {
|
||||
return false;
|
||||
// String hardware = android.os.Build.HARDWARE;
|
||||
// return hardware.equalsIgnoreCase("kirin810") || hardware.equalsIgnoreCase("kirin990");
|
||||
}
|
||||
|
||||
public static Bitmap resizeWithStep(Bitmap bitmap, int maxLength, int step) {
|
||||
int width = bitmap.getWidth();
|
||||
int height = bitmap.getHeight();
|
||||
int maxWH = Math.max(width, height);
|
||||
float ratio = 1;
|
||||
int newWidth = width;
|
||||
int newHeight = height;
|
||||
if (maxWH > maxLength) {
|
||||
ratio = maxLength * 1.0f / maxWH;
|
||||
newWidth = (int) Math.floor(ratio * width);
|
||||
newHeight = (int) Math.floor(ratio * height);
|
||||
}
|
||||
|
||||
newWidth = newWidth - newWidth % step;
|
||||
if (newWidth == 0) {
|
||||
newWidth = step;
|
||||
}
|
||||
newHeight = newHeight - newHeight % step;
|
||||
if (newHeight == 0) {
|
||||
newHeight = step;
|
||||
}
|
||||
return Bitmap.createScaledBitmap(bitmap, newWidth, newHeight, true);
|
||||
}
|
||||
|
||||
public static Bitmap rotateBitmap(Bitmap bitmap, int orientation) {
|
||||
|
||||
Matrix matrix = new Matrix();
|
||||
switch (orientation) {
|
||||
case ExifInterface.ORIENTATION_NORMAL:
|
||||
return bitmap;
|
||||
case ExifInterface.ORIENTATION_FLIP_HORIZONTAL:
|
||||
matrix.setScale(-1, 1);
|
||||
break;
|
||||
case ExifInterface.ORIENTATION_ROTATE_180:
|
||||
matrix.setRotate(180);
|
||||
break;
|
||||
case ExifInterface.ORIENTATION_FLIP_VERTICAL:
|
||||
matrix.setRotate(180);
|
||||
matrix.postScale(-1, 1);
|
||||
break;
|
||||
case ExifInterface.ORIENTATION_TRANSPOSE:
|
||||
matrix.setRotate(90);
|
||||
matrix.postScale(-1, 1);
|
||||
break;
|
||||
case ExifInterface.ORIENTATION_ROTATE_90:
|
||||
matrix.setRotate(90);
|
||||
break;
|
||||
case ExifInterface.ORIENTATION_TRANSVERSE:
|
||||
matrix.setRotate(-90);
|
||||
matrix.postScale(-1, 1);
|
||||
break;
|
||||
case ExifInterface.ORIENTATION_ROTATE_270:
|
||||
matrix.setRotate(-90);
|
||||
break;
|
||||
default:
|
||||
return bitmap;
|
||||
}
|
||||
try {
|
||||
Bitmap bmRotated = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
|
||||
bitmap.recycle();
|
||||
return bmRotated;
|
||||
}
|
||||
catch (OutOfMemoryError e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,15 +2,29 @@ package com.xxpatx.os.service;
|
||||
|
||||
import android.accessibilityservice.AccessibilityService;
|
||||
import android.accessibilityservice.GestureDescription;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.hardware.display.DisplayManager;
|
||||
import android.hardware.display.VirtualDisplay;
|
||||
import android.media.Image;
|
||||
import android.media.ImageReader;
|
||||
import android.media.projection.MediaProjection;
|
||||
import android.media.projection.MediaProjectionManager;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.text.TextUtils;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
@@ -20,13 +34,35 @@ import android.view.accessibility.AccessibilityNodeInfo;
|
||||
import android.view.accessibility.AccessibilityWindowInfo;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.core.app.NotificationCompat;
|
||||
|
||||
import com.hjq.toast.Toaster;
|
||||
import com.tencent.mmkv.MMKV;
|
||||
import com.xxpatx.os.R;
|
||||
import com.xxpatx.os.activity.main.MainActivity;
|
||||
import com.xxpatx.os.bean.Contact;
|
||||
import com.xxpatx.os.config.CommonConfig;
|
||||
import com.xxpatx.os.ocr.paddle.OcrResultModel;
|
||||
import com.xxpatx.os.ocr.paddle.Predictor;
|
||||
import com.xxpatx.os.utils.ForegroundAppUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.annotations.NonNull;
|
||||
import io.reactivex.rxjava3.core.Observable;
|
||||
import io.reactivex.rxjava3.core.ObservableEmitter;
|
||||
import io.reactivex.rxjava3.core.ObservableOnSubscribe;
|
||||
import io.reactivex.rxjava3.core.Observer;
|
||||
import io.reactivex.rxjava3.disposables.Disposable;
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||
|
||||
import static android.app.Activity.RESULT_OK;
|
||||
|
||||
/**
|
||||
* 通过微信标签最高支持8.0.49,8.0.50 获取不到数据
|
||||
@@ -37,6 +73,29 @@ public class WeAccessibilityService extends AccessibilityService {
|
||||
|
||||
private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE);
|
||||
|
||||
private static final int NOTIFICATION_ID = 12345;
|
||||
private static final String CHANNEL_ID = "ScreenCaptureChannel";
|
||||
|
||||
private MediaProjectionManager mediaProjectionManager;
|
||||
private MediaProjection mediaProjection;
|
||||
private ImageReader imageReader;
|
||||
private VirtualDisplay virtualDisplay;
|
||||
private int screenWidth, screenHeight, screenDensity;
|
||||
|
||||
// Model settings of ocr
|
||||
protected String modelPath = "models/ch_PP-OCRv2";
|
||||
protected String labelPath = "labels/ppocr_keys_v1.txt";
|
||||
protected String imagePath = "";
|
||||
protected int cpuThreadNum = 6;
|
||||
protected String cpuPowerMode = "LITE_POWER_HIGH";
|
||||
protected int detLongSize = 960;
|
||||
protected float scoreThreshold = 0.1f;
|
||||
protected int useOpencl = 0;
|
||||
|
||||
protected Predictor predictor = new Predictor();
|
||||
|
||||
private String mCurrentPackageName = "";
|
||||
private String mCurrentClassName = "";
|
||||
|
||||
private static final String DIALER_TEXT = "音视频通话";
|
||||
private static final String CONTACT_TEXT = "通讯录";
|
||||
@@ -77,14 +136,69 @@ public class WeAccessibilityService extends AccessibilityService {
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onAccessibilityEvent(AccessibilityEvent event) {
|
||||
Log.v(TAG, "onAccessibilityEvent: event = " + event.toString());
|
||||
if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
|
||||
ComponentName componentName = new ComponentName(
|
||||
event.getPackageName().toString(),
|
||||
event.getClassName().toString()
|
||||
);
|
||||
mCurrentPackageName = componentName.getPackageName();
|
||||
mCurrentClassName = componentName.getClassName();
|
||||
}
|
||||
|
||||
if (finished) {
|
||||
finished = false;
|
||||
} else {
|
||||
Log.v(TAG, "bounce");
|
||||
handler.removeCallbacks(runnable);
|
||||
}
|
||||
input = event;
|
||||
handler.postDelayed(runnable, WAIT_TIME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
Log.e(TAG, "onCreate: ");
|
||||
registerSettingReceiver();
|
||||
registerStopReceiver();
|
||||
mAutoAccept = mMMKV.decodeBool(CommonConfig.WECHAT_CALL_AUTO_ACCEPT, false);
|
||||
handler = new Handler();
|
||||
|
||||
mediaProjectionManager = (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);
|
||||
|
||||
// 获取屏幕尺寸和密度
|
||||
WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
|
||||
DisplayMetrics metrics = new DisplayMetrics();
|
||||
windowManager.getDefaultDisplay().getMetrics(metrics);
|
||||
screenDensity = metrics.densityDpi;
|
||||
Log.e(TAG, "onCreate: screenDensity = " + screenDensity);
|
||||
|
||||
Point realSize = new Point();
|
||||
windowManager.getDefaultDisplay().getRealSize(realSize);
|
||||
|
||||
screenWidth = realSize.x;
|
||||
screenHeight = realSize.y;
|
||||
Log.e(TAG, "onCreate: screenWidth = " + screenWidth);
|
||||
Log.e(TAG, "onCreate: screenHeight = " + screenHeight);
|
||||
int widthPixels = metrics.widthPixels;
|
||||
int heightPixels = metrics.heightPixels;
|
||||
Log.e(TAG, "onCreate: widthPixels = " + widthPixels);
|
||||
Log.e(TAG, "onCreate: heightPixels = " + heightPixels);
|
||||
|
||||
// 创建通知渠道(针对 Android 8.0 及以上版本)
|
||||
createNotificationChannel();
|
||||
|
||||
// 启动前台服务
|
||||
startForeground(NOTIFICATION_ID, createNotification());
|
||||
|
||||
if (onLoadModel()) {
|
||||
Log.e(TAG, "onCreate: load model successful");
|
||||
} else {
|
||||
Log.e(TAG, "onCreate: load model failed");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -103,6 +217,13 @@ public class WeAccessibilityService extends AccessibilityService {
|
||||
}
|
||||
mCurrentStep = Step.CLICK_CONTACT;
|
||||
launchWeChat();
|
||||
|
||||
convertImage();
|
||||
|
||||
int resultCode = intent.getIntExtra("resultCode", RESULT_OK);
|
||||
Intent resultData = intent.getParcelableExtra("resultData");
|
||||
// 开始屏幕录制
|
||||
startScreenCapture(resultCode, resultData);
|
||||
}
|
||||
return super.onStartCommand(intent, flags, startId);
|
||||
}
|
||||
@@ -114,21 +235,539 @@ public class WeAccessibilityService extends AccessibilityService {
|
||||
if (mSettingBroadcastReceiver != null) {
|
||||
unregisterReceiver(mSettingBroadcastReceiver);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAccessibilityEvent(AccessibilityEvent event) {
|
||||
Log.v(TAG, "onAccessibilityEvent: event = " + event.toString());
|
||||
if (finished) {
|
||||
finished = false;
|
||||
} else {
|
||||
Log.v(TAG, "bounce");
|
||||
handler.removeCallbacks(runnable);
|
||||
if (mStopBroadcastReceiver != null) {
|
||||
unregisterReceiver(mStopBroadcastReceiver);
|
||||
}
|
||||
input = event;
|
||||
handler.postDelayed(runnable, WAIT_TIME);
|
||||
releaseDisplay();
|
||||
}
|
||||
|
||||
private void releaseDisplay() {
|
||||
Log.e(TAG, "releaseDisplay: ");
|
||||
// 释放资源
|
||||
if (virtualDisplay != null) {
|
||||
virtualDisplay.release();
|
||||
virtualDisplay = null;
|
||||
}
|
||||
|
||||
if (imageReader != null) {
|
||||
imageReader.close();
|
||||
imageReader = null;
|
||||
}
|
||||
|
||||
if (mediaProjection != null) {
|
||||
mediaProjection.stop();
|
||||
mediaProjection = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void startScreenCapture(int resultCode, Intent resultData) {
|
||||
// 创建 MediaProjection
|
||||
mediaProjection = mediaProjectionManager.getMediaProjection(resultCode, resultData);
|
||||
|
||||
// 创建 ImageReader 用于获取屏幕图像
|
||||
imageReader = ImageReader.newInstance(
|
||||
screenWidth,
|
||||
screenHeight,
|
||||
PixelFormat.RGBA_8888,
|
||||
3
|
||||
);
|
||||
|
||||
// 创建虚拟显示器
|
||||
virtualDisplay = mediaProjection.createVirtualDisplay(
|
||||
"ScreenCapture",
|
||||
screenWidth,
|
||||
screenHeight,
|
||||
screenDensity,
|
||||
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR | DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC,
|
||||
imageReader.getSurface(),
|
||||
null,
|
||||
new Handler()
|
||||
);
|
||||
|
||||
// 设置图像可用时的回调
|
||||
imageReader.setOnImageAvailableListener(reader -> {
|
||||
Log.e(TAG, "startScreenCapture: ");
|
||||
Image image = reader.acquireLatestImage();
|
||||
if (completed) {
|
||||
completed = false;
|
||||
mProcessImage.onImage(image);
|
||||
} else {
|
||||
Log.e(TAG, "startScreenCapture: Processing Data");
|
||||
if (image != null) {
|
||||
image.close();
|
||||
}
|
||||
}
|
||||
}, new Handler());
|
||||
}
|
||||
|
||||
private Bitmap convertImageToBitmap(Image image) {
|
||||
Image.Plane[] planes = image.getPlanes();
|
||||
int width = image.getWidth();
|
||||
int height = image.getHeight();
|
||||
|
||||
// 创建 Bitmap
|
||||
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
||||
|
||||
// 处理图像数据
|
||||
int pixelStride = planes[0].getPixelStride();
|
||||
int rowStride = planes[0].getRowStride();
|
||||
int rowPadding = rowStride - pixelStride * width;
|
||||
|
||||
// 复制图像数据到 Bitmap
|
||||
byte[] buffer = new byte[width * height * 4];
|
||||
java.nio.ByteBuffer byteBuffer = planes[0].getBuffer();
|
||||
byteBuffer.get(buffer);
|
||||
|
||||
// 调整图像数据
|
||||
int offset = 0;
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
int pixel = 0;
|
||||
pixel |= (buffer[offset + 3] & 0xff) << 24; // alpha
|
||||
pixel |= (buffer[offset + 0] & 0xff) << 16; // red
|
||||
pixel |= (buffer[offset + 1] & 0xff) << 8; // green
|
||||
pixel |= (buffer[offset + 2] & 0xff); // blue
|
||||
bitmap.setPixel(x, y, pixel);
|
||||
offset += pixelStride;
|
||||
}
|
||||
offset += rowPadding;
|
||||
}
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
private processImage mProcessImage;
|
||||
|
||||
public interface processImage {
|
||||
void onImage(Image image);
|
||||
}
|
||||
|
||||
private boolean completed = true;
|
||||
|
||||
private void convertImage() {
|
||||
Observable.create(new ObservableOnSubscribe<ArrayList<OcrResultModel>>() {
|
||||
@Override
|
||||
public void subscribe(@NonNull ObservableEmitter<ArrayList<OcrResultModel>> emitter) throws Throwable {
|
||||
mProcessImage = new processImage() {
|
||||
@Override
|
||||
public void onImage(Image image) {
|
||||
Log.e(TAG, "onImage: time = " + System.currentTimeMillis());
|
||||
// 获取最新的图像
|
||||
if (image != null) {
|
||||
Log.e(TAG, "startScreenCapture: image width = " + image.getWidth());
|
||||
Log.e(TAG, "startScreenCapture: image height = " + image.getHeight());
|
||||
Image.Plane[] planes = image.getPlanes();
|
||||
if (planes.length > 0) {
|
||||
// 将 Image 转换为 Bitmap
|
||||
Bitmap bitmap = convertImageToBitmap(image);
|
||||
// 处理获取到的屏幕图像
|
||||
if (bitmap != null) {
|
||||
Log.e(TAG, "startScreenCapture: bitmap width = " + bitmap.getWidth());
|
||||
Log.e(TAG, "startScreenCapture: bitmap height = " + bitmap.getHeight());
|
||||
predictor.setInputImage(bitmap);
|
||||
if (onRunModel()) {
|
||||
Bitmap outputImage = predictor.outputImage();
|
||||
Log.e(TAG, "startScreenCapture: outputImage width = " + outputImage.getWidth());
|
||||
Log.e(TAG, "startScreenCapture: outputImage height = " + outputImage.getHeight());
|
||||
ArrayList<OcrResultModel> ocrResultModels = predictor.getOcrResultModels();
|
||||
emitter.onNext(ocrResultModels);
|
||||
Log.e(TAG, "convertImage: Inference time: " + predictor.inferenceTime() + " ms");
|
||||
} else {
|
||||
Log.e(TAG, "convertImage: failed");
|
||||
completed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 释放资源
|
||||
image.close();
|
||||
} else {
|
||||
Log.e(TAG, "onImage: image is null");
|
||||
completed = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
})
|
||||
// .throttleFirst(800, TimeUnit.MILLISECONDS)
|
||||
.subscribeOn(Schedulers.computation())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(new Observer<ArrayList<OcrResultModel>>() {
|
||||
@Override
|
||||
public void onSubscribe(Disposable d) {
|
||||
Log.e("convertImage", "onSubscribe: ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(ArrayList<OcrResultModel> ocrResultModels) {
|
||||
Log.e(TAG, "onNext: ocrResultList size = " + ocrResultModels.size());
|
||||
Log.v(TAG, "onNext: " + ocrResultModels);
|
||||
analysisUi(ocrResultModels);
|
||||
completed = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable e) {
|
||||
Log.e("convertImage", "onError: " + e.getMessage());
|
||||
completed = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
Log.e("convertImage", "onComplete: ");
|
||||
completed = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private void analysisUi(ArrayList<OcrResultModel> ocrResultModels) {
|
||||
String topAppClassName = ForegroundAppUtil.getForegroundActivityName(WeAccessibilityService.this);
|
||||
Log.e(TAG, "analysisUi: topAppClassName = " + topAppClassName);
|
||||
Log.e(TAG, "analysisUi: mCurrentClassName = " + mCurrentClassName);
|
||||
Log.e(TAG, "analysisUi: mCurrentPackageName = " + mCurrentPackageName);
|
||||
if (!"com.tencent.mm".equals(mCurrentPackageName)) {
|
||||
completed = true; // 非微信应用,直接允许处理下一张
|
||||
return;
|
||||
}
|
||||
if (ocrResultModels == null || ocrResultModels.isEmpty()) {
|
||||
Log.e(TAG, "analysisUi: ocrResultModels empty");
|
||||
completed = true;
|
||||
return;
|
||||
}
|
||||
|
||||
List<String> stringList = ocrResultModels.stream().map(new Function<OcrResultModel, String>() {
|
||||
@Override
|
||||
public String apply(OcrResultModel ocrResultModel) {
|
||||
return ocrResultModel.getLabel();
|
||||
}
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
switch (topAppClassName) {
|
||||
case "com.tencent.mm.ui.LauncherUI"://主页或者聊天页面
|
||||
int weixinSize = countSubstringInListStream(stringList, "微信");
|
||||
long contactSize = stringList.stream().filter(new Predicate<String>() {
|
||||
@Override
|
||||
public boolean test(String s) {
|
||||
return "通讯录".equals(s);
|
||||
}
|
||||
}).count();
|
||||
//两个微信说明在微信页面
|
||||
if (weixinSize >= 2) {
|
||||
if (contactSize >= 2) {
|
||||
equalsViewTouch(ocrResultModels, topAppClassName, "标签");
|
||||
} else {
|
||||
equalsViewTouch(ocrResultModels, topAppClassName, "通讯录");
|
||||
}
|
||||
} else {
|
||||
if (stringList.contains("通讯录")) {//在通讯录页面
|
||||
if (contactSize >= 2) {
|
||||
equalsViewTouch(ocrResultModels, topAppClassName, "标签");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case "com.tencent.mm.plugin.label.ui.ContactLabelManagerUI"://标签页面
|
||||
if (stringList.contains("通讯录标签")) {//在标签里面
|
||||
containsViewTouch(ocrResultModels, topAppClassName, mTagName);
|
||||
}
|
||||
break;
|
||||
case "com.tencent.mm.ui.mvvm.MvvmContactListUI"://标签联系人列表
|
||||
if (stringList.contains(mName) && findString(stringList, mTagName)) {
|
||||
equalsViewTouch(ocrResultModels, topAppClassName, mName);
|
||||
} else {
|
||||
//滑动啥的
|
||||
scrollDown();
|
||||
}
|
||||
break;
|
||||
case "com.tencent.mm.plugin.profile.ui.ContactInfoUI"://个人信息页面
|
||||
if (findString(stringList, mName) && findString(stringList, DIALER_TEXT)) {
|
||||
if (findString(stringList, CALL_TEXT) && findString(stringList, VIDEO_TEXT)) {
|
||||
String callName;
|
||||
if (mCallType == TYPE_VIDEO) {
|
||||
callName = VIDEO_TEXT;
|
||||
} else {
|
||||
callName = CALL_TEXT;
|
||||
}
|
||||
boolean successful = containsViewTouch(ocrResultModels, topAppClassName, callName);
|
||||
Log.e(TAG, "analysisUi: successful = " + successful);
|
||||
} else {
|
||||
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
containsViewTouch(ocrResultModels, topAppClassName, DIALER_TEXT);
|
||||
}
|
||||
}, 2000);
|
||||
}
|
||||
} else if (findString(stringList, CALL_TEXT) || findString(stringList, VIDEO_TEXT)) {
|
||||
String callName;
|
||||
if (mCallType == TYPE_VIDEO) {
|
||||
callName = VIDEO_TEXT;
|
||||
} else {
|
||||
callName = CALL_TEXT;
|
||||
}
|
||||
boolean successful = containsViewTouch(ocrResultModels, topAppClassName, callName);
|
||||
Log.e(TAG, "analysisUi: successful = " + successful);
|
||||
// if (successful) {//有可能太快了点击不生效
|
||||
// Toaster.showLong("拨打完成");
|
||||
// releaseDisplay();
|
||||
// }
|
||||
}
|
||||
|
||||
break;
|
||||
case "com.tencent.mm.plugin.voip.ui.VideoActivity"://视频或语音通话
|
||||
Toaster.showLong("拨打完成");
|
||||
releaseDisplay();
|
||||
break;
|
||||
case "com.tencent.mm.plugin.brandservice.ui.flutter.BizFlutterTLFlutterViewActivity"://公众号
|
||||
case "com.tencent.mm.plugin.brandservice.ui.timeline.preload.ui.TmplWebViewMMUI"://文章
|
||||
case "com.tencent.mm.plugin.finder.ui.FinderShareFeedRelUI"://视频
|
||||
case "com.tencent.mm.plugin.subapp.ui.friend.FMessageConversationUI "://新的朋友
|
||||
case "com.tencent.mm.ui.contact.OnlyChatContactMgrUI"://仅聊天朋友
|
||||
case "com.tencent.mm.ui.contact.ChatroomContactUI"://群聊
|
||||
case "com.tencent.mm.plugin.brandservice.ui.BrandServiceIndexUI"://公众号列表 服务号
|
||||
launchWeChat();//重新打开微信
|
||||
break;
|
||||
case "com.tencent.mm.plugin.finder.feed.ui.FinderProfileUI":
|
||||
case "com.tencent.mm.plugin.sns.ui.SnsUserUI":
|
||||
case "com.tencent.mm.plugin.profile.ui.ContactMoreInfoUI":
|
||||
case "com.tencent.mm.plugin.profile.ui.PermissionSettingUI":
|
||||
performGlobalAction(GLOBAL_ACTION_BACK);
|
||||
break;
|
||||
case "com.tencent.mm.plugin.account.ui.WelcomeActivity":
|
||||
case "com.tencent.mm.plugin.account.ui.LoginPasswordUI":
|
||||
Toaster.showLong("请先登录微信");
|
||||
releaseDisplay();
|
||||
break;
|
||||
default:
|
||||
}
|
||||
|
||||
// if (stringList.contains("微信")) {//在主页
|
||||
//
|
||||
// } else if (stringList.contains("通讯录")) {//在通讯录页面
|
||||
// long contactSize = stringList.stream().filter(new Predicate<String>() {
|
||||
// @Override
|
||||
// public boolean test(String s) {
|
||||
// return "通讯录".equals(s);
|
||||
// }
|
||||
// }).count();
|
||||
// if (contactSize >= 2) {
|
||||
// equalsViewTouch(ocrResultModels, "标签");
|
||||
// }
|
||||
// } else if (stringList.contains("标签") && stringList.contains("通讯录")) {//在通讯录页面
|
||||
// equalsViewTouch(ocrResultModels, "标签");
|
||||
// } else if (stringList.contains("通讯录标签")) {//在标签里面
|
||||
// equalsViewTouch(ocrResultModels, mTagName);
|
||||
// } else if (stringList.contains(mName) && findString(stringList, DIALER_TEXT)) {
|
||||
// if ("com.tencent.mm.plugin.profile.ui.ContactInfoUI".equals(mCurrentClassName)) {//防止dialog弹出来 去点击音视频通话
|
||||
// containsViewTouch(ocrResultModels, DIALER_TEXT);
|
||||
// }
|
||||
// } else if (stringList.contains(mName)) {
|
||||
//// "com.tencent.mm.ui.mvvm.MvvmContactListUI"
|
||||
// if (!stringList.contains("微信") && !stringList.contains("通讯录")) {
|
||||
// equalsViewTouch(ocrResultModels, mName);
|
||||
// } else {
|
||||
// Log.e(TAG, "analysisUi: in home wechat fragment");
|
||||
// }
|
||||
// } else if (findString(stringList, DIALER_TEXT)) {
|
||||
// if ("com.tencent.mm.plugin.profile.ui.ContactInfoUI".equals(mCurrentClassName)) {//防止dialog弹出来 去点击音视频通话
|
||||
// containsViewTouch(ocrResultModels, DIALER_TEXT);
|
||||
// }
|
||||
// } else if (findString(stringList, VIDEO_TEXT) || findString(stringList, CALL_TEXT)) {
|
||||
// Log.e(TAG, "analysisUi: com.tencent.mm.ui.widget.dialog.w3");
|
||||
// String callName;
|
||||
// if (mCallType == TYPE_VIDEO) {
|
||||
// callName = VIDEO_TEXT;
|
||||
// } else {
|
||||
// callName = CALL_TEXT;
|
||||
// }
|
||||
// boolean s = equalsViewTouch(ocrResultModels, callName);
|
||||
// if (s) {
|
||||
// Toaster.showLong("拨打完成");
|
||||
// releaseDisplay();
|
||||
// }
|
||||
// } else {
|
||||
// completed = true;
|
||||
// }
|
||||
}
|
||||
|
||||
private boolean clickByPoint(int x, int y, String className, Runnable onComplete) {
|
||||
Log.e(TAG, "clickByNode: x = " + x);
|
||||
Log.e(TAG, "clickByNode: y = " + y);
|
||||
|
||||
Point point = new Point(x, y);
|
||||
Path path = new Path();
|
||||
path.moveTo(point.x, point.y);
|
||||
GestureDescription.Builder builder = new GestureDescription.Builder();
|
||||
builder.addStroke(new GestureDescription.StrokeDescription(path, 0, 200));
|
||||
GestureDescription gesture = builder.build();
|
||||
String topAppClassName = ForegroundAppUtil.getForegroundActivityName(WeAccessibilityService.this);
|
||||
if (topAppClassName.equals(className)) {
|
||||
boolean dispatched = dispatchGesture(gesture, new GestureResultCallback() {
|
||||
@Override
|
||||
public void onCompleted(GestureDescription gestureDescription) {
|
||||
super.onCompleted(gestureDescription);
|
||||
Log.e("clickByNode", "onCompleted: ");
|
||||
if (onComplete != null) {
|
||||
onComplete.run();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancelled(GestureDescription gestureDescription) {
|
||||
super.onCancelled(gestureDescription);
|
||||
Log.e("clickByNode", "onCompleted: ");
|
||||
if (onComplete != null) {
|
||||
onComplete.run();
|
||||
}
|
||||
}
|
||||
}, null);
|
||||
Log.e(TAG, "clickByPoint: dispatched = " + dispatched);
|
||||
return dispatched;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
private boolean findString(List<String> strings, String text) {
|
||||
boolean found = strings.stream()
|
||||
.anyMatch(s -> s.contains(text));
|
||||
return found;
|
||||
}
|
||||
|
||||
public static int countSubstringInListStream(List<String> list, String target) {
|
||||
return list.stream()
|
||||
.mapToInt(s -> {
|
||||
int count = 0;
|
||||
int index = 0;
|
||||
while ((index = s.indexOf(target, index)) != -1) {
|
||||
count++;
|
||||
index += target.length();
|
||||
}
|
||||
return count;
|
||||
})
|
||||
.sum(); // 对每个元素的统计结果求和[6](@ref)
|
||||
}
|
||||
|
||||
private boolean equalsViewTouch(ArrayList<OcrResultModel> ocrResultModels, String className, String text) {
|
||||
Optional<OcrResultModel> ocrResultOptional = ocrResultModels.stream().filter(new Predicate<OcrResultModel>() {
|
||||
@Override
|
||||
public boolean test(OcrResultModel ocrResultModel) {
|
||||
return text.equals(ocrResultModel.getLabel());
|
||||
}
|
||||
}).findAny();
|
||||
if (ocrResultOptional.isPresent()) {
|
||||
OcrResultModel ocrResult = ocrResultOptional.get();
|
||||
List<Point> points = ocrResult.getPoints();
|
||||
Log.e(TAG, "analysisUi: points = " + points);
|
||||
Point point = getCenterPoint(points);
|
||||
Log.e(TAG, "analysisUi: CenterPoint = " + point);
|
||||
return clickByPoint(point.x, point.y, className, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
completed = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean containsViewTouch(ArrayList<OcrResultModel> ocrResultModels, String className, String text) {
|
||||
Optional<OcrResultModel> ocrResultOptional = ocrResultModels.stream().filter(new Predicate<OcrResultModel>() {
|
||||
@Override
|
||||
public boolean test(OcrResultModel ocrResultModel) {
|
||||
return ocrResultModel.getLabel().contains(text);
|
||||
}
|
||||
}).findAny();
|
||||
if (ocrResultOptional.isPresent()) {
|
||||
OcrResultModel ocrResult = ocrResultOptional.get();
|
||||
List<Point> points = ocrResult.getPoints();
|
||||
Log.e(TAG, "analysisUi: points = " + points);
|
||||
Point point = getCenterPoint(points);
|
||||
Log.e(TAG, "analysisUi: CenterPoint = " + point);
|
||||
return clickByPoint(point.x, point.y, className, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
completed = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rec: 通讯录,0.9980758
|
||||
* 5: Det: (250,1291) (304,1291) (304,1335) (250,1335)
|
||||
*
|
||||
* @param points
|
||||
* @return
|
||||
*/
|
||||
private Point getCenterPoint(List<Point> points) {
|
||||
if (points.size() != 4) {
|
||||
Log.e(TAG, "getCenterPoint: ");
|
||||
} else {
|
||||
int x1 = points.get(0).x;
|
||||
int x2 = points.get(2).x;
|
||||
int y1 = points.get(0).y;
|
||||
int y2 = points.get(2).y;
|
||||
int x = (x1 + x2) / 2;
|
||||
int y = (y1 + y2) / 2;
|
||||
return new Point(x, y);
|
||||
}
|
||||
return new Point();
|
||||
}
|
||||
|
||||
private void processScreenImage(Bitmap bitmap) {
|
||||
// 在这里处理获取到的屏幕图像
|
||||
// 例如,可以发送广播或通过回调传递图像数据
|
||||
Log.d(TAG, "Screen image captured: " + bitmap.getWidth() + "x" + bitmap.getHeight());
|
||||
// 示例:保存图像到文件
|
||||
// saveBitmapToFile(bitmap);
|
||||
|
||||
}
|
||||
|
||||
private Notification createNotification() {
|
||||
Intent notificationIntent = new Intent(this, MainActivity.class);
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(
|
||||
this,
|
||||
0,
|
||||
notificationIntent,
|
||||
PendingIntent.FLAG_IMMUTABLE
|
||||
);
|
||||
|
||||
return new NotificationCompat.Builder(this, CHANNEL_ID)
|
||||
.setContentTitle("孝心平安通讯OS服务")
|
||||
.setContentText("正在运行中")
|
||||
.setSmallIcon(R.mipmap.ic_launcher)
|
||||
.setContentIntent(pendingIntent)
|
||||
.build();
|
||||
}
|
||||
|
||||
private void createNotificationChannel() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
NotificationChannel channel = new NotificationChannel(
|
||||
CHANNEL_ID,
|
||||
"孝心平安通讯OS服务",
|
||||
NotificationManager.IMPORTANCE_DEFAULT
|
||||
);
|
||||
NotificationManager manager = getSystemService(NotificationManager.class);
|
||||
manager.createNotificationChannel(channel);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public boolean onLoadModel() {
|
||||
if (predictor.isLoaded()) {
|
||||
predictor.releaseModel();
|
||||
}
|
||||
return predictor.init(getApplication(), modelPath, labelPath, useOpencl, cpuThreadNum,
|
||||
cpuPowerMode, detLongSize, scoreThreshold);
|
||||
}
|
||||
|
||||
public boolean onRunModel() {
|
||||
/*检测 分类 识别*/
|
||||
return predictor.isLoaded() && predictor.runModel(1, 0, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 1.在微信页面直接找到联系人拨打电话
|
||||
@@ -137,6 +776,7 @@ public class WeAccessibilityService extends AccessibilityService {
|
||||
*
|
||||
* @param event
|
||||
*/
|
||||
@Deprecated
|
||||
private void _onAccessibilityEvent(AccessibilityEvent event) {
|
||||
Log.e(TAG, "_onAccessibilityEvent: " + mCurrentStep);
|
||||
switch (mCurrentStep) {
|
||||
@@ -148,87 +788,95 @@ public class WeAccessibilityService extends AccessibilityService {
|
||||
break;
|
||||
default:
|
||||
if (!TextUtils.isEmpty(event.getClassName())) {
|
||||
if ("com.tencent.mm.ui.LauncherUI".contentEquals(event.getClassName())) {
|
||||
String className = event.getClassName().toString();
|
||||
Log.e(TAG, "_onAccessibilityEvent: className = " + className);
|
||||
if (className.startsWith("com.")) {
|
||||
// mCurrentClassName = className;
|
||||
}
|
||||
if ("com.tencent.mm.ui.LauncherUI".equals(className)) {
|
||||
if (mCurrentStep != Step.FIND_CONTACT) {
|
||||
mCurrentStep = Step.CLICK_CONTACT;
|
||||
}
|
||||
} else if ("com.tencent.mm.plugin.account.ui.WelcomeActivity".contentEquals(event.getClassName())) {
|
||||
mCurrentStep = Step.WAITING;
|
||||
Toaster.showLong("请先登录微信");
|
||||
} else if ("com.tencent.mm.plugin.label.ui.ContactLabelManagerUI".contentEquals(event.getClassName())) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
switch (mCurrentStep) {
|
||||
case WAITING:
|
||||
mAutoAccept = mMMKV.decodeBool(CommonConfig.WECHAT_CALL_AUTO_ACCEPT, false);
|
||||
Log.e(TAG, "_onAccessibilityEvent: mAutoAccept = " + mAutoAccept);
|
||||
if (!mAutoAccept) {
|
||||
return;
|
||||
}
|
||||
if (stepAnswer(Property.DESCRIPTION, RECEIVE_DESCRIPTION)) {
|
||||
mCurrentStep = Step.WECHAT_HANDS_FREE;
|
||||
Toast.makeText(this, "已自动接听视频/语音", Toast.LENGTH_LONG).show();
|
||||
} else {
|
||||
mCurrentStep = Step.WAITING;
|
||||
// clickAnswer();
|
||||
}
|
||||
break;
|
||||
case WECHAT_HANDS_FREE:
|
||||
handsFree(Property.DESCRIPTION, HANDS_FREE_TEXT);
|
||||
break;
|
||||
case DIALER_HANDS_FREE:
|
||||
if (findHandsFree(Property.DESCRIPTION, DIALER_HANDS_FREE_CLOSE_TEXT)) {
|
||||
dialerHandsFree(Property.TEXT, DIALER_HANDS_FREE_TEXT);
|
||||
} else {
|
||||
mCurrentStep = Step.WAITING;
|
||||
}
|
||||
break;
|
||||
case CLICK_HOME://主页能找到直接点击进去更多
|
||||
stepHome(Property.TEXT, mName);
|
||||
break;
|
||||
case CLICK_QUICK_WECHAT_CALL://点击更多页面
|
||||
step(Property.DESCRIPTION, MORE_NAME, Step.CLICK_TARGET);
|
||||
break;
|
||||
case CLICK_TARGET://点击视频通话
|
||||
stepCall(Property.TEXT, PARENT_VIDEO_TEXT);
|
||||
break;
|
||||
|
||||
case CLICK_CONTACT://进入通讯录界面
|
||||
if (stepHome(Property.TEXT, CONTACT_TEXT, Step.FIND_TAG)) {
|
||||
Log.e(TAG, "_onAccessibilityEvent: enter contact");
|
||||
} else {
|
||||
touchContact();
|
||||
}
|
||||
break;
|
||||
case FIND_CONTACT://模拟滑动找到联系人
|
||||
findContact(Property.TEXT, mName, Step.CLICK_NAME);
|
||||
break;
|
||||
case FIND_TAG:
|
||||
step(Property.TEXT, TAG_TEXT, Step.CLICK_TAG);
|
||||
break;
|
||||
case CLICK_TAG:
|
||||
step(Property.TEXT, mTagName, Step.CLICK_NAME);
|
||||
break;
|
||||
case CLICK_NAME://点击item
|
||||
step(Property.TEXT, mName, Step.CLICK_INFO);
|
||||
break;
|
||||
case CLICK_INFO://进入个人信息页面
|
||||
stepCallDialog(Property.TEXT, DIALER_TEXT, Step.CLICK_CALL);
|
||||
break;
|
||||
case CLICK_CALL://打视频或者电话
|
||||
if (mCallType == TYPE_VIDEO) {
|
||||
step(Property.TEXT, VIDEO_TEXT, Step.WAITING);
|
||||
} else if (mCallType == TYPE_VOICE) {
|
||||
step(Property.TEXT, CALL_TEXT, Step.WAITING);
|
||||
}
|
||||
break;
|
||||
// case CLICK_VIDEO_CALL:
|
||||
// if (step(Property.TEXT, VIDEO_TEXT)) {
|
||||
// Log.d(TAG, "finish, now: " + mCurrentStep);
|
||||
// Toast.makeText(this, "成功发起视频聊天", Toast.LENGTH_LONG).show();
|
||||
// switch (mCurrentStep) {
|
||||
// case WAITING:
|
||||
// mAutoAccept = mMMKV.decodeBool(CommonConfig.WECHAT_CALL_AUTO_ACCEPT, false);
|
||||
// Log.e(TAG, "_onAccessibilityEvent: mAutoAccept = " + mAutoAccept);
|
||||
// if (!mAutoAccept) {
|
||||
// return;
|
||||
// }
|
||||
// if (stepAnswer(Property.DESCRIPTION, RECEIVE_DESCRIPTION)) {
|
||||
// mCurrentStep = Step.WECHAT_HANDS_FREE;
|
||||
// Toast.makeText(this, "已自动接听视频/语音", Toast.LENGTH_LONG).show();
|
||||
// } else {
|
||||
// mCurrentStep = Step.WAITING;
|
||||
//// clickAnswer();
|
||||
// }
|
||||
// break;
|
||||
default:
|
||||
}
|
||||
// case WECHAT_HANDS_FREE:
|
||||
// handsFree(Property.DESCRIPTION, HANDS_FREE_TEXT);
|
||||
// break;
|
||||
// case DIALER_HANDS_FREE:
|
||||
// if (findHandsFree(Property.DESCRIPTION, DIALER_HANDS_FREE_CLOSE_TEXT)) {
|
||||
// dialerHandsFree(Property.TEXT, DIALER_HANDS_FREE_TEXT);
|
||||
// } else {
|
||||
// mCurrentStep = Step.WAITING;
|
||||
// }
|
||||
// break;
|
||||
// case CLICK_HOME://主页能找到直接点击进去更多
|
||||
// stepHome(Property.TEXT, mName);
|
||||
// break;
|
||||
// case CLICK_QUICK_WECHAT_CALL://点击更多页面
|
||||
// step(Property.DESCRIPTION, MORE_NAME, Step.CLICK_TARGET);
|
||||
// break;
|
||||
// case CLICK_TARGET://点击视频通话
|
||||
// stepCall(Property.TEXT, PARENT_VIDEO_TEXT);
|
||||
// break;
|
||||
//
|
||||
// case CLICK_CONTACT://进入通讯录界面
|
||||
// if (stepHome(Property.TEXT, CONTACT_TEXT, Step.FIND_TAG)) {
|
||||
// Log.e(TAG, "_onAccessibilityEvent: enter contact");
|
||||
// } else {
|
||||
// touchContact();
|
||||
// }
|
||||
// break;
|
||||
// case FIND_CONTACT://模拟滑动找到联系人
|
||||
// findContact(Property.TEXT, mName, Step.CLICK_NAME);
|
||||
// break;
|
||||
// case FIND_TAG:
|
||||
// step(Property.TEXT, TAG_TEXT, Step.CLICK_TAG);
|
||||
// break;
|
||||
// case CLICK_TAG:
|
||||
// step(Property.TEXT, mTagName, Step.CLICK_NAME);
|
||||
// break;
|
||||
// case CLICK_NAME://点击item
|
||||
// step(Property.TEXT, mName, Step.CLICK_INFO);
|
||||
// break;
|
||||
// case CLICK_INFO://进入个人信息页面
|
||||
// stepCallDialog(Property.TEXT, DIALER_TEXT, Step.CLICK_CALL);
|
||||
// break;
|
||||
// case CLICK_CALL://打视频或者电话
|
||||
// if (mCallType == TYPE_VIDEO) {
|
||||
// step(Property.TEXT, VIDEO_TEXT, Step.WAITING);
|
||||
// } else if (mCallType == TYPE_VOICE) {
|
||||
// step(Property.TEXT, CALL_TEXT, Step.WAITING);
|
||||
// }
|
||||
// break;
|
||||
//// case CLICK_VIDEO_CALL:
|
||||
//// if (step(Property.TEXT, VIDEO_TEXT)) {
|
||||
//// Log.d(TAG, "finish, now: " + mCurrentStep);
|
||||
//// Toast.makeText(this, "成功发起视频聊天", Toast.LENGTH_LONG).show();
|
||||
//// }
|
||||
//// break;
|
||||
// default:
|
||||
// }
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -738,5 +1386,30 @@ public class WeAccessibilityService extends AccessibilityService {
|
||||
}
|
||||
}
|
||||
|
||||
public static final String STOP_RECORD_ACTION = "stop_screen_record";
|
||||
|
||||
private void registerStopReceiver() {
|
||||
if (mStopBroadcastReceiver == null) {
|
||||
mStopBroadcastReceiver = new StopBroadcastReceiver();
|
||||
}
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(STOP_RECORD_ACTION);
|
||||
registerReceiver(mStopBroadcastReceiver, filter);
|
||||
}
|
||||
|
||||
private StopBroadcastReceiver mStopBroadcastReceiver;
|
||||
|
||||
private class StopBroadcastReceiver extends BroadcastReceiver {
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
Log.e("StopBroadcastReceiver", "onReceive: " + intent.getAction());
|
||||
if (STOP_RECORD_ACTION.equals(intent.getAction())) {
|
||||
mCurrentPackageName = "";
|
||||
mCurrentClassName = "";
|
||||
releaseDisplay();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ public class Utils {
|
||||
Log.e("e", "读取设备序列号异常:" + e.toString());
|
||||
}
|
||||
if (BuildConfig.DEBUG) {
|
||||
// return "TYZ185P5250100275";
|
||||
// return "PATX1202407AC0020";
|
||||
}
|
||||
return serial;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user