Android微信抢红包功能实现原理
利用手机自带的辅助服务(无障碍模式),监听手机的动态 : AccessibilityService
1、如果通知栏有[微信红包]的消息来,就会自动截取并模拟人手点击通知栏--进入微信聊天界面
2、在聊天界面匹配[查看红包、领取红包]字样的信息,再模拟人手点击
3、如果可以点击(也就是说不是文字,那就是红包)
4、匹配[开]按钮(以前是“拆”),如果有这个button,就模拟人手点击,进入下一界面
5、开之后就出现两种界面:匹配“已存入零钱”的textview有结果的话就说明抢到了红包,否则匹配“手慢了”的textview就说明没有抢到红包
6、如果有红包就找到“已存入零钱”前面的”元“的前一个位置,这个位置上就是红包钱数的textview
7、当然,打开界面后要返回
问题:
1、如何判断红包是否已经被打开?
通过红包的Signature获得到红包的:发红包人名、留言、时间;通过这三个信息判定是否已经领过红包。这里又会出现两个问题:
a、微信每条信息的时间只会精确到分,不会精确到秒,所以同一分钟内同一个人发送的同一留言的红包就不能判定唯一
b、并不是每条信息都会获取到时间,有许多时间获得到是:unknownTime吗,也就是没有获取到时间,这更增加了标志的困难性
2、如何计算抢到红包的准确钱数?
这个问题与上一个问题是联系在一起的,当不能判定是否为抢过的红包情况下,可能会重复打开已经抢过的红包,这样--记录过的红包钱数就会被再记录一次 ......
3、如何在锁屏的情况下实现抢红包?
手机一旦锁屏,关于屏幕上的一切信息我们都无从得到,但是后台的服务是一直在运行的,所以我们只要用服务一直坚挺通知栏是否有[微信红包]等字样的信息来,如果检测到有此类信息,我们只要将屏幕点亮,再启动监听聊天界面的服务就可以获取到红包了。那么,问题来了?怎么点亮屏幕?---待会有代码奉上。
4、可以自动回复吗?
肯定是可以的,只要在正确的实际发送触发聊天界面上的“发送”按钮,就能将你想回复的感谢语发送出去了
核心代码
/**
* Created by rinzz08 on 17/2/16.
*
* 抢红包服务类
*/public class RedPacketService extends AccessibilityService {
/**
* 必须重写的方法:此方法用了接受系统发来的event。在你注册的event发生是被调用。在整个生命周期会被调用多次。
*/
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
}
/**
* 必须重写的方法:系统要中断此service返回的响应时会调用。在整个生命周期会被调用多次。
*/
@Override
public void onInterrupt() {
Toast.makeText(this, “中断了—–”, Toast.LENGTH_SHORT).show();
}
/**
* 服务已连接
*/
@Override
protected void onServiceConnected() {
Toast.makeText(this, “开启抢红包服务”, Toast.LENGTH_SHORT).show();
super.onServiceConnected();
}
/**
* 服务已断开
*/
@Override
public boolean onUnbind(Intent intent) {
Toast.makeText(this, “关闭抢红包服务已被”, Toast.LENGTH_SHORT).show();
return super.onUnbind(intent);
}
}
像这类重写AccessibilityService类的服务代码晚上多得去了,下面主要介绍一下重点功能实现的地方,代码确实有点low,功能确实是实现了…..
获取抢到红包的钱数
/**
* 遍历查找文本框(这里主要获取钱数)
*
* @param node
* @return
*/
private AccessibilityNodeInfo findOpenTextView(AccessibilityNodeInfo node) {float allHong = RinnzSharedUtil.getFloat(MyApp.getContext(), SharedConstants.HISTORY_MONEY, 0.0f); float todayHong = RinnzSharedUtil.getFloat(MyApp.getContext(), SharedConstants.TODAY_MONEY, 0.0f); List<String> texts = new ArrayList<>(); if (node == null) return null; //非layout元素 if (node.getChildCount() == 0) { if ("android.widget.TextView".equals(node.getClassName())) return node; else return null; } //layout元素,遍历找文本框 AccessibilityNodeInfo textView; for (int i = 0; i < node.getChildCount(); i++) { textView = findOpenTextView(node.getChild(i)); if (textView != null) if (textView.getText() != null) { String textName = textView.getText().toString(); texts.add(textName); // Log.i(TAG, "findOpenTextView_text: " + textName); if (textName.contains("已存入零钱")) { //说明抢到一个红包,给本地一个信息 int hongbaoNum = RinnzSharedUtil.getInt(MyApp.getContext(), SharedConstants.HONGBAO_NUM); RinnzSharedUtil.putInt(MyApp.getContext(), SharedConstants.HONGBAO_NUM, hongbaoNum + 1); //在这里就去截取钱数所在的文本框吧(getText().toString();) } } //return textView; } return null; }
自动回复
如果有自动回复的功能,只要在抢红包的时候在找到“开”的时候做一个标记,然后返回到聊天界面的时候检测编辑框,然后给编辑框赋值,再找到“发送”按钮,再触发按钮就OK了
/**
* 遍历查找EditText
*
* @param node
* @return
*/
private AccessibilityNodeInfo findOpenEditText(AccessibilityNodeInfo node) {
Log.i(TAG, “findOpenButton: 找到编辑框了”);
if (node == null)
return null;//非layout元素 if (node.getChildCount() == 0) { if ("android.widget.EditText".equals(node.getClassName())) return node; else return null; } //layout元素,遍历找EditText AccessibilityNodeInfo editText; for (int i = 0; i < node.getChildCount(); i++) { editText = findOpenEditText(node.getChild(i)); if (button != null) { return editText; } } return null; }
回复
private void autoReponse() {
AccessibilityNodeInfonodeInfo=findOpenEditText(this.rootNodeInfo);
// Log.i(TAG, "autoReponse: 有个编辑框"); isInput++; if (isInput == 1) { Bundle arguments = new Bundle(); String autoSend = RinnzSharedUtil.getString(MyApp.getContext(), SharedConstants.AUTO_RESPONSE_TEXT); if (autoSend != null) { arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, autoSend); nodeInfo.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments); //编辑了文本需要点击发送 send(); } else { arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, "谢谢老板"); nodeInfo.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments); //编辑了文本需要点击发送 send(); } return; } } }
发送按钮
/**
* 寻找窗体中的“发送”按钮,并且点击。@SuppressLint("NewApi") private void send() { AccessibilityNodeInfo nodeInfo = getRootInActiveWindow(); if (nodeInfo != null) { List<AccessibilityNodeInfo> list = nodeInfo .findAccessibilityNodeInfosByText("发送"); if (list != null && list.size() > 0) { for (AccessibilityNodeInfo n : list) { n.performAction(AccessibilityNodeInfo.ACTION_CLICK); } } else { List<AccessibilityNodeInfo> liste = nodeInfo .findAccessibilityNodeInfosByText("Send"); if (liste != null && liste.size() > 0) { for (AccessibilityNodeInfo n : liste) { n.performAction(AccessibilityNodeInfo.ACTION_CLICK); } } } } }
屏蔽不想抢的红包
这个就简单了,只要在聊天界面检测发来红包的Signature值,这个值有三个信息(用户名、留言信息、时间),然后将你屏蔽的文字与这个红包的Signature值是否匹配,匹配的话直接过滤就行了,这里就补粘贴代码了
手机锁屏依旧抢红包
/**
*唤醒屏幕:这里要注意的是手机如何设置了锁屏密码,那么这个功能就不能唤醒了,怎么唤醒?撬了手机锁屏?呵呵哒。只要一直用服务判断通知栏是否有红包信息来就行了,有信息来直接唤醒,其他的事就不用你做了
*/
public static void wakeUpAndUnlock(){//屏锁管理器 KeyguardManager km= (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE); KeyguardManager.KeyguardLock kl = km.newKeyguardLock("unLock"); //解锁 kl.disableKeyguard(); //获取电源管理器对象 PowerManager pm=(PowerManager) mContext.getSystemService(Context.POWER_SERVICE); //获取PowerManager.WakeLock对象,后面的参数|表示同时传入两个值,最后的是LogCat里用的Tag PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.SCREEN_DIM_WAKE_LOCK,"bright"); //点亮屏幕 wl.acquire(); //释放 wl.release(); }
第一次写CSDN,写的是乱七八糟,见谅见谅。有问题请多多指教!