一、描述
最近看到同事有用抢红包的软件,就想看看抢红包的具体实现是如何的,所以了解了一下,有用辅助功能实现的,所以在下面的示例中会展示一个抢红包的小Demo,附带源码抢红包源码。
二、效果图
在桌面收到红包进行抢
在聊天页面收到口令红包
三、AccessibilityService使用
创建辅助服务类,继承AccessibilityService,实现两个接口,接收系统的事件
public class MyService extends AccessibilityService {
@Override public void onAccessibilityEvent(AccessibilityEvent event) {
}
@Override public void onInterrupt() {
}}
辅助服务的配置文件,配置事件,在 res/xml下创建accessibility_service_info.xml
//具体属性的说明在第5点有说明注册Service辅助服务,并且为Service附加上第二步创建的xml,看清除下面的一些属性,必须要加,如果有的没加的话是没效果的
4 清单文件中添加权限辅助服务配置文件xml属性说明:
//是否可以检索整个层级下的内容android:canRetrieveWindowContent="true"级下的信息
//事件通知触发点,比如窗口打开,滑动,焦点变化,长按等。
android:accessibilityEventTypes="typeAllMask"#TYPES_ALL_MASK:所有类型#TYPE_VIEW_CLICKED :单击#TYPE_VIEW_LONG_CLICKED :长按#TYPE_VIEW_SELECTED :选中#TYPE_VIEW_FOCUSED :获取焦点#TYPE_VIEW_TEXT_CHANGED :文字改变#TYPE_WINDOW_STATE_CHANGED :窗口状态改变
//表示反馈方式,比如是语音播放,还是震动android:accessibilityFeedbackType="feedbackGeneric"
//接受事件的时间间隔,通常将其设置为100即可.
android:notificationTimeout="100"
//表示该服务是用来单独监听哪个应用的产生的事件,其他的都会过滤,如果不填就是对所有的应用进行监听,填入包名即可。
android:packageNames="top.cokernut.sample"
//在代码中我们就可以通过node节点来getViewIdResourceName()获取对应的节点的id android:accessibilityFlags="flagDefault"
提供一个AccessibilityService的基类,集成了一些常用方法:public class BaseAccessibilityService extends AccessibilityService {
private AccessibilityManager mAccessibilityManager;private Context mContext;private static BaseAccessibilityService mInstance;
public void init(Context context) {mContext = context.getApplicationContext();mAccessibilityManager = (AccessibilityManager) mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);}
public static BaseAccessibilityService getInstance() {if (mInstance == null) {mInstance = new BaseAccessibilityService();}return mInstance;}
/*** Check当前辅助服务是否启用** @param serviceName serviceName* @return 是否启用*/ private boolean checkAccessibilityEnabled(String serviceName) {List
/*** 前往开启辅助服务界面*/ public void goAccess() {Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);mContext.startActivity(intent);}
/*** 模拟点击事件** @param nodeInfo nodeInfo*/ public void performViewClick(AccessibilityNodeInfo nodeInfo) {if (nodeInfo == null) {return;}while (nodeInfo != null) {if (nodeInfo.isClickable()) {nodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK);break;}nodeInfo = nodeInfo.getParent();}}
/*** 模拟返回操作*/ public void performBackClick() {try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}performGlobalAction(GLOBAL_ACTION_BACK);}
/*** 模拟下滑操作*/ public void performScrollBackward() {try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}performGlobalAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);}
/*** 模拟上滑操作*/ public void performScrollForward() {try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}performGlobalAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);}
/*** 查找对应文本的View** @param text text* @return View*/ public AccessibilityNodeInfo findViewByText(String text) {return findViewByText(text, false);}
/*** 查找对应文本的View** @param text text* @param clickable 该View是否可以点击* @return View*/ public AccessibilityNodeInfo findViewByText(String text, boolean clickable) {AccessibilityNodeInfo accessibilityNodeInfo = getRootInActiveWindow();if (accessibilityNodeInfo == null) {return null;}List
/*** 查找对应ID的View** @param id id* @return View*/@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2) public AccessibilityNodeInfo findViewByID(String id) {AccessibilityNodeInfo accessibilityNodeInfo = getRootInActiveWindow();if (accessibilityNodeInfo == null) {return null;}List
public void clickTextViewByText(String text) {AccessibilityNodeInfo accessibilityNodeInfo = getRootInActiveWindow();if (accessibilityNodeInfo == null) {return;}List
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2) public void clickTextViewByID(String id) {AccessibilityNodeInfo accessibilityNodeInfo = getRootInActiveWindow();if (accessibilityNodeInfo == null) {return;}List
/*** 模拟输入** @param nodeInfo nodeInfo* @param text text*/ public void inputText(AccessibilityNodeInfo nodeInfo, String text) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {Bundle arguments = new Bundle();arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, text);nodeInfo.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);nodeInfo.performAction(AccessibilityNodeInfo.ACTION_FOCUS);nodeInfo.performAction(AccessibilityNodeInfo.ACTION_PASTE);}}
@Override public void onAccessibilityEvent(AccessibilityEvent event) {}
@Override public void onInterrupt() {}}
四、QQ抢红包
(一)抢红包流程:
通知栏收到QQ的消息,发现是QQ红包,模拟点击消息进入聊天页面检索页面上的所有元素,发现有包含“点击拆开”的字眼,就模拟点击打开红包窗口一两秒后执行Back操作,关闭红包窗口。
继续等待消息来到。
(二)实现功能:
锁屏抢红包(不可以有密码或者图案之类的锁屏)
口令红包,自动输入口令并且发送抢完红包后,自动回复感谢语,可在红包设置里自行设置内容其他的功能就没继续往下做了,知道方法,其他都可能慢慢研究出来。
(三)抢红包辅助功能类,注释都写好了,很好理解,类中有用到QQConstant类,在第四点贴出了代码/*** 描述:QQ抢红包服务* 作者:卜俊文* 邮箱:344176791@qq.com* 日期:2017/11/6 上午9:25*/ public class EnvelopeService extends BaseAccessibilityService {
//锁屏、解锁相关private KeyguardManager.KeyguardLock kl;
//唤醒屏幕相关private PowerManager.WakeLock wl = null;
private long delayTime = 0;//延迟抢的时间
/*** 描述:所有事件响应的时候会回调* 作者:卜俊文* 邮箱:344176791@qq.com* 日期:2017/11/6 上午9:26*/@Override public void onAccessibilityEvent(AccessibilityEvent event) {
//验证抢红包的开关if (!invalidEnable()) {return;}
//事件类型int eventType = event.getEventType();
//获取包名CharSequence packageName = event.getPackageName();if (TextUtils.isEmpty(packageName)) {return;}
switch (eventType) {
//状态栏变化case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:
if (QQConstant.QQ_PACKAGE_NAME.equals(packageName)) {//处理状态栏上QQ的消息,如果是红包就跳转过去progressQQStatusBar(event);}break;
//窗口切换的时候回调case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED:if (QQConstant.QQ_PACKAGE_NAME.equals(packageName)) {//处理正在QQ聊天窗口页面,有其他群或者人有新的红包提醒,跳转过去。
progressNewMessage(event);//处理聊天页面的红包progressQQChat(event);}
break;}
}
/*** 描述:处理新消息* 作者:卜俊文* 邮箱:344176791@qq.com* 日期:2017/11/3 下午11:21*/ private void progressNewMessage(AccessibilityEvent event) {
if (event == null) {return;}AccessibilityNodeInfo source = event.getSource();if (source == null) {return;}//根据event的source里的text,来判断这个消息是否包含[QQ红包]的字眼,有的话就跳转过去CharSequence text = source.getText();if (!TextUtils.isEmpty(text) && text.toString().contains(QQConstant.QQ_ENVELOPE_KEYWORD)) {performViewClick(source);}}
/*** 描述:验证抢红包是否开启* 作者:卜俊文* 邮箱:344176791@qq.com* 日期:2017/11/3 下午4:57*/ private boolean invalidEnable() {return SettingConfig.getInstance().getReEnable();}
/*** 描述:处理QQ状态栏* 作者:卜俊文* 邮箱:344176791@qq.com* 日期:2017/11/1 下午1:49*/ public void progressQQStatusBar(AccessibilityEvent event) {List
/*** 描述:处理QQ聊天红包* 作者:卜俊文* 邮箱:344176791@qq.com* 日期:2017/11/1 下午1:56*/ public void progressQQChat(AccessibilityEvent event) {
if (TextUtils.isEmpty(event.getClassName())) {return;//如果当前页面是聊天页面或者当前的描述信息是"返回消息界面",就肯定是对话页面}
//验证当前事件是否符合查询页面上的红包if (!invalidEnvelopeUi(event)) {return;}
//延迟点击红包,防止被检测到开了抢红包,不过感觉还是感觉会被检测到,应该有的效果吧...
try {Thread.sleep(delayTime);} catch (InterruptedException e) {e.printStackTrace();}
//普通红包,检索点击拆开的字眼。
List
//口令红包,检索口令红包的字眼。
List
/*** 描述:验证是否现在是在聊天页面,可以进行抢红包处理* 作者:卜俊文* 邮箱:344176791@qq.com* 日期:2017/11/3 上午11:52** @param event*/
public boolean invalidEnvelopeUi(AccessibilityEvent event) {
//判断类名是否是聊天页面if (!QQConstant.QQ_IM_CHAT_ACTIVITY.equals(event.getClassName().toString())) {return true;}
//判断页面中的元素是否有点击拆开的文本,有就返回可以进行查询了int recordCount = event.getRecordCount();if (recordCount > 0) {for (int i = 0; i < recordCount; i++) {AccessibilityRecord record = event.getRecord(i);if (record == null) {break;}List
/*** 回到系统桌面*/ private void back2Home(int time) {try {Thread.sleep(time);} catch (InterruptedException e) {e.printStackTrace();}Intent home = new Intent(Intent.ACTION_MAIN);home.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);home.addCategory(Intent.CATEGORY_HOME);startActivity(home);}
/*** 描述:处理普通红包* 作者:卜俊文* 邮箱:344176791@qq.com* 日期:2017/11/1 下午5:02*/ public void progressNormal(List
//点击拆开红包performViewClick(accessibilityNodeInfo);
//回复感谢信息,根据配置文件中配置的回复信息回复String reReplyMessage = SettingConfig.getInstance().getReReplyMessage();if (!TextUtils.isEmpty(reReplyMessage)) {replyMessage(reReplyMessage);}}}//最后延迟事件触发返回事件,关闭红包页面performBackClick(1200);}}
/*** 描述:处理口令红包* 作者:卜俊文* 邮箱:344176791@qq.com* 日期:2017/11/1 下午4:58** @param passwordList*/ public void progressPassword(List
//2. 填写口令到编辑框上然后进行发送replyMessage(key);
//返回,关闭红包页面performBackClick(1200);}}}}}}
/*** 唤醒屏幕并解锁权限*
/*** 描述:回复消息,无延迟* 作者:卜俊文* 邮箱:344176791@qq.com* 日期:2017/11/3 下午5:10*/ public void replyMessage(String key) {replyMessage(key, 0);}
/*** 描述:回复消息* 作者:卜俊文* 邮箱:344176791@qq.com* 日期:2017/11/3 下午5:10*/ public void replyMessage(String key, int time) {
//延迟if (time > 0) {try {Thread.sleep(time);} catch (InterruptedException e) {e.printStackTrace();}}
//获取QQ聊天页面输入框AccessibilityNodeInfo chat_edit = findViewByID(QQConstant.QQ_CHAT_MESSAGE_INPUT);if (chat_edit != null) {
//把口令粘贴到输入框中pastaText(chat_edit, MyApp.context, key);
//获取QQ聊天页面发送消息按钮AccessibilityNodeInfo sendMessage = findViewByID(QQConstant.QQ_CHAT_MESSAGE_SEND);
//然后就按下发送按钮if (sendMessage != null && Button.class.getName().equals(sendMessage.getClassName())) {performViewClick(sendMessage);}}
}
@Override public void onInterrupt() {}
@Override protected void onServiceConnected() {super.onServiceConnected();
// 获取电源管理器对象PowerManager pm = (PowerManager) MyApp.context.getSystemService(Context.POWER_SERVICE);// 获取PowerManager.WakeLock对象,后面的参数|表示同时传入两个值,最后的是调试用的Tag wl = pm.newWakeLock(PowerManager.ACQUIRE_CAUSES_WAKEUP| PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "bright");
KeyguardManager km = (KeyguardManager) MyApp.context.getSystemService(Context.KEYGUARD_SERVICE);kl = km.newKeyguardLock("unLock");
//初始化屏幕的监听ScreenListener screenListener = new ScreenListener(MyApp.context);screenListener.begin(new ScreenListener.ScreenStateListener() {@Override public void onScreenOn() {Log.e("ScreenListener", "屏幕打开了");}
@Override public void onScreenOff() {//在屏幕关闭的时候,进行锁屏,不执行的话,锁屏就失效了,因为要实现锁屏状态下也可以进行抢红包。
Log.e("ScreenListener", "屏幕关闭了");if (kl != null) {kl.disableKeyguard();kl.reenableKeyguard();}}
@Override public void onUserPresent() {Log.e("ScreenListener", "解锁了");}});}
@Override public void onDestroy() {super.onDestroy();}}
(四)QQ辅助服务里有用到的常量public class QQConstant {
//QQ的应用包名public static final String QQ_PACKAGE_NAME = "com.tencent.mobileqq";
//状态栏红包关键字public static final String QQ_ENVELOPE_KEYWORD = "[QQ红包]";
//QQ聊天页面public static final String QQ_IM_CHAT_ACTIVITY = "com.tencent.mobileqq.activity.SplashActivity";
//点击拆开public static final String QQ_CLICK_TAKE_APART = "点击拆开";
//口令红包public static final String QQ_CLICK_PASSWORD_DIALOG = "口令红包";
//聊天页面,输入框ID public static final String QQ_CHAT_MESSAGE_INPUT = "com.tencent.mobileqq:id/input";
//聊天页面,发送按钮public static final String QQ_CHAT_MESSAGE_SEND = "com.tencent.mobileqq:id/fun_btn";
}
五、红包问题
用的时候偶尔会被QQ检测到用了红包插件,可能是因为抢的速度太快,导致数据不符合正常的点击时间,我有加入一个延迟时间,不知道有没有效果,如果有知道的也可以留言,谢谢。
在QQ的主页面上,收到消息的时候通知栏是不会通知的,所以这里不能进行解析通知栏跳转聊天页面,没有找到什么元素可以告诉我怎么进入红包的聊天页面,如果有知道的可以留言,谢谢。
这种辅助服务的方式抢红包,进入聊天页面后,他检索字段只会检索当前页面可视的元素,某些红包要是在聊天记录上面看不见的,需要滑动上去才可以触发解析红包,不过一般不会一次性10个红包都发出来吧,嘿嘿。
六、总结
学习制作了这个项目,也了解了辅助功能的使用,感觉这个还是可以做很多东西的,上面已经贴出了核心代码,要源码的可以点击。
抢红包源码以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。
您可能感兴趣的文章:教你一步步实现Android微信自动抢红包Android辅助功能AccessibilityService与抢红包辅助Android实现QQ抢红包插件Android中微信抢红包插件原理解析及开发思路Android抢红包插件实现原理浅析Android实现红包雨动画效果分享Android微信红包插件Android实现微信自动抢红包的程序Android微信自动抢红包插件优化和实现SurfaceView实现红包雨平移动画
