743 lines
28 KiB
Java
743 lines
28 KiB
Java
package com.xxpatx.os.service;
|
||
|
||
import android.accessibilityservice.AccessibilityService;
|
||
import android.accessibilityservice.GestureDescription;
|
||
import android.content.BroadcastReceiver;
|
||
import android.content.ComponentName;
|
||
import android.content.Context;
|
||
import android.content.Intent;
|
||
import android.content.IntentFilter;
|
||
import android.graphics.Path;
|
||
import android.graphics.Point;
|
||
import android.graphics.Rect;
|
||
import android.os.Handler;
|
||
import android.text.TextUtils;
|
||
import android.util.DisplayMetrics;
|
||
import android.util.Log;
|
||
import android.view.WindowManager;
|
||
import android.view.accessibility.AccessibilityEvent;
|
||
import android.view.accessibility.AccessibilityNodeInfo;
|
||
import android.view.accessibility.AccessibilityWindowInfo;
|
||
import android.widget.Toast;
|
||
|
||
import com.hjq.toast.Toaster;
|
||
import com.tencent.mmkv.MMKV;
|
||
import com.xxpatx.os.bean.Contact;
|
||
import com.xxpatx.os.config.CommonConfig;
|
||
import com.xxpatx.os.utils.ForegroundAppUtil;
|
||
|
||
import java.util.List;
|
||
|
||
/**
|
||
* 通过微信标签最高支持8.0.49,8.0.50 获取不到数据
|
||
* 通过 {@link android.accessibilityservice.AccessibilityService#getWindows}和修改accessibility-service 配置能遍历屏幕元素
|
||
*/
|
||
public class WeAccessibilityService extends AccessibilityService {
|
||
private static final String TAG = "WeAccessibilityService";
|
||
|
||
private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE);
|
||
|
||
|
||
private static final String DIALER_TEXT = "音视频通话";
|
||
private static final String CONTACT_TEXT = "通讯录";
|
||
private static final String TAG_TEXT = "标签";
|
||
private static final String TAG_NAME = "孝心通";
|
||
private static final String MORE_NAME = "更多功能按钮,已折叠";
|
||
private static final String PARENT_VIDEO_TEXT = "视频通话";
|
||
|
||
private static final String VIDEO_TEXT = "视频通话";
|
||
private static final String CALL_TEXT = "语音通话";
|
||
|
||
private static final String RECEIVE_DESCRIPTION = "接听";
|
||
private static final String HANDS_FREE_TEXT = "扬声器已关";
|
||
private static final String DIALER_HANDS_FREE_TEXT = "免提";
|
||
private static final String DIALER_HANDS_FREE_CLOSE_TEXT = "免提,已关闭";
|
||
|
||
public static final int TYPE_VOICE = 0;
|
||
public static final int TYPE_VIDEO = 1;
|
||
|
||
private static final int WAIT_TIME = 1000;
|
||
|
||
private int mCallType = TYPE_VOICE;
|
||
|
||
private Contact mContact;
|
||
private Step mCurrentStep = Step.WAITING;
|
||
private String mName = "";//微信昵称
|
||
private String mTagName = "";//微信联系人标签名
|
||
private boolean mAutoAccept = false;
|
||
private boolean finished = true;
|
||
|
||
private Handler handler = null;
|
||
private AccessibilityEvent input = null;
|
||
private Runnable runnable = new Runnable() {
|
||
@Override
|
||
public void run() {
|
||
_onAccessibilityEvent(input);
|
||
finished = true;
|
||
}
|
||
};
|
||
|
||
@Override
|
||
public void onCreate() {
|
||
super.onCreate();
|
||
Log.e(TAG, "onCreate: ");
|
||
registerSettingReceiver();
|
||
mAutoAccept = mMMKV.decodeBool(CommonConfig.WECHAT_CALL_AUTO_ACCEPT, false);
|
||
handler = new Handler();
|
||
|
||
}
|
||
|
||
@Override
|
||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||
Log.e(TAG, "onStartCommand: ");
|
||
if (intent != null) {
|
||
mContact = (Contact) intent.getSerializableExtra("WechatInfo");
|
||
Log.e(TAG, "onStartCommand: wechatInfo = " + mContact);
|
||
mCallType = intent.getIntExtra("call_type", TYPE_VOICE);
|
||
mName = mContact.getName();
|
||
String groupTag = mContact.getTag();
|
||
if (TextUtils.isEmpty(groupTag)) {
|
||
mTagName = TAG_NAME;
|
||
} else {
|
||
mTagName = groupTag;
|
||
}
|
||
mCurrentStep = Step.CLICK_CONTACT;
|
||
launchWeChat();
|
||
}
|
||
return super.onStartCommand(intent, flags, startId);
|
||
}
|
||
|
||
@Override
|
||
public void onDestroy() {
|
||
super.onDestroy();
|
||
Log.e(TAG, "onDestroy: ");
|
||
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);
|
||
}
|
||
input = event;
|
||
handler.postDelayed(runnable, WAIT_TIME);
|
||
}
|
||
|
||
|
||
/**
|
||
* 1.在微信页面直接找到联系人拨打电话
|
||
* 2.在联系人页面找到并拨打
|
||
* 3.通过进入联系人-标签找到并拨打
|
||
*
|
||
* @param event
|
||
*/
|
||
private void _onAccessibilityEvent(AccessibilityEvent event) {
|
||
Log.e(TAG, "_onAccessibilityEvent: " + mCurrentStep);
|
||
switch (mCurrentStep) {
|
||
case WAITING:
|
||
if (!TextUtils.isEmpty(event.getPackageName()) && "com.android.dialer".equals(event.getPackageName())) {
|
||
Log.e(TAG, "_onAccessibilityEvent: to dialer hands free");
|
||
mCurrentStep = Step.DIALER_HANDS_FREE;
|
||
}
|
||
break;
|
||
default:
|
||
if (!TextUtils.isEmpty(event.getClassName())) {
|
||
if ("com.tencent.mm.ui.LauncherUI".contentEquals(event.getClassName())) {
|
||
if (mCurrentStep != Step.FIND_CONTACT) {
|
||
mCurrentStep = Step.CLICK_CONTACT;
|
||
}
|
||
} 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();
|
||
// }
|
||
// break;
|
||
default:
|
||
}
|
||
}
|
||
|
||
@Override
|
||
public void onInterrupt() {
|
||
Log.e(TAG, "onInterrupt: ");
|
||
|
||
}
|
||
|
||
@Override
|
||
protected void onServiceConnected() {
|
||
super.onServiceConnected();
|
||
Log.e(TAG, "onServiceConnected: ");
|
||
}
|
||
|
||
private void launchWeChat() {
|
||
Intent intent = new Intent();
|
||
ComponentName cmp = new ComponentName("com.tencent.mm", "com.tencent.mm.ui.LauncherUI");
|
||
intent.setAction(Intent.ACTION_MAIN);
|
||
intent.addCategory(Intent.CATEGORY_LAUNCHER);
|
||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||
intent.addFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
|
||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||
intent.setComponent(cmp);
|
||
try {
|
||
startActivity(intent);
|
||
} catch (Exception e) {
|
||
Log.e(TAG, "launchWeChat: " + e.getMessage());
|
||
}
|
||
}
|
||
|
||
@Deprecated
|
||
private void clickAnswer() {
|
||
String className = ForegroundAppUtil.getForegroundActivityName(WeAccessibilityService.this);
|
||
Log.e(TAG, "clickAnswer: " + className);
|
||
if (!TextUtils.isEmpty(className)) {
|
||
if ("com.tencent.mm.plugin.voip.ui.VideoActivity".contentEquals(className)) {
|
||
boolean successful = clickByPoint(595, 1376);
|
||
Log.e(TAG, "clickAnswer: " + successful);
|
||
if (successful) {
|
||
Toast.makeText(this, "已自动接听视频/语音", Toast.LENGTH_LONG).show();
|
||
}
|
||
} else {
|
||
Log.e(TAG, "clickAnswer: Not in the answering interface");
|
||
}
|
||
}
|
||
}
|
||
|
||
private boolean step(Property type, String text, Step nextStep) {
|
||
AccessibilityNodeInfo node = findNode(getRootInActiveWindow(), type, text);
|
||
if (node != null) {
|
||
Rect rect = new Rect();
|
||
node.getBoundsInScreen(rect);
|
||
Log.e(TAG, "step: rect = " + rect);
|
||
if (rect.left < 0 || rect.top < 0 || rect.right < 0 || rect.bottom < 0) {
|
||
return false;
|
||
}
|
||
clickNode(node);
|
||
Log.e(TAG, "step: mCurrentStep: " + mCurrentStep + " done");
|
||
mCurrentStep = nextStep;
|
||
Log.e(TAG, "step: next: " + mCurrentStep);
|
||
return true;
|
||
} else {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
// TODO: 2025/2/8 先把通讯录点击的换成node
|
||
private boolean stepHome(Property type, String text, Step nextStep) {
|
||
AccessibilityNodeInfo node = findNode(getWindows(), type, text);
|
||
if (node != null) {
|
||
Rect rect = new Rect();
|
||
node.getBoundsInScreen(rect);
|
||
Log.e(TAG, "step: rect = " + rect);
|
||
if (rect.left < 0 || rect.top < 0 || rect.right < 0 || rect.bottom < 0) {
|
||
return false;
|
||
}
|
||
clickNode(node);
|
||
Log.e(TAG, "step: mCurrentStep: " + mCurrentStep + " done");
|
||
mCurrentStep = nextStep;
|
||
Log.e(TAG, "step: next: " + mCurrentStep);
|
||
return true;
|
||
} else {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
@Deprecated
|
||
private void touchContact() {
|
||
boolean successful = clickByPoint(268, 1440);
|
||
if (successful) {
|
||
mCurrentStep = Step.FIND_TAG;
|
||
} else {
|
||
mCurrentStep = Step.WAITING;
|
||
Toaster.show("点击失败,请重试");
|
||
}
|
||
}
|
||
|
||
private List<AccessibilityNodeInfo> findNodesByViewId(String id) {
|
||
List<AccessibilityNodeInfo> accessibilityNodeInfos = getRootInActiveWindow().findAccessibilityNodeInfosByViewId(id);
|
||
return accessibilityNodeInfos;
|
||
}
|
||
|
||
private AccessibilityNodeInfo findNodeByText(AccessibilityNodeInfo root, String text) {
|
||
if (root == null) return null;
|
||
Log.e(TAG, "findNodeByText: getText = " + root.getText());
|
||
Log.e(TAG, "findNodeByText: getContentDescription = " + root.getContentDescription());
|
||
boolean found = root.getText() != null && text.contentEquals(root.getText());
|
||
if (found) {
|
||
return root;
|
||
} else {
|
||
for (int i = 0; i < root.getChildCount(); i++) {
|
||
AccessibilityNodeInfo result = findNodeByText(root.getChild(i), text);
|
||
if (result != null) {
|
||
return result;
|
||
}
|
||
}
|
||
}
|
||
root.recycle();
|
||
return null;
|
||
}
|
||
|
||
private boolean stepCallDialog(Property type, String text, Step nextStep) {
|
||
AccessibilityNodeInfo node = findNode(getRootInActiveWindow(), type, text);
|
||
if (node != null) {
|
||
Log.e(TAG, "stepCallDialog: isVisibleToUser: " + node.isVisibleToUser());
|
||
if (node.isVisibleToUser()) {
|
||
clickNode(node);
|
||
Log.e(TAG, "stepCallDialog: mCurrentStep: " + mCurrentStep + " done");
|
||
mCurrentStep = nextStep;
|
||
Log.e(TAG, "stepCallDialog: next: " + mCurrentStep);
|
||
return true;
|
||
} else {
|
||
scrollDown();
|
||
return false;
|
||
}
|
||
} else {
|
||
if (mFindCount == mMaxCount) {
|
||
Log.e("stepCallDialog", "mCurrentStep: max");
|
||
Toast.makeText(this, "没有找到联系人", Toast.LENGTH_LONG).show();
|
||
mCurrentStep = Step.WAITING;
|
||
mFindCount = 0;
|
||
return false;
|
||
} else {
|
||
Log.e("stepCallDialog", "mCurrentStep: not found");
|
||
mFindCount++;
|
||
Log.e("stepCallDialog", "mCurrentStep: mFindCount = " + mFindCount);
|
||
scrollDown();
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
|
||
private boolean stepHome(Property type, String text) {
|
||
AccessibilityNodeInfo node = findNode(getRootInActiveWindow(), type, text);
|
||
if (node != null) {
|
||
clickNode(node);
|
||
Log.e(TAG, "stepHome: mCurrentStep: " + mCurrentStep + " done");
|
||
mCurrentStep = mCurrentStep.next();
|
||
Log.e(TAG, "stepHome: next: " + mCurrentStep);
|
||
return true;
|
||
} else {
|
||
mCurrentStep = Step.CLICK_CONTACT;
|
||
return false;
|
||
}
|
||
}
|
||
|
||
private int mFindCount = 0;
|
||
private int mMaxCount = 5;
|
||
|
||
|
||
private boolean findContact(Property type, String text, Step nextStep) {
|
||
AccessibilityNodeInfo node = findNode(getRootInActiveWindow(), type, text);
|
||
if (node != null) {
|
||
clickNode(node);
|
||
Log.e("findContact", "mCurrentStep: " + mCurrentStep + " done");
|
||
mCurrentStep = nextStep;
|
||
Log.e("findContact", "next: " + mCurrentStep);
|
||
mFindCount = 0;
|
||
return true;
|
||
} else {
|
||
if (mFindCount == mMaxCount) {
|
||
Log.e("findContact", "mCurrentStep: max");
|
||
Toast.makeText(this, "没有找到联系人", Toast.LENGTH_LONG).show();
|
||
mCurrentStep = Step.WAITING;
|
||
mFindCount = 0;
|
||
return false;
|
||
} else {
|
||
Log.e("findContact", "mCurrentStep: not found");
|
||
mFindCount++;
|
||
Log.e("findContact", "mCurrentStep: mFindCount = " + mFindCount);
|
||
scrollDown();
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
private AccessibilityNodeInfo findNode(AccessibilityNodeInfo root, Property type, String text) {
|
||
if (root == null) return null;
|
||
// Log.v(TAG, "findNode: getPackageName = " + root.getPackageName());
|
||
Log.v(TAG, "findNode: getText = " + root.getText());
|
||
Log.v(TAG, "findNode: getClassName = " + root.getClassName());
|
||
Log.v(TAG, "findNode: getContentDescription = " + root.getContentDescription());
|
||
boolean satisfied = false;
|
||
switch (type) {
|
||
case TEXT:
|
||
satisfied = root.getText() != null && text.contentEquals(root.getText());
|
||
break;
|
||
case CLASS_NAME:
|
||
satisfied = root.getClassName() != null && text.contentEquals(root.getClassName());
|
||
break;
|
||
case DESCRIPTION:
|
||
satisfied = root.getContentDescription() != null && text.contentEquals(root.getContentDescription());
|
||
break;
|
||
default:
|
||
}
|
||
if (satisfied) {
|
||
return root;
|
||
} else {
|
||
for (int i = 0; i < root.getChildCount(); i++) {
|
||
AccessibilityNodeInfo result = findNode(root.getChild(i), type, text);
|
||
if (result != null) {
|
||
return result;
|
||
}
|
||
}
|
||
}
|
||
root.recycle();
|
||
return null;
|
||
}
|
||
|
||
private AccessibilityNodeInfo findNode(List<AccessibilityWindowInfo> windows, Property type, String text) {
|
||
for (AccessibilityWindowInfo accessibilityWindowInfo : windows) {
|
||
AccessibilityNodeInfo nodeInfo = findNode(accessibilityWindowInfo.getRoot(), type, text);
|
||
if (nodeInfo != null) {
|
||
return nodeInfo;
|
||
}
|
||
}
|
||
Log.e(TAG, "findNode windows: not found");
|
||
return null;
|
||
}
|
||
|
||
|
||
private void clickNode(AccessibilityNodeInfo node) {
|
||
try {
|
||
Log.e(TAG, "clickNode: getText = " + node.getText());
|
||
Log.e(TAG, "clickNode: isClickable = " + node.isClickable());
|
||
} catch (Exception e) {
|
||
Log.e(TAG, "clickNode: e = " + e.getMessage());
|
||
}
|
||
if (node.isClickable()) {
|
||
boolean performAction = node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
|
||
Log.e(TAG, "clickNode: performAction = " + performAction);
|
||
if (!performAction) {
|
||
Rect rect = new Rect();
|
||
node.getBoundsInScreen(rect);
|
||
Log.e(TAG, "clickNode: rect = " + rect);
|
||
// 点击节点的中心位置
|
||
int centerX = (rect.left + rect.right) / 2;
|
||
int centerY = (rect.top + rect.bottom) / 2;
|
||
Log.e(TAG, "clickNode: clickByNode = " + clickByPoint(centerX, centerY));
|
||
}
|
||
node.recycle();
|
||
} else {
|
||
Rect rect = new Rect();
|
||
node.getBoundsInScreen(rect);
|
||
Log.e(TAG, "clickNode: rect = " + rect);
|
||
// 点击节点的中心位置
|
||
int centerX = (rect.left + rect.right) / 2;
|
||
int centerY = (rect.top + rect.bottom) / 2;
|
||
Log.e(TAG, "clickNode: clickByNode = " + clickByPoint(centerX, centerY));
|
||
}
|
||
// else {
|
||
// AccessibilityNodeInfo parent = node.getParent();
|
||
// node.recycle();
|
||
// clickNode(parent);
|
||
// }
|
||
}
|
||
|
||
private boolean stepCall(Property type, String text) {
|
||
AccessibilityNodeInfo node = findNode(getRootInActiveWindow(), type, text);
|
||
if (node != null) {
|
||
Point point = getPointtByNode(node);
|
||
Log.e(TAG, "stepCall: " + point);
|
||
clickByPoint(point.x, point.y + 30);
|
||
// clickNode(node);
|
||
Log.e(TAG, "stepCall: mCurrentStep " + mCurrentStep + " done");
|
||
mCurrentStep = Step.CLICK_CALL;
|
||
Log.e(TAG, "stepCall: next " + mCurrentStep);
|
||
return true;
|
||
} else {
|
||
Log.e(TAG, "stepCall: not found");
|
||
return false;
|
||
}
|
||
}
|
||
|
||
private boolean stepAnswer(Property type, String text) {
|
||
AccessibilityNodeInfo node = findNode(getWindows(), type, text);
|
||
if (node != null) {
|
||
Point point = getPointtByNode(node);
|
||
Log.e(TAG, "stepAnswer: " + point);
|
||
clickByPoint(point.x, point.y - 50);
|
||
clickByPoint(point.x, point.y);
|
||
// clickNode(node);
|
||
Log.e(TAG, "stepAnswer: mCurrentStep " + mCurrentStep + " done");
|
||
mCurrentStep = Step.WAITING;
|
||
Log.e(TAG, "stepAnswer: next " + mCurrentStep);
|
||
return true;
|
||
} else {
|
||
Log.e(TAG, "stepAnswer: not found");
|
||
return false;
|
||
}
|
||
}
|
||
|
||
private boolean dialerHandsFree(Property type, String text) {
|
||
AccessibilityNodeInfo node = findNode(getWindows(), type, text);
|
||
if (node != null) {
|
||
Rect rect = new Rect();
|
||
node.getBoundsInScreen(rect);
|
||
Log.e(TAG, "dialerHandsFree: rect = " + rect);
|
||
clickNode(node);
|
||
Log.e(TAG, "dialerHandsFree: mCurrentStep: " + mCurrentStep + " done");
|
||
mCurrentStep = Step.WAITING;
|
||
Log.e(TAG, "dialerHandsFree: next: " + mCurrentStep);
|
||
return true;
|
||
} else {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
private boolean findHandsFree(Property type, String text) {
|
||
AccessibilityNodeInfo node = findNode(getWindows(), type, text);
|
||
if (node != null) {
|
||
Log.e(TAG, "findHandsFree: true");
|
||
return true;
|
||
} else {
|
||
Log.e(TAG, "findHandsFree: false");
|
||
return false;
|
||
}
|
||
}
|
||
|
||
private boolean handsFree(Property type, String text) {
|
||
AccessibilityNodeInfo node = findNode(getWindows(), type, text);
|
||
if (node != null) {
|
||
Point point = getPointtByNode(node);
|
||
Log.e(TAG, "handsFree: " + point);
|
||
clickByPoint(point.x, point.y - 50);
|
||
clickByPoint(point.x, point.y);
|
||
// clickNode(node);
|
||
Log.e(TAG, "handsFree: mCurrentStep " + mCurrentStep + " done");
|
||
mCurrentStep = Step.WAITING;
|
||
Log.e(TAG, "handsFree: next " + mCurrentStep);
|
||
return true;
|
||
} else {
|
||
Log.e(TAG, "handsFree: not found");
|
||
mCurrentStep = Step.WAITING;
|
||
return false;
|
||
}
|
||
}
|
||
|
||
//根据节点信息可获得对应的x,y坐标
|
||
static Point getPointtByNode(AccessibilityNodeInfo node) {
|
||
if (node == null) {
|
||
return new Point(0, 0);
|
||
}
|
||
Rect rect = new Rect();
|
||
node.getBoundsInScreen(rect);
|
||
Point point = new Point(rect.centerX(), rect.centerY());
|
||
return point;
|
||
}
|
||
|
||
//实现对(x,y)坐标进行点击操作。
|
||
private boolean clickByPoint(int x, int y) {
|
||
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();
|
||
boolean dispatched = dispatchGesture(gesture, new GestureResultCallback() {
|
||
@Override
|
||
public void onCompleted(GestureDescription gestureDescription) {
|
||
super.onCompleted(gestureDescription);
|
||
Log.e("clickByNode", "onCompleted: ");
|
||
}
|
||
|
||
@Override
|
||
public void onCancelled(GestureDescription gestureDescription) {
|
||
super.onCancelled(gestureDescription);
|
||
Log.e("clickByNode", "onCompleted: ");
|
||
}
|
||
}, null);
|
||
return dispatched;
|
||
}
|
||
|
||
private boolean scrollScreen(double startY, double endY) {
|
||
WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
|
||
DisplayMetrics dm = new DisplayMetrics();
|
||
wm.getDefaultDisplay().getRealMetrics(dm);
|
||
int width = dm.widthPixels; // 屏幕宽度(像素)
|
||
int height = dm.heightPixels; // 屏幕高度(像素)
|
||
float density = dm.density; // 屏幕密度(0.75 / 1.0 / 1.5)
|
||
int densityDpi = dm.densityDpi; // 屏幕密度dpi(120 / 160 / 240)
|
||
// 屏幕宽度算法:屏幕宽度(像素)/屏幕密度
|
||
// int screenWidth = (int) (width / density); // 屏幕宽度(dp)
|
||
// int screenHeight = (int) (height / density);// 屏幕高度(dp)
|
||
Log.e(TAG, "scrollScreen: screenWidth = " + width);
|
||
Log.e(TAG, "scrollScreen: screenHeight = " + height);
|
||
int center_X = width / 2;
|
||
int center_Y = height / 2;
|
||
Log.e("scrollScreen", "center position:" + "(" + center_X + "," + center_Y + ")");
|
||
Path path = new Path();
|
||
path.moveTo(center_X, (int) (center_Y * startY)); //起点坐标。
|
||
path.lineTo(center_X, (int) (center_Y * endY)); //终点坐标。
|
||
GestureDescription.Builder builder = new GestureDescription.Builder();
|
||
GestureDescription gestureDescription = builder.addStroke(new GestureDescription.StrokeDescription(path, 0, 200)).build();
|
||
boolean dispatched = dispatchGesture(gestureDescription, new GestureResultCallback() {
|
||
@Override
|
||
public void onCompleted(GestureDescription gestureDescription) {
|
||
super.onCompleted(gestureDescription);
|
||
Log.d("scrollScreen", "dispatchGesture ScrollUp onCompleted.");
|
||
path.close();
|
||
}
|
||
|
||
@Override
|
||
public void onCancelled(GestureDescription gestureDescription) {
|
||
super.onCancelled(gestureDescription);
|
||
Log.d("scrollScreen", "dispatchGesture ScrollUp cancel.");
|
||
}
|
||
}, null);
|
||
return dispatched;
|
||
}
|
||
|
||
private boolean scrollDown() {
|
||
return scrollScreen(1.5, 0.5);
|
||
}
|
||
|
||
private boolean scrollUp() {
|
||
return scrollScreen(0.5, 1.5);
|
||
}
|
||
|
||
private enum Step {
|
||
WAITING,
|
||
WECHAT_HANDS_FREE,
|
||
DIALER_HANDS_FREE,
|
||
CLICK_HOME,
|
||
CLICK_QUICK_WECHAT_CALL,
|
||
CLICK_TARGET,
|
||
|
||
CLICK_CONTACT,
|
||
FIND_CONTACT,
|
||
CLICK_TAG,
|
||
FIND_TAG,
|
||
|
||
CLICK_NAME,
|
||
CLICK_INFO,
|
||
|
||
CLICK_CALL,
|
||
CLICK_VIDEO_CALL;
|
||
|
||
private Step next() {
|
||
return values()[(this.ordinal() + 1) % values().length];
|
||
}
|
||
}
|
||
|
||
private enum Property {
|
||
TEXT,
|
||
CLASS_NAME,
|
||
DESCRIPTION
|
||
}
|
||
|
||
public static final String SETTING_CALL_TYPE_ACTION = "setting_call_type_action";
|
||
public static final String SETTING_AUTOMATIC_ANSWER_ACTION = "setting_automatic_answer_action";
|
||
|
||
private SettingBroadcastReceiver mSettingBroadcastReceiver;
|
||
|
||
private void registerSettingReceiver() {
|
||
if (mSettingBroadcastReceiver == null) {
|
||
mSettingBroadcastReceiver = new SettingBroadcastReceiver();
|
||
}
|
||
IntentFilter filter = new IntentFilter();
|
||
filter.addAction(SETTING_CALL_TYPE_ACTION);
|
||
filter.addAction(SETTING_AUTOMATIC_ANSWER_ACTION);
|
||
registerReceiver(mSettingBroadcastReceiver, filter);
|
||
}
|
||
|
||
private class SettingBroadcastReceiver extends BroadcastReceiver {
|
||
@Override
|
||
public void onReceive(Context context, Intent intent) {
|
||
String action = intent.getAction();
|
||
Log.e("SettingBroadcastReceiver", "onReceive: " + action);
|
||
if (TextUtils.isEmpty(action)) return;
|
||
switch (action) {
|
||
case SETTING_CALL_TYPE_ACTION:
|
||
int callType = intent.getIntExtra("call_type", TYPE_VOICE);
|
||
mCallType = callType;
|
||
Log.e("SettingBroadcastReceiver", "onReceive: callType = " + callType);
|
||
break;
|
||
case SETTING_AUTOMATIC_ANSWER_ACTION:
|
||
boolean autoAnswer = intent.getBooleanExtra("auto_answer", false);
|
||
mAutoAccept = autoAnswer;
|
||
Log.e("SettingBroadcastReceiver", "onReceive: autoAnswer = " + autoAnswer);
|
||
break;
|
||
default:
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
}
|