每次看到别人发红包,但是没有抢到,这就很尴尬了。于是想着做一个红包插件,嘿嘿,再也不怕我反应迟钝了。
首先,想到做这个功能时,我联想到的是这个是不是lua写出来的脚本呢?但是条件不成立,因为可以不需要ROOT的。那么问题就来了,这玩意怎么实现?网上查了查,这下子明白了,Android平台为了给一些使用手机不是很方便的人提供了一个解决方案,被叫做AccessibilityService。这是一个辅助类,用于监听手机的焦点,窗口变化,按钮点击等等。
接下来,分析了源码上的绿色字体,大致意思如下:
无障碍服务的开发需要扩展这个类并实现其抽象方法。生命周期
由系统和遵循已建立的服务生命周期当用户在设备设置中或在设备设置期间关闭时,无障碍服务将停止声明
必须完成2件事:
1.指定处理”android.accessibilityservice.AccessibilityService
”
2.请求android.Manifest.permission#BIND_ACCESSIBILITY_SERVICE权限使有且仅有系统可以绑定。两种配置方法:
1.在manifest中设置;
2.在代码类中动态设置。辅助功能可以配置为特定类型的辅助功能事件,仅监听特定的包,在给定的时间内做指定的事。
继续往下看代码,Callbacks里有onAccessibilityEvent、onInterrupt和onServiceConnected是很重要的。
关于onAccessibilityEvent,源码的解释:
The new event. This event is owned by the caller and cannot be used after this method returns. Services wishing to use the event after this method returns should make a copy.
意思大致是我们需要继承AccessibilityService,然后实现onAccessibilityEvent方法。
关于onInterrupt,源码的解释:
Callback for interrupting the accessibility feedback.
这个是用功能中断时回调。
关于onServiceConnected,源码的解释:
This method is a part of the {@link AccessibilityService} lifecycle and is called after the system has successfully bound to the service. If is convenient to use this method for setting the {@link AccessibilityServiceInfo}.
系统成功绑定后回调,前面提到的代码配置也是在这个方法内。
好了,源码看到这里。我们需要的几个方法都了解了,接下来直接来看代码。首先创建工程,然后创建一个RBAccessibilityService继承自AccessibilityService。实现onAccessibilityEvent、onInterrupt和onServiceConnected方法。然后配置manifest
<service
android:name=".service.RBAccessibilityService"android:exported="true"android:enabled="true"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/accessibilityservice_set"></meta-data></service>
我们需要注意的是meta-data里的resource,这里需要在res创建一个xml文件夹,写入如下代码:
<accessibility-service
xmlns:android="http://schemas.android.com/apk/res/android"android:accessibilityEventTypes="typeAllMask"android:accessibilityFeedbackType="feedbackSpoken"android:accessibilityFlags="flagDefault"android:canRequestTouchExplorationMode="true"android:canRetrieveWindowContent="true"android:notificationTimeout="50"android:description="@string/accessibility_description"android:packageNames="com.tencent.mobileqq,com.tencent.mm"/>
packageNames里的是需要监听的包名,需逗号隔开。配置完成,继续实现具体代码,完善RBAccessibilityService的onAccessibilityEvent:
@Overridepublic void onAccessibilityEvent(AccessibilityEvent event) {String className = event.getClassName().toString();switch (event.getEventType()) {//通知栏case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:List<CharSequence> texts = event.getText();if (!texts.isEmpty()) {for (CharSequence text : texts) {String content = text.toString();/*** 微信的notification*/if (content.contains(WECHAT_RB_TEXT)) {//赋值给判断值QQ_OR_WECHAT = THISWECHAT;RBnotification(event);}/*** QQ的notification*/if (content.contains(QQ_RB_TXT)) {//赋值给判断值QQ_OR_WECHAT = THISQQ;RBnotification(event);}}}break;case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED://判断是QQ或者微信发送来的红包if (QQ_OR_WECHAT == THISQQ) {openQQHongBao(event);} else {openWeChatHongBao(event);}break;case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED:break;default:break;}}
看明白了么?这个方法内TYPE_NOTIFICATION_STATE_CHANGED状态就是监听微信或QQ发送红包过来时出现的notification,然后通过这个notification进入出现该红包的聊天页面。这个时候
窗口状态也变换了,又运行到了TYPE_WINDOW_STATE_CHANGED这个状态,这时我们的微信或QQ应该还是在聊天页面,接下来去实现点击或打开功能。需要注意的是,微信打开红包需要两次点击,先是领取,然后打开,但是QQ只需要一次点击,微信红包较简单,只有一种类型,QQ红包类型有两种。所以我们应该在窗口变化之前监听是QQ还是微信发送过来的红包,然后根据不同的值去做不同的动作。
QQ红包
//检测到QQ红包private void openQQHongBao(AccessibilityEvent event) {state_qq = STATE_NO_QQ;
// getRunningActivityName();if ("cooperation.qwallet.plugin.QWalletPluginProxyActivity".equals(event.getClassName())) {state_qq = STATE_OPENED_QQ;if (RBSharedPerences.readRBQQState(getApplicationContext(), STATE_CODE_QQ).equals(state_qq)) {performGlobalAction(GLOBAL_ACTION_HOME);RBSharedPerences.writeRBQQstate(getApplicationContext(), STATE_CODE_QQ, STATE_NO_QQ);}} else if ("com.tencent.mobileqq.activity.SplashActivity".equals(event.getClassName())) {//拆红包state_qq = STATE_CLIECKED_QQ;openQQPacket();}}//领取打开QQ红包private void openQQPacket() {AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();if (nodeInfo != null) {AccessibilityNodeInfo targetNode = null;targetNode = findNodeInfosByText(nodeInfo, RB_BUTTON_TEXT_NAME);//普通红包if (targetNode != null) {performClick(targetNode);RBSharedPerences.writeRBQQstate(getApplicationContext(), STATE_CODE_QQ, STATE_OPENED_QQ);}//口令红包else {AccessibilityNodeInfo accessibilityNodeInfo = getRootInActiveWindow();if (accessibilityNodeInfo != null) {List<AccessibilityNodeInfo> nodeInfos = accessibilityNodeInfo.findAccessibilityNodeInfosByText(RB_PASSWORD);for (AccessibilityNodeInfo nodeInfo1 : nodeInfos) {targetNode = nodeInfos.get(nodeInfos.size() - 1);performClick(targetNode);writePassword();}}}}}//写入并发送口令private void writePassword() {AccessibilityNodeInfo accessibilityNodeInfo = getRootInActiveWindow();if (accessibilityNodeInfo != null) {findWidgetByText(accessibilityNodeInfo, RB_CLICK_TO_PASTE_PASSWORD);}if (accessibilityNodeInfo != null) {findWidgetByText(accessibilityNodeInfo, SEND_PASSWORD);RBSharedPerences.writeRBQQstate(getApplicationContext(), STATE_CODE_QQ, STATE_OPENED_QQ);}}
这里面有个巧妙的点,就是开始时我始终找不到红包RelativeLayout的ID,那可怎么办呢,我继续往外层查找,红包外层还是一层RelativeLayout,但是content-desc中的值是 “xxxx,点击查看详情”。
明白了吗?没错,我通过文本查找遇到有文字包含有这个时再去实现点击nodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK);
那么口令红包又是如何实现的呢?同理,也是需要通过文本查找文字
但是这里直接查找”口令红包”就可以了,查找到后实现点击,QQ会自动给我们出现一个按钮在输入框上方
继续查找文本并点击”点击输入口令”,这时候,输入框就有口令了,那么查找到发送的文本再实现一次点击,红包就领取成功了。
微信红包
//检测到微信红包private void openWeChatHongBao(AccessibilityEvent event) {state = STATE_NO;
// getRunningActivityName();if ("com.tencent.mm.ui.LauncherUI".equals(event.getClassName())) {//点中红包getWeChatPacket();} else if ("com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI".equals(event.getClassName())) {//拆红包openWeChatPacket();} else if ("com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyDetailUI".equals(event.getClassName())) {state = STATE_OPENED;//拆完红包后返回if (RBSharedPerences.readRBWeChatState(getApplicationContext(), STATE_CODE).equals(state)) {performGlobalAction(GLOBAL_ACTION_HOME);RBSharedPerences.writeRBWeChatState(getApplicationContext(), STATE_CODE, STATE_NO);}}}//领取红包private void getWeChatPacket() {AccessibilityNodeInfo accessibilityNodeInfo = getRootInActiveWindow();if (accessibilityNodeInfo != null) {findWidgetByText(accessibilityNodeInfo, GET_RB_TEXT);}}//拆开红包@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)private void openWeChatPacket() {AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();if (nodeInfo != null) {AccessibilityNodeInfo targetNode = null;targetNode = findNodeInfosByClassName(nodeInfo, RB_BUTTON_CLASS_NAME);performClick(targetNode);//设置值RBSharedPerences.writeRBWeChatState(getApplicationContext(), STATE_CODE, STATE_OPENED);}}
微信的红包和QQ 不一样的地方在于需要先点开红包出现一个红包页面,再点击红包才会打开红包。点开红包查找文本”领取红包”并实现点击,这时页面进入”com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI”
这时候显然通过文本查找是不行的了,因为这里只是一个Button,并没有文字,所以说需要另辟蹊径,因为当前页面仅有这一个Button,所以我们可以通过控件具体名字查找控件。
//通过组件名字查找public static AccessibilityNodeInfo findNodeInfosByClassName(AccessibilityNodeInfo nodeInfo, String className) {if (TextUtils.isEmpty(className)) {return null;}for (int i = 0; i < nodeInfo.getChildCount(); i++) {AccessibilityNodeInfo node = nodeInfo.getChild(i);if (className.equals(node.getClassName())) {return node;}}return null;}
再实现一个点击事件。perfect,微信红包领取成功。
一个抢红包功能就这么愉快的搞定了!!快去试试吧!!你的点赞是对我最好的支持!!谢谢!!
最后附上CSDN下载链接和github的链接
http://download.csdn.net/detail/a876980961/9723236
https://github.com/a876980961/pop_hongbao_assistant