抢红包核心代码
AccessibilityService配置android:accessibilityEventTypes
设置触发监听回调的事件类型;android:packageNames
设置监听的应用,这里监听的是微信,因此填上微信的包名com.tencent.mm
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"android:accessibilityEventTypes="typeNotificationStateChanged|typeWindowStateChanged|typeWindowContentChanged" android:accessibilityFeedbackType="feedbackGeneric" android:accessibilityFlags="flagDefault" android:canRetrieveWindowContent="true" android:description="@string/accessibility_description" android:notificationTimeout="100" android:packageNames="com.tencent.mm" android:settingsActivity="com.oden.annotations.app.activity.ManActivity" />在AndroidManifest.xml中声明:
<service android:name=".app.service.HongbaoService_" android:enabled="true" android:exported="true" android:label="@string/app_name" android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE" > <intent-filter><action android:name="android.accessibilityservice.AccessibilityService" /> </intent-filter> <meta-dataandroid:name="android.accessibilityservice"android:resource="@xml/accessibility_service_config" /></service>抢红包实现代码
AccessibilityEvent
private static final String GET_RED_PACKET = "领取红包"; private static final String CHECK_RED_PACKET = "查看红包"; private static final String RED_PACKET_PICKED = "手慢了,红包派完了"; private static final String RED_PACKET_PICKED2 = "手气"; private static final String RED_PACKET_PICKED_DETAIL = "红包详情"; private static final String RED_PACKET_SAVE = "已存入零钱"; private static final String RED_PACKET_NOTIFICATION = "[微信红包]"; @Override public void onAccessibilityEvent(AccessibilityEvent event) {L.d("RECEIVE EVENT!");if (watchedFlags == null) return; /* 检测通知消息 */if (!mMutex) { if (watchedFlags.get("pref_watch_notification") && watchNotifications(event)) return; if (watchedFlags.get("pref_watch_list") && watchList(event)) return;}if (!watchedFlags.get("pref_watch_chat")) return;this.rootNodeInfo = event.getSource();if (rootNodeInfo == null) return;mReceiveNode = null;mUnpackNode = null;checkNodeInfo(); /* 如果已经接收到红包并且还没有戳开 */if (mLuckyMoneyReceived && !mLuckyMoneyPicked && (mReceiveNode != null)) { mMutex = true; AccessibilityNodeInfo cellNode = mReceiveNode; cellNode.getParent().performAction(AccessibilityNodeInfo.ACTION_CLICK); mLuckyMoneyReceived = false; mLuckyMoneyPicked = true; L.d("正在打开!");} /* 如果戳开但还未领取 */if (mNeedUnpack && (mUnpackNode != null)) { AccessibilityNodeInfo cellNode = mUnpackNode; cellNode.performAction(AccessibilityNodeInfo.ACTION_CLICK); mNeedUnpack = false; L.d("正在领取!");}if (mNeedBack) { performGlobalAction(GLOBAL_ACTION_BACK); mMutex = false; mNeedBack = false; L.d("正在返回!"); //总次数和金额统计 if (isGetMoney) {T.showShort(this, "抢到一个红包: " + gotMoney + "元!");totalMoney = totalMoney + gotMoney;totalSuccessNum++;myPrefs.totalMoney().put(totalMoney);myPrefs.successNum().put(totalSuccessNum);L.d("totalMoney: " + totalMoney);L.d("totalSuccessNum: " + totalSuccessNum);saveToLog(hongbaoInfo);isGetMoney = false; }} }检测监听事件的节点信息
private void checkNodeInfo() {L.d("checkNodeInfo!");if (this.rootNodeInfo == null) return; /* 聊天会话窗口,遍历节点匹配“领取红包”和"查看红包" */List<AccessibilityNodeInfo> nodes1 = this.findAccessibilityNodeInfosByTexts(this.rootNodeInfo, new String[]{GET_RED_PACKET, CHECK_RED_PACKET});if (!nodes1.isEmpty()) {L.d("!nodes1.isEmpty()"); AccessibilityNodeInfo targetNode = nodes1.get(nodes1.size() - 1); if ("android.widget.LinearLayout".equals(targetNode.getParent().getClassName()))//避免被文字干扰导致外挂失效 {if (this.signature.generateSignature(targetNode)) { mLuckyMoneyReceived = true; mReceiveNode = targetNode; L.d("signature:" + this.signature.toString());} } else {L.d("this is text"); } return;}List<AccessibilityNodeInfo> nodes2 = this.findAccessibilityNodeInfosByTexts(this.rootNodeInfo, new String[]{"拆红包"});if (!nodes2.isEmpty()) { L.d("node2 != null"); for (AccessibilityNodeInfo nodeInfo : nodes2) { if (nodeInfo.getClassName().equals("android.widget.Button"))nodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK); }} else {/* 戳开红包,红包还没抢完,遍历节点匹配“拆红包” */ AccessibilityNodeInfo node2 = (this.rootNodeInfo.getChildCount() > 3) ? this.rootNodeInfo.getChild(3) : null; if (node2 != null && node2.getClassName().equals("android.widget.Button")) {mUnpackNode = node2;mNeedUnpack = true;isToGetMoney = true;L.d("find red packet!");return; }} /* 戳开红包,红包已被抢完,遍历节点匹配“已存入零钱”和“手慢了” */if (mLuckyMoneyPicked) { List<AccessibilityNodeInfo> nodes3 = this.findAccessibilityNodeInfosByTexts(this.rootNodeInfo, new String[]{ RED_PACKET_PICKED, RED_PACKET_SAVE, RED_PACKET_PICKED2, RED_PACKET_PICKED_DETAIL}); if (!nodes3.isEmpty()) {L.d("!nodes3.isEmpty()");if (rootNodeInfo.getChildCount() > 1) { L.d("RED_PACKET_PICKED!");} else { L.d("nodes3.get(0).toString(): " + nodes3.get(0).getText().toString()); if (!nodes3.get(0).getText().toString().equals(RED_PACKET_PICKED_DETAIL)) {AccessibilityNodeInfo targetNode = nodes3.get(nodes3.size() - 1);hongbaoInfo.getInfo(targetNode);if (isToGetMoney) { isGetMoney = true; isToGetMoney = false; gotMoney = hongbaoInfo.getMoney(); L.d("gotMoney: " + gotMoney);}L.d("RED_PACKET_SAVE!");L.d("hongbaoInfo: " + hongbaoInfo.toString()); } else {L.d("this packet is myself!"); }}mNeedBack = true;mLuckyMoneyPicked = false; }} }主要通过检测“领取红包”等关键文字信息来判断是否有新红包
android.widget.LinearLayout
",屏蔽聊天信息中的文字干扰private boolean watchList(AccessibilityEvent event) {// Not a messageif (event.getEventType() != AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED || event.getSource() == null) return false;List<AccessibilityNodeInfo> nodes = event.getSource().findAccessibilityNodeInfosByText(RED_PACKET_NOTIFICATION);if (!nodes.isEmpty()) { AccessibilityNodeInfo nodeToClick = nodes.get(0); CharSequence contentDescription = nodeToClick.getContentDescription(); if (contentDescription != null && !lastContentDescription.equals(contentDescription)) {nodeToClick.performAction(AccessibilityNodeInfo.ACTION_CLICK);lastContentDescription = contentDescription.toString();return true; }}return false; }下面是监听通知信息的代码:
private boolean watchNotifications(AccessibilityEvent event) {// Not a notificationif (event.getEventType() != AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED) return false;// Not a hongbaoString tip = event.getText().toString();if (!tip.contains(RED_PACKET_NOTIFICATION)) return true;Parcelable parcelable = event.getParcelableData();if (parcelable instanceof Notification) { Notification notification = (Notification) parcelable; try {notification.contentIntent.send(); } catch (PendingIntent.CanceledException e) {e.printStackTrace(); }}return true; }红包信息的获取,及日志的存储
@Table(name = "HongbaoInfos")public class HongbaoInfo extends Model { private int month; private int day; private int hour; private int min; private int sec; @Column(name = "sender") public String sender; @Column(name = "money") public String money; @Column(name = "time") public String time; public void getInfo(AccessibilityNodeInfo node) {AccessibilityNodeInfo hongbaoNode = node.getParent();sender = hongbaoNode.getChild(0).getText().toString();money = hongbaoNode.getChild(2).getText().toString();time = getStringTime(); } private String getStringTime() {Calendar c = Calendar.getInstance();month = c.get(Calendar.MONTH) + 1;day = c.get(Calendar.DAY_OF_MONTH);hour = c.get(Calendar.HOUR_OF_DAY);min = c.get(Calendar.MINUTE);sec = c.get(Calendar.SECOND);return month+"月"+day+"日 "+hour+":"+min+":"+sec; } @Override public String toString() {return "HongbaoInfo [sender=" + sender + ", money=" + money + ", time=" + time + "]"; } public static List<HongbaoInfo> getAll() {return new Select().from(HongbaoInfo.class).orderBy("Id ASC").execute(); } public static void deleteALL() {new Delete().from(HongbaoInfo.class).execute(); } public float getMoney() {return Float.parseFloat(money); } public String getSender() {return sender; } public String getTime() {return time; }}存储操作:
private void saveToLog(HongbaoInfo hongbaoInfo) {if (watchedFlags.get("pref_etc_log")) { HongbaoInfo hongbaoInfo1 = new HongbaoInfo(); hongbaoInfo1 = hongbaoInfo; hongbaoInfo1.save();} else { L.d("log closed!");} }总结
AccessibilityService
开启时间长后有时会被系统关掉