一、内容简介
AccesslibityService辅助功能服务目的是帮助那些具有视觉、身体或年龄相关限制的用户而设计的,主要功能是控制屏幕视图的响应,可以模拟点击,后退,滚动等事件,可用于自动化处理。因此可用来开发自动抢红包等功能,惊奇死我了,开篇第一弹就让我大有收获,迫不及待的分享给我的猿友们。
- 官方API:AccessibilityService
二、代码示例(通过自动抢红包来讲解)
(1)注册方式
(a) 代码注册,继承AccessibilityService,重写onServiceConnected()方法,如下:
@Override
protected void onServiceConnected() {
AccessibilityServiceInfo info = getServiceInfo();
info.eventTypes = AccessibilityEvent.TYPES_ALL_MASK;
info.feedbackType = AccessibilityServiceInfo.FEEDBACK_ALL_MASK;
info.notificationTimeout = 1000;
info.packageNames = packageNames;
setServiceInfo(info);
super.onServiceConnected();
}
(b) Android4.0以后,可以通过meta-data标签引用xml注册
- (i) 新建accessibility.xml,在res/xml/accessibility.xml,如下:
<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityEventTypes="typeAllMask"
android:accessibilityFeedbackType="feedbackAllMask"
android:accessibilityFlags="flagDefault|flagRetrieveInteractiveWindows
|flagReportViewIds|flagRequestFilterKeyEvents"
android:canRequestEnhancedWebAccessibility="true"
android:canRequestTouchExplorationMode="true"
android:canRetrieveWindowContent="true"
android:description="@string/lbl_auto_open_red_packet_desc"
android:notificationTimeout="1000" />
(ii) 在AndroidManifest.xml中注册service,如下
<service
android:name=".service.AutoOpenRedPacketService"
android:label="@string/lbl_auto_open_red_packet"
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>
(2)功能代码(抢红包功能使用的是方式注册)
(a) 简单的封装了一下,AccessibilityService提供的一些Action操作,定义到接口IAccessbilityAction中,如下:
package com.ronindong.meet.android.dao;
import android.view.accessibility.AccessibilityNodeInfo;
public interface IAccessbilityAction {
/**
* 模拟后退事件
*/
void performBack();
/**
* 模拟向上滚动
*/
void performScrollUp();
/**
* 模拟向下滚动
*/
void performScrollDown();
/**
* 模拟view点击
*
* @param nodeInfo
*/
void performViewClick(AccessibilityNodeInfo nodeInfo);
/**
* 根据文本获取view控件
*
* @param text
* @return
*/
AccessibilityNodeInfo findViewByText(String text);
/**
* 根据文本获取view控件
*
* @param text
* @param clickable
* @return
*/
AccessibilityNodeInfo findViewByText(String text, boolean clickable);
/**
* 点击指定text的view
*
* @param text
*/
void clickTextViewByText(String text);
/**
* 点击指定viewId的view
*
* @param viewId
*/
void clickTextViewByViewId(String viewId);
/**
* 模拟文本框输入
*
* @param info
* @param text
*/
void performInputText(AccessibilityNodeInfo info, String text);
/**
*
* @param serviceName
* @return
*/
boolean checkAccessbilityEnabled(String serviceName);
}
(b) 编写基类 BaseAccessibilityService,代码如下:
package com.ronindong.meet.android.service;
import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import com.ronindong.meet.android.dao.IAccessbilityAction;
import java.util.List;
/**
* @author donghailong
*/
public abstract class BaseAccessibilityService extends AccessibilityService
implements IAccessbilityAction {
private static final String TAG = BaseAccessibilityService.class.getSimpleName();
/**
*
*/
private AccessibilityManager mManager;
public BaseAccessibilityService() {
}
@Override
public void performBack() {
performGlobalAction(GLOBAL_ACTION_BACK);
}
@Override
public void performScrollUp() {
performGlobalAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
}
@Override
public void performScrollDown() {
performGlobalAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
}
@Override
public void performViewClick(AccessibilityNodeInfo nodeInfo) {
if (nodeInfo == null) {
return;
}
while (nodeInfo != null) {
if (nodeInfo.isClickable()) {
nodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK);
break;
}
nodeInfo = nodeInfo.getParent();
}
}
@Override
public AccessibilityNodeInfo findViewByText(String text) {
return findViewByText(text, false);
}
@Override
public AccessibilityNodeInfo findViewByText(String text, boolean clickable) {
AccessibilityNodeInfo accessibilityNodeInfo = getRootInActiveWindow();
if (accessibilityNodeInfo == null) {
return null;
}
List<AccessibilityNodeInfo> nodeInfoList =
accessibilityNodeInfo.findAccessibilityNodeInfosByText(text);
if (nodeInfoList != null && !nodeInfoList.isEmpty()) {
for (AccessibilityNodeInfo nodeInfo : nodeInfoList) {
if (nodeInfo != null && (nodeInfo.isClickable() == clickable)) {
return nodeInfo;
}
}
}
return null;
}
@Override
public void clickTextViewByText(String text) {
AccessibilityNodeInfo accessibilityNodeInfo = getRootInActiveWindow();
if (accessibilityNodeInfo == null) {
Log.i(TAG, "accessibilityNodeInfo is null");
return;
}
List<AccessibilityNodeInfo> nodeInfoList = accessibilityNodeInfo
.findAccessibilityNodeInfosByText(text);
if (nodeInfoList != null && !nodeInfoList.isEmpty()) {
for (AccessibilityNodeInfo nodeInfo : nodeInfoList) {
if (nodeInfo != null) {
performViewClick(nodeInfo);
break;
}
}
}
}
@Override
public void clickTextViewByViewId(String viewId) {
AccessibilityNodeInfo accessibilityNodeInfo = getRootInActiveWindow();
if (accessibilityNodeInfo == null) {
Log.i(TAG, "accessibilityNodeInfo is null");
return;
}
List<AccessibilityNodeInfo> nodeInfoList = accessibilityNodeInfo
.findAccessibilityNodeInfosByViewId(viewId);
if (nodeInfoList != null && !nodeInfoList.isEmpty()) {
for (AccessibilityNodeInfo nodeInfo : nodeInfoList) {
if (nodeInfo != null) {
performViewClick(nodeInfo);
break;
}
}
}
}
@Override
public void performInputText(AccessibilityNodeInfo info, String text) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Bundle arguments = new Bundle();
arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, text);
info.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText("label", text);
clipboard.setPrimaryClip(clip);
info.performAction(AccessibilityNodeInfo.ACTION_FOCUS);
info.performAction(AccessibilityNodeInfo.ACTION_PASTE);
}
}
@Override
public boolean checkAccessbilityEnabled(String serviceName) {
List<AccessibilityServiceInfo> accessibilityServices =
mManager.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_GENERIC);
for (AccessibilityServiceInfo info : accessibilityServices) {
if (info.getId().equals(serviceName)) {
return true;
}
}
return false;
}
}
(c) 抢红包功能实现类AutoOpenRedPacketService,代码如下:
package com.ronindong.meet.android.service;
import android.util.Log;
import android.view.accessibility.AccessibilityEvent;
/**
* @author donghailong
*/
public class AutoOpenRedPacketService extends BaseAccessibilityService {
private static final String TAG = AutoOpenRedPacketService.class.getSimpleName();
/**
* wx包名
*/
public static final String WX_PACKAGE_NAME = "com.tencent.mm";
/**
* text
*/
public static final String WX_RED_PACKAGE_TEXT = "微信红包";
/**
* 开 对应的viewId(随着微信的更新,可能会变)
*/
public static final String WX_OPEN_RED_PACKAGE_VIEW_ID = "com.tencent.mm:id/c31";
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
Log.i(TAG, "event type:" + event.getEventType());
if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
&& event.getPackageName().equals(WX_PACKAGE_NAME)) {
clickTextViewByText(WX_RED_PACKAGE_TEXT);
clickTextViewByViewId(WX_OPEN_RED_PACKAGE_VIEW_ID);
}
}
@Override
public void onInterrupt() {
}
}
(d) 由于AccessibilityService是系统级别的服务,需要用户主动打开。代码如下:
package com.ronindong.meet.android.helper;
import android.content.Context;
import android.content.Intent;
import android.provider.Settings;
/**
* @author donghailong
*/
public class CxHelper {
private Context context;
public void init(Context cx) {
if (null != cx) {
this.context = cx.getApplicationContext();
}
}
/**
* 打开辅助功能设置
*/
public void openAccessSetting() {
Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
}
(e)最后一步,只需要在MainActivity中,请求权限即可,如下:
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.openAccess:
SingletonManager.get(CxHelper.class).openAccessSetting();
break;
default:
break;
}
}
大功告成!目前只是实现了简单的抢红包功能,还有需要待完善的地方。
注:以上代码亲测可用,若有问题,欢迎随时联系我。
三、用途展望
- 可实现自动安装APP;模拟各种事件,如点击,滚动等
- 自动抢红包;
- 自动打电话功能
- 解放用户,实现一些自动化操作
- 你的想象不应该被限制
四、爬坑实录
- AccessbiliryService辅助服务,属于系统级别的服务,需要用户主动开启.
- 继承AccessbiliryService,需要实现的逻辑代码,不可在外部调用,否则没有效果;
- AccessbiliryService的子类服务,和普通service不同,不需要主动启动。用户主动开启权限后,服务就运行了。
- 辅助服务不容易捕捉没有text或没有设置Id的控件;还有EditText和ImageView也不容易获取。
Github地址:MeetAndroidDemo