version:1.6.3

fix:
update:改回无障碍接听
This commit is contained in:
2025-06-03 16:30:33 +08:00
parent 4482aedea6
commit 6457d40df8
361 changed files with 906 additions and 134994 deletions

View File

@@ -0,0 +1,760 @@
package com.google.android.accessibility.selecttospeak;
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.498.0.50 获取不到数据
* 通过 {@link AccessibilityService#getWindows}和修改accessibility-service 配置能遍历屏幕元素
*/
public class SelectToSpeakService extends AccessibilityService {
private static final String TAG = "SelectToSpeakService";
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());
checkClassName(event);
if (finished) {
finished = false;
} else {
Log.v(TAG, "bounce");
handler.removeCallbacks(runnable);
}
input = event;
handler.postDelayed(runnable, WAIT_TIME);
}
private void checkClassName(AccessibilityEvent event) {
Log.e(TAG, "checkClassName: mCurrentStep = " + mCurrentStep);
if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
String currentPackageName = event.getPackageName().toString();
String currentClassName = event.getClassName().toString();
switch (mCurrentStep) {
case WAITING:
if (!TextUtils.isEmpty(currentPackageName) && "com.android.incallui".equals(currentPackageName)) {
Log.e(TAG, "checkClassName: to dialer hands free");
// mCurrentStep = Step.DIALER_HANDS_FREE;
}
break;
default:
if (!TextUtils.isEmpty(currentClassName)) {
switch (currentClassName) {
case "com.tencent.mm.ui.LauncherUI":
if (mCurrentStep != Step.FIND_CONTACT) {
mCurrentStep = Step.CLICK_CONTACT;
}
break;
case "com.tencent.mm.plugin.label.ui.ContactLabelManagerUI":
break;
case "com.tencent.mm.plugin.account.ui.WelcomeActivity":
case "com.tencent.mm.plugin.account.ui.LoginPasswordUI":
Toaster.showLong("请先登录微信");
mCurrentStep = Step.WAITING;
break;
default:
}
}
}
}
}
/**
* 1.在微信页面直接找到联系人拨打电话
* 2.在联系人页面找到并拨打
* 3.通过进入联系人-标签找到并拨打
*
* @param event
*/
private void _onAccessibilityEvent(AccessibilityEvent event) {
Log.e(TAG, "_onAccessibilityEvent: " + mCurrentStep);
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(SelectToSpeakService.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;
}
}
//根据节点信息可获得对应的xy坐标
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;
}
//实现对xy坐标进行点击操作。
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; // 屏幕密度dpi120 / 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:
}
}
}
}

View File

@@ -1,23 +1,17 @@
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;
import com.google.android.accessibility.selecttospeak.SelectToSpeakService;
import com.hjq.toast.Toaster;
import com.xxpatx.os.R;
import com.xxpatx.os.base.mvvm.BaseMvvmActivity;
import com.xxpatx.os.bean.Contact;
import com.xxpatx.os.databinding.ActivityWechatCallBinding;
import com.xxpatx.os.service.WeAccessibilityService;
import com.xxpatx.os.utils.AccessibilityUtils;
public class CallWechatActivity extends BaseMvvmActivity<CallWechatViewModel, ActivityWechatCallBinding> {
@@ -25,9 +19,8 @@ public class CallWechatActivity extends BaseMvvmActivity<CallWechatViewModel, Ac
private static final String TAG = "CallWechatActivity";
private Contact mContact;
private int mCallType = 0;
private static final int REQUEST_CODE_SCREEN_CAPTURE = 1874;
// private static final int REQUEST_CODE_SCREEN_CAPTURE = 1874;
@Override
public boolean setfitWindow() {
@@ -76,34 +69,34 @@ 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();
}
}
// @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, SelectToSpeakService.class);
//// Intent intent = new Intent(CallWechatActivity.this, TalkBackService.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) {
Toast.makeText(this, "请在无障碍服务中打开 - 关爱守护快捷服务", Toast.LENGTH_LONG).show();
startActivity(new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS));
AccessibilityUtils.openAccessibilitySettings(CallWechatActivity.this);
}
return accessibility;
}
@@ -125,18 +118,27 @@ public class CallWechatActivity extends BaseMvvmActivity<CallWechatViewModel, Ac
return;
}
if (checkSettings()) {
mCallType = WeAccessibilityService.TYPE_VIDEO;
// 1. 获取 MediaProjectionManager 实例
MediaProjectionManager projectionManager = (MediaProjectionManager)
getSystemService(Context.MEDIA_PROJECTION_SERVICE);
Intent captureIntent = projectionManager.createScreenCaptureIntent();
startActivityForResult(captureIntent, REQUEST_CODE_SCREEN_CAPTURE);
// 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);
Intent intent = new Intent(CallWechatActivity.this, SelectToSpeakService.class);
intent.putExtra("WechatInfo", mContact);
intent.putExtra("call_type", SelectToSpeakService.TYPE_VIDEO);
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// startForegroundService(intent);
// } else {
startService(intent);
// }
finish();
}
}
@@ -147,12 +149,15 @@ public class CallWechatActivity extends BaseMvvmActivity<CallWechatViewModel, Ac
return;
}
if (checkSettings()) {
mCallType = WeAccessibilityService.TYPE_VOICE;
MediaProjectionManager projectionManager = (MediaProjectionManager)
getSystemService(Context.MEDIA_PROJECTION_SERVICE);
Intent captureIntent = projectionManager.createScreenCaptureIntent();
startActivityForResult(captureIntent, REQUEST_CODE_SCREEN_CAPTURE);
// MediaProjectionManager projectionManager = (MediaProjectionManager)
// getSystemService(Context.MEDIA_PROJECTION_SERVICE);
// Intent captureIntent = projectionManager.createScreenCaptureIntent();
// startActivityForResult(captureIntent, REQUEST_CODE_SCREEN_CAPTURE);
Intent intent = new Intent(CallWechatActivity.this, SelectToSpeakService.class);
intent.putExtra("WechatInfo", mContact);
intent.putExtra("call_type", SelectToSpeakService.TYPE_VOICE);
startService(intent);
}
}

View File

@@ -74,7 +74,6 @@ 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;
@@ -833,7 +832,7 @@ public class MainActivity extends BaseMvvmActivity<MainViewModel, ActivityMainBi
// 去开启 监听通知权限
startActivity(new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"));
}
sendBroadcast(new Intent(WeAccessibilityService.STOP_RECORD_ACTION));
// sendBroadcast(new Intent(SelectToSpeakService.STOP_RECORD_ACTION));
setDockApp();
}

View File

@@ -17,6 +17,7 @@ import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import androidx.lifecycle.Observer;
import com.google.android.accessibility.selecttospeak.SelectToSpeakService;
import com.hjq.toast.Toaster;
import com.tencent.mmkv.MMKV;
import com.xxpatx.os.BuildConfig;
@@ -31,7 +32,6 @@ import com.xxpatx.os.bean.AppInfo;
import com.xxpatx.os.bean.SystemSettings;
import com.xxpatx.os.config.CommonConfig;
import com.xxpatx.os.databinding.ActivitySettingBinding;
import com.xxpatx.os.service.WeAccessibilityService;
import com.xxpatx.os.service.main.MainService;
import com.xxpatx.os.utils.AccessibilityUtils;
import com.xxpatx.os.utils.ApkUtils;
@@ -282,7 +282,7 @@ public class SettingActivity extends BaseMvvmActivity<SettingViewModel, Activity
Log.e(TAG, "autoAccept: autoAccept = " + autoAccept);
mViewDataBinding.setAutoAccept(!autoAccept);
mMMKV.encode(CommonConfig.WECHAT_CALL_AUTO_ACCEPT, !autoAccept);
Intent intent = new Intent(WeAccessibilityService.SETTING_AUTOMATIC_ANSWER_ACTION);
Intent intent = new Intent(SelectToSpeakService.SETTING_AUTOMATIC_ANSWER_ACTION);
intent.putExtra("auto_answer", !autoAccept);
sendBroadcast(intent);
} else {
@@ -290,8 +290,7 @@ public class SettingActivity extends BaseMvvmActivity<SettingViewModel, Activity
if (floatWindowEnable) {
Toaster.showLong("因为系统限制,请先关闭悬浮窗功能,开启自动接听后再打开悬浮窗");
} else {
Toast.makeText(SettingActivity.this, "请在无障碍服务中打开 - 关爱守护快捷服务", Toast.LENGTH_LONG).show();
startActivity(new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS));
AccessibilityUtils.openAccessibilitySettings(SettingActivity.this);
}
}
}

View File

@@ -106,7 +106,7 @@ public class DockAppGridAdapter extends BaseAdapter {
case "com.android.dialer":
case "xxpatx.os.dialer":
size = readMissCall();
Log.e(TAG, "getView: readMissCall = " + size);
Log.d(TAG, "getView: readMissCall = " + size);
break;
default:
size = NotificationService.getNotificationLength(pkg);

View File

@@ -2,14 +2,12 @@ package com.xxpatx.os.adapter;
import android.content.Context;
import android.content.Intent;
import android.provider.Settings;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.constraintlayout.widget.ConstraintLayout;
@@ -113,8 +111,7 @@ public class WechatContactAdapter extends RecyclerView.Adapter<WechatContactAdap
private void checkAccessibility() {
if (!AccessibilityUtils.isAccessibilitySettingsOn(mContext)) {
Toast.makeText(mContext, "请在无障碍服务中打开 - 关爱守护快捷服务", Toast.LENGTH_LONG).show();
mContext.startActivity(new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS));
AccessibilityUtils.openAccessibilitySettings(mContext);
} else {
Log.e(TAG, "checkAccessibility: 无障碍服务已打开");
}

View File

@@ -1,9 +1,13 @@
package com.xxpatx.os.service;
import android.app.Notification;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.text.TextUtils;
import android.util.Log;
import com.xxpatx.os.utils.ApkUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
@@ -79,6 +83,20 @@ public class NotificationService extends NotificationListenerService {
for (NotificationListener listener : mListener) {
listener.onListenerUpdate();
}
// 获取通知来源包名如微信、QQ
String packageName = sbn.getPackageName();
if ("com.tencent.mm".equalsIgnoreCase(packageName)) {
// 提取通知内容
Notification notification = sbn.getNotification();
String text = notification.extras.getString(Notification.EXTRA_TEXT);
if (!TextUtils.isEmpty(text)) {
if (text.contains("邀请你语音通话") || text.contains("邀请你视频通话")) {
ApkUtils.openPackage(NotificationService.this, "com.tencent.mm");
}
}
}
}
@Override

View File

@@ -1,18 +1,21 @@
package com.xxpatx.os.utils;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;
import com.xxpatx.os.service.WeAccessibilityService;
import com.google.android.accessibility.selecttospeak.SelectToSpeakService;
public class AccessibilityUtils {
private static final String TAG = "AccessibilityUtils";
public static boolean isAccessibilitySettingsOn(Context context) {
int accessibilityEnabled = 0;
final String service = context.getPackageName() + "/" + WeAccessibilityService.class.getCanonicalName();
final String service = context.getPackageName() + "/" + SelectToSpeakService.class.getCanonicalName();
try {
accessibilityEnabled = Settings.Secure.getInt(
context.getApplicationContext().getContentResolver(),
@@ -40,4 +43,16 @@ public class AccessibilityUtils {
Log.v(TAG, "***ACCESSIBILITY IS DISABLED***");
return false;
}
public static void openAccessibilitySettings(Context context) {
Toast.makeText(context, "请在无障碍服务中打开 -孝心平安通讯OS服务", Toast.LENGTH_LONG).show();
Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
String str = context.getPackageName() + "/" + SelectToSpeakService.class.getName();
Bundle bundle = new Bundle();
bundle.putString(":settings:fragment_args_key", str);
intent.putExtra(":settings:fragment_args_key", str);
intent.putExtra(":settings:show_fragment_args", bundle);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
}