version:1.6.0

fix:
update:使用ocr进行坐标点击
This commit is contained in:
2025-05-22 14:44:58 +08:00
parent a379ec9802
commit bbc5093b1a
361 changed files with 134816 additions and 117 deletions

View File

@@ -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) {

View File

@@ -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();
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View 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());
}
}

View 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;
}
}
}

View File

@@ -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.498.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();
}
}
}
}

View File

@@ -66,7 +66,7 @@ public class Utils {
Log.e("e", "读取设备序列号异常:" + e.toString());
}
if (BuildConfig.DEBUG) {
// return "TYZ185P5250100275";
// return "PATX1202407AC0020";
}
return serial;
}