Introduction to the
This paper introduces the implementation method of wechat automatic red envelope snatching, mainly to achieve the following functions:
- Automatically open the red envelope that appears on the screen
- Automatically enter the chat interface and open the red envelope when receiving the red envelope information when you are in the desktop or chat list
- Log function, record the detailed log of snatching red packets
Realize the principle of
- AccessibilityService is used to monitor the content of the screen and realize the purpose of automatically opening red envelopes.
- Use the ActiveAndroid database to simply record red envelope log
- Use preference to record monitoring options
The final interface
Grab red envelope core code
AccessibilityService configuration
Android: accessibilityEventTypes set the trigger to monitor the callback event types; Android :packageNames Set the application to monitor. Here we monitor wechat, so fill in the package name of wechat com.tencent. Mm
Copy the code
Declare in androidmanifest.xml:
Copy the code
Grab the red envelope code
The AccessibilityEvent sent by the receiving system
Private static final String GET_RED_PACKET = ""; Private static final String CHECK_RED_PACKET = "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 = "red envelope detail "; Private static final String RED_PACKET_SAVE = ""; Private static final String RED_PACKET_NOTIFICATION = "[微信 red]"; @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. (" Opening!" ); } /* If (mNeedUnpack && (mUnpackNode! = null)) { AccessibilityNodeInfo cellNode = mUnpackNode; cellNode.performAction(AccessibilityNodeInfo.ACTION_CLICK); mNeedUnpack = false; L.d. (" Collecting!" ); } if (mNeedBack) { performGlobalAction(GLOBAL_ACTION_BACK); mMutex = false; mNeedBack = false; L.d. (" Returning!" ); If (isGetMoney) {t.howshort (this, "get a red envelope:" + gotMoney + "yuan! ); totalMoney = totalMoney + gotMoney; totalSuccessNum++; myPrefs.totalMoney().put(totalMoney); myPrefs.successNum().put(totalSuccessNum); L.d("totalMoney: " + totalMoney); L.d("totalSuccessNum: " + totalSuccessNum); saveToLog(hongbaoInfo); isGetMoney = false; }}}Copy the code
Detects node information for listening events
private void checkNodeInfo() { L.d("checkNodeInfo!" ); if (this.rootNodeInfo == null) return; /* Chat window, Traverse the nodes matching "to receive a red envelope" and "see red envelope" * / List nodes1 = this. FindAccessibilityNodeInfosByTexts (enclosing 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. The LinearLayout "equals (targetNode. The getParent (). The getClassName ())) / / avoid external failure caused by text disturbance {the if (this.signature.generateSignature(targetNode)) { mLuckyMoneyReceived = true; mReceiveNode = targetNode; L.d("signature:" + this.signature.toString()); } } else { L.d("this is text"); } return; } the List nodes2 = this. FindAccessibilityNodeInfosByTexts (enclosing rootNodeInfo, new String [] {" open red envelope "}); if (! nodes2.isEmpty()) { L.d("node2 ! = null"); for (AccessibilityNodeInfo nodeInfo : nodes2) { if (nodeInfo.getClassName().equals("android.widget.Button")) nodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK); }} else {/ * poke open red envelopes, red packets haven't rob, traverse the nodes matching "red envelopes" * / AccessibilityNodeInfo 2 = (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; }} /* Open the red envelope, red envelope has been robbed, Traverse the nodes matching "has been deposited in the change" and "both" * / if (mLuckyMoneyPicked) {List nodes3 = this. FindAccessibilityNodeInfosByTexts (enclosing 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; }}}Copy the code
- It mainly determines whether there is a new red envelope by detecting key text information such as “get red envelope”
- When received a red envelope to determine whether “android. Widget. LinearLayout”, a text chat information interference shielding
- When opening a red envelope, because the wechat version may be different, two judgments are made at the same time to be compatible with part of the version
- After opening the red envelope, you need to return it automatically. There are the following situations: grabbed, slow, and the red envelope is your own red envelope
Here is the code to listen to the chat list:
private boolean watchList(AccessibilityEvent event) { // Not a message if (event.getEventType() ! = AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED || event.getSource() == null) return false; List 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; }Copy the code
Here is the code to listen for notifications:
private boolean watchNotifications(AccessibilityEvent event) { // Not a notification if (event.getEventType() ! = AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED) return false; // Not a hongbao String 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; }Copy the code
Red envelope information acquisition, and log storage
By obtaining the sub-information of the node, the sender of the red envelope, the amount captured and the time of the red envelope were obtained, and a simple form was established to record the information.
@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+" month "+day+" day "+hour+":"+min+":"+ SEC; } @Override public String toString() { return "HongbaoInfo [sender=" + sender + ", money=" + money + ", time=" + time + "]"; } public static List 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; }}Copy the code
Storage operation:
private void saveToLog(HongbaoInfo hongbaoInfo) { if (watchedFlags.get("pref_etc_log")) { HongbaoInfo hongbaoInfo1 = new HongbaoInfo(); hongbaoInfo1 = hongbaoInfo; hongbaoInfo1.save(); } else { L.d("log closed!" ); }}Copy the code
conclusion
The main code is basically finished here. Now the test is ok on the latest version of wechat, but there are still the following problems:
- The same person continuous hair can not be automatically robbed, because in order to prevent repeated click filtering, the same person’s red envelope robbed will not click again
- The AccessibilityService may be disabled after being enabled for a long time
Wechat automatic red envelope grab APK source code