想要实现微信自动抢红包功能,就必须使用AccessibilityService 这个辅助功能类,那么我们去源码里面看看它到底是怎么回事
源码的开头大概有一百多行的注释,那么我来试着翻译一下,它是怎么描述的
/**
* Accessibility services should only be used to assist users with disabilities in using
* Android devices and apps. They run in the background and receive callbacks by the system
* when {@link AccessibilityEvent}s are fired. Such events denote some state transition
* in the user interface, for example, the focus has changed, a button has been clicked,
* etc. Such a service can optionally request the capability for querying the content
* of the active window. Development of an accessibility service requires extending this
* class and implementing its abstract methods.
*
它大概的意思是 辅助功能服务仅应用于帮助残障用户使用,它们在后台运行并接收系统的回调,
当用户界面焦点发生变更,或者一个button被点击,AccessibilityEvent 都会接收到回调事件,
开发者可以继承AccessibilityService 实现它的抽象方法,对此进行扩展
* The lifecycle of an accessibility service is managed exclusively by the system and
* follows the established service life cycle. Starting an accessibility service is triggered
* exclusively by the user explicitly turning the service on in device settings. After the system
* binds to a service, it calls {@link AccessibilityService#onServiceConnected()}. This method can
* be overriden by clients that want to perform post binding setup.
它的生命周期由系统来管理,遵循既定的服务生命周期,服务的开启必须由用户在设置中去手动开启,不能通过代码的方式去
开 启,当服务 被开启后,在 onServiceConnected 方法中会收到回调,关闭服务直接调用 disableSelf()方法
public class MyAccessibilityService extends AccessibilityService
{
@Override
public void onAccessibilityEvent(AccessibilityEvent accessibilityEvent)
{
}
@Override
protected void onServiceConnected()
{
super.onServiceConnected();
Log.d("mmp","===========抢红包服务启动了=============");
}
@Override
public void onInterrupt()
{
}
}
AccessibilityService需要在 AndroidManifest.xml 中进行声明
//权限
//意图过滤器
//配置信息
// 配置文件 我们自己配置的
accessibilityservice 具体的配置,就是我们上面 @xml/accessibilityservice 所引用的,这种引用方式也可以在代码中动态的区设置
android:accessibilityEventTypes 表示那些事件被触发 例如
typeNotificationStateChanged 通知栏
typeWindowStateChanged 窗口状态改变
android:accessibilityFeedbackType 反馈方式 例如 声音反馈 震动反馈
feedbackGeneric 为默认方式
android:packageNames 想要具体监听的App包名 com.tencent.mm 是微信的包名,如果想要再监听QQ的话
就在后面 写上一个 逗号 再加上QQ的包名 android:packageNames="com.tencent.mm,com.tencent.qq"
android:description="@string/accessibility_description" 自己随便取个名自就行
运行完上面的代码,可以看到,手机 无障碍 设置里面有一个 红包插件
手动开启服务后,可以收到 onServiceConnected 的回调事件
这样我们的服务就成功开启了
**public void onAccessibilityEvent(AccessibilityEvent accessibilityEvent)**中做事件回调逻辑处理
public class MyAccessibilityService extends AccessibilityService
{
@Override
protected void onServiceConnected()
{
super.onServiceConnected();
Log.d("mm_wx","===========抢红包服务启动了=============");
}
@Override
public void onAccessibilityEvent(AccessibilityEvent event)
{
if (event.getClassName() != null)
Log.d("mm_wx", event.getClassName().toString());
int eventType=event.getEventType();
switch (eventType)
{
//通知栏消息
case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:
break;
//窗口状态变更
case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
break;
}
}
@Override
public void onInterrupt()
{
}
}
运行以上代码,开启服务,使用微信发送一个红包给当前运行服务的手机,然后进入当前手机的微信聊天界面
我们就拿到了微信聊天界面的 UI 名称,然后点击界面上的红包,拿到领取红包界面的UI名称
以上三个界面的名称就全部拿到了
/**
* 微信几个页面的包名+地址。用于判断在哪个页面
* LAUCHER-微信聊天界面
* LUCKEY_MONEY_RECEIVER-点击红包弹出的界面
* LUCKEY_MONEY_DETAIL-红包领取后的详情界面
*/
private String LAUCHER = "com.tencent.mm.ui.LauncherUI";
private String LUCKEY_MONEY_DETAIL = "com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyDetailUI";
private String LUCKEY_MONEY_RECEIVER = "com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI";
接下来就直接上代码了吧 由于android 9.0 系统 使用AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED: 这种方式是监听不到通知栏的,所以我们再加上一个类
public class MyNotificationListenerService extends NotificationListenerService
{
@Override
public void onListenerConnected() {
//当连接成功时调用,一般在开启监听后会回调一次该方法
Log.e("onListenerConnected", "666666");
}
@Override
public void onNotificationPosted(StatusBarNotification sbn) {
Notification notification = sbn.getNotification();
if (notification == null) {
return;
}
if (sbn.getPackageName().equals("com.tencent.mm")) {
//打开对应的聊天界面
PendingIntent pendingIntent = notification.contentIntent;
try {
pendingIntent.send();
} catch (PendingIntent.CanceledException e) {
e.printStackTrace();
}
}
Log.e("StatusBarNotification", sbn.toString());
//当收到一条消息时回调,sbn里面带有这条消息的具体信息
}
@Override
public void onNotificationRemoved(StatusBarNotification sbn) {
//当移除一条消息的时候回调,sbn是被移除的消息
}
}
public class MyAccessibilityService extends AccessibilityService
{
/**
* 微信几个页面的包名+地址。用于判断在哪个页面
* LAUCHER-微信聊天界面
* LUCKEY_MONEY_RECEIVER-点击红包弹出的界面
* LUCKEY_MONEY_DETAIL-红包领取后的详情界面
*/
private String LAUCHER = "com.tencent.mm.ui.LauncherUI";
private String LUCKEY_MONEY_DETAIL = "com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyDetailUI";
private String LUCKEY_MONEY_RECEIVER = "com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI";
/**
* 用于判断是否点击过红包了
*/
private boolean isOpenRP;
private boolean isOpenDetail = false;
/**
* 用于判断是否屏幕是亮着的
*/
private boolean isScreenOn;
/**
* 获取PowerManager.WakeLock对象
*/
private PowerManager.WakeLock wakeLock;
/**
* KeyguardManager.KeyguardLock对象
*/
private KeyguardManager.KeyguardLock keyguardLock;
@Override
public void onAccessibilityEvent(AccessibilityEvent event)
{
if (event.getClassName() != null)
Log.e("packageName: ", event.getClassName().toString());
int eventType = event.getEventType();
switch (eventType)
{
//通知栏来信息,判断是否含有微信红包字样,是的话跳转
case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:
// if (event.getParcelableData() != null && event.getParcelableData() instanceof Notification) {
// Notification notification = (Notification) event.getParcelableData();
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// Log.e("ChannelId: ",notification.getChannelId()+"");
// }
// }
List texts = event.getText();
for (CharSequence text : texts)
{
String content = text.toString();
if (!TextUtils.isEmpty(content))
{
//判断是否含有[微信红包]字样
if (content.contains("[微信红包]"))
{
if (!isScreenOn())
{
wakeUpScreen();
}
//如果有则打开微信红包页面
openWeChatPage(event);
isOpenRP = false;
isOpenDetail=false;
Log.d("mm_wx", "==========如果有则打开微信红包页面isOpenDetail===========" + isOpenDetail);
}
}
}
break;
//界面跳转的监听
case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
String className = null;
if (event.getClassName() != null)
{
className = event.getClassName().toString();
}
Log.d("mm_wx", "==========isOpenDetail===========" + isOpenDetail);
//判断是否是微信聊天界面
if (LAUCHER.equals(className) && !isOpenDetail)
{
Log.d("mm_wx", "==========来了===========" + isOpenDetail);
//获取当前聊天页面的根布局
AccessibilityNodeInfo rootNode = getRootInActiveWindow();
//开始找红包
findRedPacket(rootNode);
}
//判断是否是显示‘开’的那个红包界面
if ("com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyNotHookReceiveUI".equals(className))
{
AccessibilityNodeInfo rootNode = getRootInActiveWindow();
//开始抢红包
openRedPacket(rootNode);
}
//判断是否是红包领取后的详情界面
if (isOpenDetail && LUCKEY_MONEY_DETAIL.equals(className))
{
//isOpenDetail = false;
//返回桌面
back2Home();
//如果之前是锁着屏幕的则重新锁回去
release();
}
break;
}
}
/**
* 开始打开红包
*/
private void openRedPacket(AccessibilityNodeInfo rootNode)
{
if (rootNode == null)
return;
for (int i = 0; i < rootNode.getChildCount(); i++)
{
AccessibilityNodeInfo node = rootNode.getChild(i);
if ("android.widget.Button".equals(node.getClassName()))
{
Log.e("6666", "来了");
node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
isOpenDetail = true;
}
openRedPacket(node);
}
}
/**
* 遍历查找红包
*/
private void findRedPacket(AccessibilityNodeInfo rootNode)
{
if (rootNode != null)
{
//从最后一行开始找起
for (int i = rootNode.getChildCount() - 1; i >= 0; i--)
{
AccessibilityNodeInfo node = rootNode.getChild(i);
//如果node为空则跳过该节点
if (node == null)
{
continue;
}
CharSequence text = node.getText();
if (text != null && text.toString().equals("微信红包"))
{
AccessibilityNodeInfo parent = node.getParent();
//while循环,遍历"领取红包"的各个父布局,直至找到可点击的为止
while (parent != null)
{
if (parent.isClickable())
{
//模拟点击
parent.performAction(AccessibilityNodeInfo.ACTION_CLICK);
//isOpenRP用于判断该红包是否点击过
isOpenRP = true;
break;
}
parent = parent.getParent();
}
}
//判断是否已经打开过那个最新的红包了,是的话就跳出for循环,不是的话继续遍历
if (isOpenRP)
{
isOpenRP = false;
break;
} else
{
findRedPacket(node);
}
}
}
}
/**
* 开启红包所在的聊天页面
*/
private void openWeChatPage(AccessibilityEvent event)
{
if (event.getParcelableData() != null && event.getParcelableData() instanceof Notification)
{
Notification notification = (Notification) event.getParcelableData();
//打开对应的聊天界面
PendingIntent pendingIntent = notification.contentIntent;
try
{
pendingIntent.send();
} catch (PendingIntent.CanceledException e)
{
e.printStackTrace();
}
}
}
/**
* 服务连接
*/
@Override
protected void onServiceConnected()
{
Toast.makeText(this, "抢红包服务开启", Toast.LENGTH_SHORT).show();
super.onServiceConnected();
}
/**
* 必须重写的方法:系统要中断此service返回的响应时会调用。在整个生命周期会被调用多次。
*/
@Override
public void onInterrupt()
{
Toast.makeText(this, "我快被终结了啊-----", Toast.LENGTH_SHORT).show();
}
/**
* 服务断开
*/
@Override
public boolean onUnbind(Intent intent)
{
Toast.makeText(this, "抢红包服务已被关闭", Toast.LENGTH_SHORT).show();
return super.onUnbind(intent);
}
/**
* 返回桌面
*/
private void back2Home()
{
Intent home = new Intent(Intent.ACTION_MAIN);
home.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
home.addCategory(Intent.CATEGORY_HOME);
startActivity(home);
}
/**
* 判断是否处于亮屏状态
*
* @return true-亮屏,false-暗屏
*/
private boolean isScreenOn()
{
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
isScreenOn = pm.isScreenOn();
Log.e("isScreenOn", isScreenOn + "");
return isScreenOn;
}
/**
* 解锁屏幕
*/
private void wakeUpScreen()
{
//获取电源管理器对象
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
//后面的参数|表示同时传入两个值,最后的是调试用的Tag
wakeLock = pm.newWakeLock(PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.FULL_WAKE_LOCK, "red:bright");
if (wakeLock == null)
{
return;
}
//点亮屏幕
wakeLock.acquire();
//得到键盘锁管理器
KeyguardManager km = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
keyguardLock = km.newKeyguardLock("unlock");
//解锁
keyguardLock.disableKeyguard();
}
/**
* 释放keyguardLock和wakeLock
*/
public void release()
{
if (keyguardLock != null)
{
keyguardLock.reenableKeyguard();
keyguardLock = null;
}
if (wakeLock != null)
{
wakeLock.release();
wakeLock = null;
}
}
}