package com.ttstd.dialer.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.Build; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.text.TextUtils; import android.util.DisplayMetrics; import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityWindowInfo; import com.blankj.utilcode.util.ToastUtils; import com.hjq.toast.Toaster; import com.tencent.mmkv.MMKV; import com.ttstd.dialer.config.CommonConfig; import com.ttstd.dialer.db.contact.ContactInfo; import com.ttstd.dialer.utils.Logger; import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.Random; public class DialerAccessibilityService extends AccessibilityService { private static final String TAG = "AccessibilityService"; private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE); public static final int ACTION_VIDEO = 1; public static final int ACTION_AUDIO = 2; private static final int ACTION_IME_ENTER_VERSION = 30; private static final int ACTION_IME_ENTER_ID = 16908372; private static final String DIALER_TEXT = "音视频通话"; private static final String CONTACT_TEXT = "通讯录"; private static final String SEARCH_TEXT = "搜索"; private static final String TAG_TEXT = "标签"; 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 = "免提,已关闭"; private static final int WAIT_TIME = 1600; private static final int DEFAULT_DELAY = 500; private static final int RANDOM_DELAY_RANGE = 300; private static final int MSG_PROCESS_STEP = 1001; private static final int MSG_DELAY_CLICK = 1002; private static final int MSG_RETRY_STEP = 1003; private Handler mHandler; private Step mCurrentStep = Step.WAITING; private String mName = ""; private String mTagName = ""; private boolean mAutoAccept = false; private boolean mAutoHandsFree = false; private ContactInfo mContactInfo; private int mCallType = ACTION_VIDEO; private int mFindCount = 0; private static final int MAX_FIND_COUNT = 5; private final Random mRandom = new Random(); public interface AccessibilityEventCallback { void onAccessibilityEventCallback(AccessibilityEvent accessibilityEvent); } private AccessibilityEventCallback mAccessibilityEventCallback; @Override public void onCreate() { super.onCreate(); Logger.e(TAG, "onCreate: "); initHandler(); registerSettingReceiver(); mAutoAccept = mMMKV.decodeBool(CommonConfig.WECHAT_AUTO_ACCEPT_CALL, false); mAutoHandsFree = mMMKV.decodeBool(CommonConfig.WECHAT_AUTO_HNADS_FREE, false); analysisAccessibilityEvent(); } private void initHandler() { mHandler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_PROCESS_STEP: processCurrentStep(); break; case MSG_DELAY_CLICK: ClickInfo clickInfo = (ClickInfo) msg.obj; performClick(clickInfo.node, clickInfo.simulate); if (clickInfo.nextStep != null) { mCurrentStep = clickInfo.nextStep; sendProcessStepMessage(DEFAULT_DELAY); } break; case MSG_RETRY_STEP: mFindCount++; if (mFindCount <= MAX_FIND_COUNT) { processCurrentStep(); } else { Logger.e(TAG, "Max retry count reached, reset to waiting"); mFindCount = 0; mCurrentStep = Step.WAITING; } break; } } }; } private void sendProcessStepMessage(long delay) { mHandler.removeMessages(MSG_PROCESS_STEP); mHandler.sendEmptyMessageDelayed(MSG_PROCESS_STEP, delay); } private void sendDelayClickMessage(AccessibilityNodeInfo node, boolean simulate, Step nextStep) { Message msg = mHandler.obtainMessage(MSG_DELAY_CLICK); msg.obj = new ClickInfo(node, simulate, nextStep); long delay = DEFAULT_DELAY + mRandom.nextInt(RANDOM_DELAY_RANGE); mHandler.sendMessageDelayed(msg, delay); } private void sendRetryStepMessage(long delay) { mHandler.removeMessages(MSG_RETRY_STEP); mHandler.sendEmptyMessageDelayed(MSG_RETRY_STEP, delay); } private void analysisAccessibilityEvent() { } @Override public int onStartCommand(Intent intent, int flags, int startId) { Logger.e(TAG, "onStartCommand: "); if (intent != null) { mContactInfo = (ContactInfo) intent.getSerializableExtra("ContactInfo"); Logger.e(TAG, "onStartCommand: contactInfo = " + mContactInfo); mCallType = intent.getIntExtra("call_type", ACTION_VIDEO); mName = mContactInfo != null ? mContactInfo.getName() : ""; mCurrentStep = Step.CLICK_HOME; if (mContactInfo != null) { startWeixin(); sendProcessStepMessage(WAIT_TIME); } } return super.onStartCommand(intent, flags, startId); } @Override public void onDestroy() { super.onDestroy(); Logger.e(TAG, "onDestroy: "); if (mHandler != null) { mHandler.removeCallbacksAndMessages(null); } if (mSettingReceiver != null) { unregisterReceiver(mSettingReceiver); } } @Override public void onAccessibilityEvent(AccessibilityEvent event) { Logger.v(TAG, "onAccessibilityEvent: event = " + event.toString()); checkClassName(event); if (mAccessibilityEventCallback != null) { mAccessibilityEventCallback.onAccessibilityEventCallback(event); } if (mCurrentStep != Step.WAITING) { sendProcessStepMessage(100); } } @Override public void onInterrupt() { } private void checkClassName(AccessibilityEvent event) { Logger.e(TAG, "checkClassName: mCurrentStep = " + mCurrentStep); if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) { String currentPackageName = event.getPackageName() != null ? event.getPackageName().toString() : ""; String currentClassName = event.getClassName() != null ? event.getClassName().toString() : ""; switch (mCurrentStep) { case WAITING: if (!TextUtils.isEmpty(currentPackageName) && "com.android.incallui".equals(currentPackageName)) { Logger.e(TAG, "checkClassName: to dialer hands free"); } break; default: if (!TextUtils.isEmpty(currentClassName)) { switch (currentClassName) { case "com.tencent.mm.ui.LauncherUI": break; case "com.tencent.mm.plugin.account.ui.WelcomeActivity": case "com.tencent.mm.plugin.account.ui.LoginPasswordUI": Toaster.showLong("请先登录微信"); mCurrentStep = Step.WAITING; break; case "com.tencent.mm.plugin.label.ui.ContactLabelManagerUI": break; default: } } } } } private void processCurrentStep() { Logger.e(TAG, "processCurrentStep: " + mCurrentStep); switch (mCurrentStep) { case WAITING: mAutoAccept = mMMKV.decodeBool(CommonConfig.WECHAT_AUTO_ACCEPT_CALL, false); Logger.e(TAG, "processCurrentStep: mAutoAccept = " + mAutoAccept); if (mAutoAccept) { autoAccept(); } break; case WECHAT_HANDS_FREE: mAutoHandsFree = mMMKV.decodeInt(CommonConfig.WECHAT_AUTO_HNADS_FREE, 0) == 1; Logger.e(TAG, "processCurrentStep: mAutoHandsFree = " + mAutoHandsFree); if (mAutoHandsFree) { handleHandsFree(Property.DESCRIPTION, HANDS_FREE_TEXT, true); } else { Logger.e(TAG, "processCurrentStep: not enable auto handsfree"); mCurrentStep = Step.WAITING; } break; case DIALER_HANDS_FREE: if (findNodeByProperty(Property.DESCRIPTION, DIALER_HANDS_FREE_CLOSE_TEXT, true) != null) { handleHandsFree(Property.TEXT, DIALER_HANDS_FREE_TEXT, false); } else { mCurrentStep = Step.WAITING; } break; case CLICK_HOME: if (findAndClick(Property.TEXT, mName, false, Step.CLICK_QUICK_WECHAT_CALL)) { Logger.e(TAG, "processCurrentStep: found contact in home"); } else { clickViewById("com.tencent.mm:id/jha", Step.CLICK_SEARCH); } break; case CLICK_SEARCH: putString(mName, Step.CLICK_SEARCH_CONTACT); break; case CLICK_SEARCH_CONTACT: if (findNodesByViewId("com.tencent.mm:id/gzf").size() > 0) { findSearchContactAndClick("com.tencent.mm:id/odf", Step.CLICK_QUICK_WECHAT_CALL); } else { Toaster.show("没有找到联系人"); mCurrentStep = Step.WAITING; } break; case CLICK_QUICK_WECHAT_CALL: clickViewById("com.tencent.mm:id/bjz", Step.CLICK_TARGET); break; case CLICK_TARGET: stepCall(Property.TEXT, PARENT_VIDEO_TEXT); break; case CLICK_CALL: if (mCallType == ACTION_AUDIO) { findAndClick(Property.TEXT, VIDEO_TEXT, false, Step.WAITING); } else if (mCallType == ACTION_VIDEO) { findAndClick(Property.TEXT, CALL_TEXT, false, Step.WAITING); } break; case CLICK_CONTACT: if (!findAndClick(Property.TEXT, CONTACT_TEXT, false, Step.FIND_TAG)) { touchContact(); } break; case FIND_CONTACT: findContactWithScroll(Property.TEXT, mName, Step.CLICK_QUICK_WECHAT_CALL); break; case FIND_TAG: findAndClick(Property.TEXT, TAG_TEXT, false, Step.CLICK_TAG); break; case CLICK_TAG: if (!findAndClick(Property.TEXT, mTagName, false, Step.CLICK_NAME)) { Toaster.show("没有找到标签"); mCurrentStep = Step.WAITING; } break; case CLICK_NAME: findContactWithScroll(Property.TEXT, mName, Step.CLICK_INFO); break; case CLICK_INFO: findAndClickWithScroll(Property.TEXT, DIALER_TEXT, Step.CLICK_CALL); break; default: } } private boolean findAndClick(Property type, String text, boolean searchWindows, Step nextStep) { AccessibilityNodeInfo node = findNodeByProperty(type, text, searchWindows); if (node != null && isNodeVisible(node)) { sendDelayClickMessage(node, false, nextStep); return true; } return false; } private void findAndClickWithScroll(Property type, String text, Step nextStep) { AccessibilityNodeInfo node = findNodeByProperty(type, text, false); if (node != null) { if (node.isVisibleToUser()) { sendDelayClickMessage(node, false, nextStep); mFindCount = 0; } else { scrollDown(); sendRetryStepMessage(WAIT_TIME); } } else { handleNotFoundWithRetry("没有找到联系人", nextStep); } } private void findContactWithScroll(Property type, String text, Step nextStep) { AccessibilityNodeInfo node = findNodeByProperty(type, text, false); if (node != null) { sendDelayClickMessage(node, false, nextStep); mFindCount = 0; } else { handleNotFoundWithRetry("没有找到联系人", nextStep); } } private void handleNotFoundWithRetry(String errorMsg, Step nextStep) { if (mFindCount >= MAX_FIND_COUNT) { Logger.e(TAG, "handleNotFoundWithRetry: max count reached"); ToastUtils.showShort(errorMsg); mCurrentStep = Step.WAITING; mFindCount = 0; } else { Logger.e(TAG, "handleNotFoundWithRetry: not found, count=" + mFindCount); mFindCount++; scrollDown(); sendRetryStepMessage(WAIT_TIME); } } private void handleHandsFree(Property type, String text, boolean isWechat) { AccessibilityNodeInfo node = findNodeByProperty(type, text, true); if (node != null) { Point point = getPointByNode(node); Logger.e(TAG, "handleHandsFree: " + point); if (isWechat) { clickByPoint(point.x, point.y - 50); clickByPoint(point.x, point.y); } else { sendDelayClickMessage(node, false, Step.WAITING); } mCurrentStep = Step.WAITING; } else { Logger.e(TAG, "handleHandsFree: not found"); mCurrentStep = Step.WAITING; } } private void autoAccept() { if (findAndClickAnswer(Property.DESCRIPTION, RECEIVE_DESCRIPTION)) { mCurrentStep = Step.WECHAT_HANDS_FREE; ToastUtils.showShort("已自动接听视频/语音"); } else if (clickNode("com.tencent.mm:id/kfp", false)) { mCurrentStep = Step.WECHAT_HANDS_FREE; ToastUtils.showShort("已自动接听视频/语音"); } else { mCurrentStep = Step.WAITING; } } private boolean findAndClickAnswer(Property type, String text) { AccessibilityNodeInfo node = findNodeByProperty(type, text, true); if (node != null) { Point point = getPointByNode(node); Logger.e(TAG, "findAndClickAnswer: " + point); clickByPoint(point.x, point.y - 50); clickByPoint(point.x, point.y); mCurrentStep = Step.WAITING; return true; } return false; } private boolean clickNode(String id, boolean simulate) { findFloatWindowNode(id); List nodeInfos = findNodesByViewId(id); Optional optional = nodeInfos.stream().findAny(); if (optional.isPresent()) { AccessibilityNodeInfo node = optional.get(); if (node.isClickable()) { boolean performAction = node.performAction(AccessibilityNodeInfo.ACTION_CLICK); Logger.e(TAG, "clickNode: performAction = " + performAction); node.recycle(); return performAction; } else { if (simulate) { Point point = getPointByNode(node); Logger.e(TAG, "clickNode: " + point); clickByPoint(point.x, point.y); Logger.e(TAG, "clickNode: mCurrentStep " + mCurrentStep + " done"); } else { AccessibilityNodeInfo clickableNode = findClickableNode(node); if (clickableNode != null) { sendDelayClickMessage(clickableNode, false, null); } } } return true; } else { Logger.e(TAG, "clickNode: not found"); return false; } } private void performClick(AccessibilityNodeInfo node, boolean simulate) { if (node == null) { Logger.e(TAG, "performClick: node is null"); return; } try { Logger.e(TAG, "performClick: getText = " + node.getText()); Logger.e(TAG, "performClick: isClickable = " + node.isClickable()); } catch (Exception e) { Logger.e(TAG, "performClick: e = " + e.getMessage()); } if (node.isClickable()) { boolean performAction = node.performAction(AccessibilityNodeInfo.ACTION_CLICK); Logger.e(TAG, "performClick: performAction = " + performAction); if (!performAction) { Rect rect = new Rect(); node.getBoundsInScreen(rect); Logger.e(TAG, "performClick: rect = " + rect); int centerX = (rect.left + rect.right) / 2; int centerY = (rect.top + rect.bottom) / 2; Logger.e(TAG, "performClick: clickByNode = " + clickByPoint(centerX, centerY)); } node.recycle(); } else { Rect rect = new Rect(); node.getBoundsInScreen(rect); Logger.e(TAG, "performClick: rect = " + rect); int centerX = (rect.left + rect.right) / 2; int centerY = (rect.top + rect.bottom) / 2; Logger.e(TAG, "performClick: clickByNode = " + clickByPoint(centerX, centerY)); } } private AccessibilityNodeInfo findClickableNode(AccessibilityNodeInfo node) { if (node == null) { Logger.e(TAG, "findClickableNode: node is null"); return null; } if (node.isClickable()) { return node; } else { AccessibilityNodeInfo parent = node.getParent(); node.recycle(); return parent != null ? findClickableNode(parent) : null; } } public void findFloatWindowNode(String id) { List windows = getWindows(); for (AccessibilityWindowInfo window : windows) { if (isFloatingWindow(window)) { AccessibilityNodeInfo rootNode = window.getRoot(); traverseNode(rootNode); if (rootNode != null) { rootNode.recycle(); } } } } private boolean isFloatingWindow(AccessibilityWindowInfo window) { Logger.e(TAG, "isFloatingWindow: " + window.getType()); return window.getType() == AccessibilityWindowInfo.TYPE_ACCESSIBILITY_OVERLAY; } private void traverseNode(AccessibilityNodeInfo node) { if (node == null) return; String text = node.getText() != null ? node.getText().toString() : ""; String id = node.getViewIdResourceName(); for (int i = 0; i < node.getChildCount(); i++) { traverseNode(node.getChild(i)); } } private AccessibilityNodeInfo findNodeByProperty(Property type, String text, boolean searchWindows) { if (searchWindows) { return findNodeInWindows(type, text); } else { return findNodeInActiveWindow(getRootInActiveWindow(), type, text); } } private AccessibilityNodeInfo findNodeInWindows(Property type, String text) { for (AccessibilityWindowInfo window : getWindows()) { AccessibilityNodeInfo root = window.getRoot(); AccessibilityNodeInfo node = findNodeInActiveWindow(root, type, text); if (node != null) { return node; } if (root != null) { root.recycle(); } } Logger.e(TAG, "findNodeInWindows: not found"); return null; } private AccessibilityNodeInfo findNodeInActiveWindow(AccessibilityNodeInfo root, Property type, String text) { if (root == null) return null; Logger.v(TAG, "findNodeInActiveWindow: getText = " + root.getText()); Logger.v(TAG, "findNodeInActiveWindow: getClassName = " + root.getClassName()); Logger.v(TAG, "findNodeInActiveWindow: getContentDescription = " + root.getContentDescription()); boolean satisfied = checkPropertyMatch(root, type, text); if (satisfied) { return root; } else { for (int i = 0; i < root.getChildCount(); i++) { AccessibilityNodeInfo result = findNodeInActiveWindow(root.getChild(i), type, text); if (result != null) { return result; } } } root.recycle(); return null; } private boolean checkPropertyMatch(AccessibilityNodeInfo node, Property type, String text) { switch (type) { case TEXT: return node.getText() != null && text.contentEquals(node.getText()); case CLASS_NAME: return node.getClassName() != null && text.contentEquals(node.getClassName()); case DESCRIPTION: return node.getContentDescription() != null && text.contentEquals(node.getContentDescription()); default: return false; } } private boolean isNodeVisible(AccessibilityNodeInfo node) { if (node == null) return false; Rect rect = new Rect(); node.getBoundsInScreen(rect); return rect.left >= 0 && rect.top >= 0 && rect.right >= 0 && rect.bottom >= 0; } private void clickViewById(String id, Step nextStep) { List nodeInfos = findNodesByViewId(id); Optional optional = nodeInfos.stream().findAny(); if (optional.isPresent()) { sendDelayClickMessage(optional.get(), false, nextStep); } else { findAndClick(Property.DESCRIPTION, SEARCH_TEXT, false, Step.CLICK_SEARCH); } } private List findNodesByViewId(String id) { AccessibilityNodeInfo nodeInfo = getRootInActiveWindow(); if (nodeInfo != null) { return nodeInfo.findAccessibilityNodeInfosByViewId(id); } else { return new ArrayList<>(); } } private void putString(String text, Step nextStep) { List nodeInfos = findNodesByViewId("com.tencent.mm:id/d98"); Optional optional = nodeInfos.stream().findAny(); if (optional.isPresent()) { AccessibilityNodeInfo nodeInfo = optional.get(); Bundle args = new Bundle(); args.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, text); nodeInfo.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, args); nodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLEAR_FOCUS); if (Build.VERSION.SDK_INT >= ACTION_IME_ENTER_VERSION) { nodeInfo.performAction(ACTION_IME_ENTER_ID); } mCurrentStep = nextStep; sendProcessStepMessage(WAIT_TIME); } else { Toaster.show("没有找到搜索框"); mCurrentStep = Step.WAITING; } } private void findSearchContactAndClick(String id, Step nextStep) { List nodeInfos = findNodesByViewId(id); Logger.e(TAG, "findSearchContactAndClick: " + nodeInfos); Optional optional = nodeInfos.stream().findAny(); if (optional.isPresent()) { sendDelayClickMessage(optional.get(), false, nextStep); } else { Toaster.show("没有找到联系人"); mCurrentStep = Step.WAITING; } } private boolean stepCall(Property type, String text) { AccessibilityNodeInfo node = findNodeByProperty(type, text, false); if (node != null) { Point point = getPointByNode(node); Logger.e(TAG, "stepCall: " + point); clickByPoint(point.x, point.y); Logger.e(TAG, "stepCall: mCurrentStep " + mCurrentStep + " done"); mCurrentStep = Step.CLICK_CALL; Logger.e(TAG, "stepCall: next " + mCurrentStep); sendProcessStepMessage(WAIT_TIME); return true; } else { Logger.e(TAG, "stepCall: not found"); return false; } } private void touchContact() { boolean successful = clickByPoint(268, 1440); if (successful) { mCurrentStep = Step.FIND_TAG; sendProcessStepMessage(WAIT_TIME); } else { mCurrentStep = Step.WAITING; Toaster.show("点击失败,请重试"); } } static Point getPointByNode(AccessibilityNodeInfo node) { if (node == null) { return new Point(0, 0); } Rect rect = new Rect(); node.getBoundsInScreen(rect); return new Point(rect.centerX(), rect.centerY()); } private boolean clickByPoint(int x, int y) { Logger.e(TAG, "clickByPoint: x = " + x); Logger.e(TAG, "clickByPoint: y = " + y); Point point = new Point(x, y); Path path = new Path(); path.moveTo(point.x, point.y); GestureDescription.Builder builder = new GestureDescription.Builder(); int duration = 200 + mRandom.nextInt(100); builder.addStroke(new GestureDescription.StrokeDescription(path, 0, duration)); GestureDescription gesture = builder.build(); boolean dispatched = dispatchGesture(gesture, new GestureResultCallback() { @Override public void onCompleted(GestureDescription gestureDescription) { super.onCompleted(gestureDescription); Logger.e("clickByPoint", "onCompleted: "); } @Override public void onCancelled(GestureDescription gestureDescription) { super.onCancelled(gestureDescription); Logger.e("clickByPoint", "onCancelled: "); } }, 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; Logger.e(TAG, "scrollScreen: screenWidth = " + width); Logger.e(TAG, "scrollScreen: screenHeight = " + height); int center_X = width / 2; int center_Y = height / 2; Logger.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); Logger.d("scrollScreen", "dispatchGesture ScrollUp onCompleted."); path.close(); } @Override public void onCancelled(GestureDescription gestureDescription) { super.onCancelled(gestureDescription); Logger.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_SEARCH, CLICK_SEARCH_CONTACT, CLICK_QUICK_WECHAT_CALL, CLICK_TARGET, CLICK_CALL, CLICK_CONTACT, FIND_CONTACT, FIND_TAG, CLICK_TAG, CLICK_NAME, CLICK_INFO, CLICK_VIDEO_CALL } private enum Property { TEXT, CLASS_NAME, DESCRIPTION } private static class ClickInfo { AccessibilityNodeInfo node; boolean simulate; Step nextStep; ClickInfo(AccessibilityNodeInfo node, boolean simulate, Step nextStep) { this.node = node; this.simulate = simulate; this.nextStep = nextStep; } } 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 SettingReceiver mSettingReceiver; private void registerSettingReceiver() { if (mSettingReceiver == null) { mSettingReceiver = new SettingReceiver(); } IntentFilter filter = new IntentFilter(); filter.addAction(SETTING_CALL_TYPE_ACTION); filter.addAction(SETTING_AUTOMATIC_ANSWER_ACTION); registerReceiver(mSettingReceiver, filter); } private class SettingReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); Logger.e("SettingReceiver", "onReceive: " + action); if (TextUtils.isEmpty(action)) return; switch (action) { case SETTING_CALL_TYPE_ACTION: int callType = intent.getIntExtra("call_type", ACTION_VIDEO); mCallType = callType; Logger.e("SettingReceiver", "onReceive: callType = " + callType); break; case SETTING_AUTOMATIC_ANSWER_ACTION: boolean autoAnswer = intent.getBooleanExtra("auto_answer", false); mAutoAccept = autoAnswer; Logger.e("SettingReceiver", "onReceive: autoAnswer = " + autoAnswer); break; default: } } } private void startWeixin() { 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) { Logger.e(TAG, "startWeixin: " + e.getMessage()); } } }