/**
* 叮咚买菜辅助
*/
public class DingdongService extends AccessibilityService {
private static final String PACKAGE_NAME = "com.yaya.zone";
private Rect payRect;
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
CharSequence packageName = event.getPackageName();
XLog.i("进入了叮咚买菜:%s", packageName);
if (!packageName.equals(PACKAGE_NAME)) {
return;
}
AccessibilityNodeInfo source = event.getSource();
if (source == null) {
return;
}
XLog.i("进入了叮咚买菜");
clickPay(source);
clickTime(source);
continuePay(source);
payConfirm(source);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private void clickTime(AccessibilityNodeInfo source) {
String viewId = "com.yaya.zone:id/cl_item_select_hour_root";
List<AccessibilityNodeInfo> timeList = source.findAccessibilityNodeInfosByViewId(viewId);
if (timeList == null || timeList.size() == 0) {
onBack(source);
return;
}
boolean ok = false;
int size = timeList.size();
for (int i = size - 1; i >= 0; i--) {
AccessibilityNodeInfo item = timeList.get(i);
if (item.isClickable()) {
if (findByText(item, "已约满") == null) {
AccessibilityNodeInfo nodeTitle = findById(item, "com.yaya.zone:id/tv_item_select_hour_title");
if (nodeTitle != null) {
XLog.i("选中了时间,请确认付款...%s", nodeTitle.getText());
}
item.performAction(AccessibilityNodeInfo.ACTION_CLICK);
ok = true;
break;
}
}
}
if (!ok) {
XLog.i("抢菜失败,退出继续点击支付...");
onBack(source);
}
}
private void payConfirm(AccessibilityNodeInfo source) {
AccessibilityNodeInfo pay = findByText(source, "确认交易");
if (pay == null) {
return;
}
pay.performAction(AccessibilityNodeInfo.ACTION_CLICK);
}
private void continuePay(AccessibilityNodeInfo source) {
AccessibilityNodeInfo pay = findByText(source, "继续支付");
if (pay == null) {
return;
}
pay.performAction(AccessibilityNodeInfo.ACTION_CLICK);
}
private void clickPay(AccessibilityNodeInfo source) {
AccessibilityNodeInfo pay = findByText(source, "立即支付");
if (pay == null) {
return;
}
pay.performAction(AccessibilityNodeInfo.ACTION_CLICK);
payRect = new Rect();
XLog.i("获取支付坐标....%s", payRect);
pay.getBoundsInScreen(payRect);
}
private AccessibilityNodeInfo findByText(AccessibilityNodeInfo source, String text) {
List<AccessibilityNodeInfo> pay = source.findAccessibilityNodeInfosByText(text);
if (pay == null || pay.size() == 0) {
return null;
}
for (AccessibilityNodeInfo item : pay) {
return item;
}
return null;
}
private AccessibilityNodeInfo findById(AccessibilityNodeInfo source, String id) {
List<AccessibilityNodeInfo> pay = source.findAccessibilityNodeInfosByViewId(id);
if (pay == null || pay.size() == 0) {
return null;
}
for (AccessibilityNodeInfo item : pay) {
return item;
}
return null;
}
@Override
public void onInterrupt() {
}
@Override
protected void onServiceConnected() {
super.onServiceConnected();
XLog.i("辅助服务启动成功...");
}
public void onBack(AccessibilityNodeInfo source) {
String viewId = "com.yaya.zone:id/iv_dialog_select_time_close";
List<AccessibilityNodeInfo> timeList = source.findAccessibilityNodeInfosByViewId(viewId);
if (timeList == null || timeList.size() == 0) {
XLog.i("没有找到关闭按钮");
return;
}
XLog.i("找到了关闭按钮:%s", timeList.size());
for (AccessibilityNodeInfo item : timeList) {
if (item.isClickable()) {
item.performAction(AccessibilityNodeInfo.ACTION_CLICK);
XLog.i("关闭菜单");
if (payRect != null) {
XLog.i("点击支付坐标....%s", payRect);
click(new Point(payRect.centerX(), payRect.centerY()));
return;
}
}
}
Point point = new Point(10, 10);
click(point);
XLog.i("点击失败...");
}
@TargetApi(24)
public void click(Point point) {
//只有7.0才可以用
GestureDescription.Builder builder = new GestureDescription.Builder();
Path path = new Path();
path.moveTo((float) point.x, (float) point.y);
path.lineTo((float) point.x, (float) point.y);
/**
* 参数path:笔画路径
* 参数startTime:时间 (以毫秒为单位),从手势开始到开始笔划的时间,非负数
* 参数duration:笔划经过路径的持续时间(以毫秒为单位),非负数
*/
builder.addStroke(new GestureDescription.StrokeDescription(path, 1, 1));
final GestureDescription build = builder.build();
/**
* 参数GestureDescription:翻译过来就是手势的描述,如果要实现模拟,首先要描述你的腰模拟的手势嘛
* 参数GestureResultCallback:翻译过来就是手势的回调,手势模拟执行以后回调结果
* 参数handler:大部分情况我们不用的话传空就可以了
* 一般我们关注GestureDescription这个参数就够了,下边就重点介绍一下这个参数
*/
dispatchGesture(build, new GestureResultCallback() {
public void onCancelled(GestureDescription gestureDescription) {
super.onCancelled(gestureDescription);
}
public void onCompleted(GestureDescription gestureDescription) {
super.onCompleted(gestureDescription);
}
}, null);
}
}
叮咚买菜辅助核心代码
配置文档accessibility_service_config.xml
<?xml version="1.0" encoding="utf-8"?>
<accessibility-service
xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityEventTypes="typeWindowStateChanged|typeWindowContentChanged|typeNotificationStateChanged"
android:accessibilityFeedbackType="feedbackAllMask"
android:packageNames="com.yaya.zone"
android:notificationTimeout="10"
android:accessibilityFlags="flagDefault|flagRetrieveInteractiveWindows|flagIncludeNotImportantViews"
android:canRetrieveWindowContent="true"/>
AndroidManifest.xml配置服务
<service
android:name=".maicai.DingdongService"
android:exported="false"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
<meta-data
android:name="android.accessibilityservice"
android:resource="@xml/accessibility_service_config" />
</service>
手动授权辅助服务启动
/**
* 该辅助功能开关是否打开了
*
* @param accessibilityServiceName:指定辅助服务名字
* @param context:上下文
* @return
*/
private boolean isAccessibilitySettingsOn(String accessibilityServiceName, Context context) {
int accessibilityEnable = 0;
String serviceName = context.getPackageName() + "/" + accessibilityServiceName;
try {
accessibilityEnable = Settings.Secure.getInt(context.getContentResolver(),
Settings.Secure.ACCESSIBILITY_ENABLED, 0);
} catch (Exception e) {
XLog.e("get accessibility enable failed, the err:" + e.getMessage());
}
if (accessibilityEnable == 1) {
TextUtils.SimpleStringSplitter mStringColonSplitter
= new TextUtils.SimpleStringSplitter(':');
String settingValue = Settings.Secure.getString(context.getContentResolver(),
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
if (settingValue != null) {
mStringColonSplitter.setString(settingValue);
while (mStringColonSplitter.hasNext()) {
String accessibilityService = mStringColonSplitter.next();
if (accessibilityService.equalsIgnoreCase(serviceName)) {
XLog.v("We've found the correct setting - accessibility is switched on!");
return true;
}
}
}
} else {
XLog.w("Accessibility service disable");
}
return false;
}
/**
* 跳转到系统设置页面开启辅助功能
*
* @param accessibilityServiceName:指定辅助服务名字
* @param context:上下文
*/
private void openAccessibility(String accessibilityServiceName, Context context) {
if (!isAccessibilitySettingsOn(accessibilityServiceName, context)) {
Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
context.startActivity(intent);
}
}