微信抢红包插件项目

前言:转载请注明出处:http://blog.csdn.net/hejjunlin/article/details/52116904

红包精灵:https://github.com/hejunlin2013/RedPackage

/*
**        RedPackage Project
**
** Copyright(c) 2016 marsXTU 
   
   
    
    
**
** This file is part of RedPackage.
**
** RedPackage is free software: you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License as published by the Free Software Foundation, either
** version 3 of the License, or (at your option) any later version.
**
** RedPackage is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
** Lesser General Public License for more details.
**
** You should have received a copy of the GNU Lesser General Public
** License along with RedPackage.  If not, see 
    
    
     
     
**
**/
package com.marsXTU.qianghongbao;

import java.util.ArrayList;
import java.util.List;

import android.accessibilityservice.AccessibilityService;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.Build;
import android.util.Log;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.Toast;

/**
 * @author marsXTU(hejunlin2013@gmail.com)
 */
@SuppressLint("NewApi")
public class GrabHongBaoService extends AccessibilityService {
	
	private static final String WECHAT_DETAILS_EN = "Details";
	private static final String WECHAT_DETAILS_CH = "红包详情";
	private static final String WECHAT_BETTER_LUCK_EN = "Better luck next time!";
	private static final String WECHAT_BETTER_LUCK_CH = "手慢了";
	private static final String WECHAT_OPEN_EN = "Open";
	private static final String WECHAT_OPENED_EN = "You've opened";
	private static final String WECHAT_OPEN_CH = "拆红包";
	private static final String WECHAT_VIEW_SELF_CH = "查看红包";
	private static final String WECHAT_VIEW_OTHERS_CH = "领取红包";
	private static final String WECHAT_NOTIFICATION_TIP = "[微信红包]";
	
    private List
     
     
      
       mReceiveNodeList; 
    private List
      
      
       
        mUnpackNodeList;
    private static final int MAX_CACHE_TOLERANCE = 5000;
	private static final String TAG = "GrabHongBaoService";
    private AccessibilityNodeInfo rootNodeInfo;
    
    private boolean mLuckyMoneyPicked;
    private boolean mLuckyMoneyReceived;
    private boolean mNeedUnpack;
    private boolean mNeedBack;
    private boolean mCycle = false;
    private boolean isClicked = false;
    private String lastFetchedHongbaoId = null;
    private long lastFetchedTime = 0;

    /**
     * AccessibilityEvent的回调方法
     *
     * @param event 事件
     */
    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
    	 Log.d(TAG, "事件---->onAccessibilityEvent " + event);
        /* 检测通知消息 */
        if (event.getEventType() == AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED && !mCycle) {
        	 List
       
       
         texts = event.getText(); if(!texts.isEmpty()) { for(CharSequence t : texts) { String text = String.valueOf(t); Log.d(TAG, "事件----> notifychanged " + text); if(text.contains(WECHAT_NOTIFICATION_TIP)) { isClicked = false; openNotify(event); break; } } } // return; } else if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED && !mCycle) { Log.i(TAG, "startGrapHongBao() "); startGrapHongBao(event); } } private void startGrapHongBao(AccessibilityEvent event) { Log.d(TAG, "事件----> start startGrapHongBao " + event); this.rootNodeInfo = event.getSource(); if (rootNodeInfo == null) return; mReceiveNodeList = null; mUnpackNodeList = null; checkNodeInfo(); /* 如果已经接收到红包并且还没有戳开 */ if (mLuckyMoneyReceived && !mLuckyMoneyPicked && (mReceiveNodeList != null)) { int size = mReceiveNodeList.size(); Log.d(TAG, "事件----> performAction 节点数目 " + mReceiveNodeList.size()); if (size > 0) { Log.d(TAG, "事件----> start 已经接收到红包并且还没有戳开 "); String id = getHongbaoText(mReceiveNodeList.get(size - 1)); long now = System.currentTimeMillis(); if (this.shouldReturn(id, now - lastFetchedTime)) return; mCycle = true; lastFetchedHongbaoId = id; lastFetchedTime = now; AccessibilityNodeInfo cellNode = mReceiveNodeList.get(size - 1); cellNode.getParent().performAction(AccessibilityNodeInfo.ACTION_CLICK); Log.d(TAG, "事件----> end 已经接收到红包并且还没有戳开 click"); mLuckyMoneyReceived = false; mLuckyMoneyPicked = true; } } /* 如果戳开但还未领取 */ if (mNeedUnpack && (mUnpackNodeList != null)) { Log.d(TAG, "事件----> start performAction 戳开但还未领取"); int size = mUnpackNodeList.size(); if (size > 0) { AccessibilityNodeInfo cellNode = mUnpackNodeList.get(size - 1); cellNode.performAction(AccessibilityNodeInfo.ACTION_CLICK); mNeedUnpack = false; } Log.d(TAG, "事件----> end performAction 戳开但还未领取"); } if (mNeedBack) { Log.d(TAG, "事件----> start performAction back"); performGlobalAction(GLOBAL_ACTION_BACK); mCycle = false; mNeedBack = false; Log.d(TAG, "事件----> end performAction back"); } // new Handler().postDelayed(new Runnable() { // @Override // public void run() { // playSounds(); // } // }, 0); Log.d(TAG, "事件----> end startGrapHongBao " + event); } private void playSounds() { MediaPlayer player; player = MediaPlayer.create(this, R.raw.shake_match); player.setLooping(false); player.start(); } /** 打开通知栏消息*/ @TargetApi(Build.VERSION_CODES.JELLY_BEAN) private void openNotify(AccessibilityEvent event) { if(event.getParcelableData() == null || !(event.getParcelableData() instanceof Notification)) { return; } //将微信的通知栏消息打开 Notification notification = (Notification) event.getParcelableData(); PendingIntent pendingIntent = notification.contentIntent; Log.d(TAG, "事件----> 打开通知栏消息 " + event); try { pendingIntent.send(); } catch (PendingIntent.CanceledException e) { e.printStackTrace(); } } @Override public void onInterrupt() { } /** * 检查节点信息 */ @TargetApi(Build.VERSION_CODES.JELLY_BEAN) private void checkNodeInfo() { Log.d(TAG, "事件----> start checkNodeInfo()!"); if (this.rootNodeInfo == null) return; /* 聊天会话窗口,遍历节点匹配“领取红包”和"查看红包" */ List 
        
          nodes1 = this.findAccessibilityNodeInfosByTexts( this.rootNodeInfo, new String[]{ WECHAT_VIEW_OTHERS_CH, WECHAT_VIEW_SELF_CH }); if (!nodes1.isEmpty()) { String nodeId = Integer.toHexString(System.identityHashCode(this.rootNodeInfo)); if (!nodeId.equals(lastFetchedHongbaoId)) { mLuckyMoneyReceived = true; mReceiveNodeList = nodes1; } return; } /* 戳开红包,红包还没抢完,遍历节点匹配“拆红包” */ List 
         
           nodes2 = this.findAccessibilityNodeInfosByTexts( this.rootNodeInfo, new String[]{ WECHAT_OPEN_CH, WECHAT_OPEN_EN }); if (!nodes2.isEmpty()) { mUnpackNodeList = nodes2; mNeedUnpack = true; return; } /* 戳开红包,红包已被抢完,遍历节点匹配“红包详情”和“手慢了” */ if (mLuckyMoneyPicked) { List 
          
            nodes3 = this.findAccessibilityNodeInfosByTexts( this.rootNodeInfo, new String[]{ WECHAT_BETTER_LUCK_CH, WECHAT_DETAILS_CH, WECHAT_BETTER_LUCK_EN, WECHAT_DETAILS_EN }); if (!nodes3.isEmpty()) { mNeedBack = true; mLuckyMoneyPicked = false; } } Log.d(TAG, "事件----> end checkNodeInfo()!"); } /** * 将节点对象的id和红包上的内容合并 * 用于表示一个唯一的红包 * * @param node 任意对象 * @return 红包标识字符串 */ private String getHongbaoText(AccessibilityNodeInfo node) { /* 获取红包上的文本 */ String content; try { AccessibilityNodeInfo i = node.getParent().getChild(0); content = i.getText().toString(); } catch (NullPointerException npe) { return null; } Log.d(TAG, "事件----> start getHongbaoText() " + content); return content; } /** * 批量化执行AccessibilityNodeInfo.findAccessibilityNodeInfosByText(text). * 由于这个操作影响性能,将所有需要匹配的文字一起处理,尽早返回 * * @param nodeInfo 窗口根节点 * @param texts 需要匹配的字符串们 * @return 匹配到的节点数组 */ private List 
           
             findAccessibilityNodeInfosByTexts(AccessibilityNodeInfo nodeInfo, String[] texts) { Log.d(TAG, "事件----> start findAccessibilityNodeInfosByTexts() " + texts); for (String text : texts) { if (text == null) continue; List 
            
              nodes = nodeInfo.findAccessibilityNodeInfosByText(text); if (!nodes.isEmpty()) { if (text.equals(WECHAT_OPEN_EN) && !nodeInfo.findAccessibilityNodeInfosByText(WECHAT_OPENED_EN).isEmpty()) { continue; } return nodes; } } Log.d(TAG, "事件----> end findAccessibilityNodeInfosByTexts() " + texts); return new ArrayList 
             
               (); } /** * 判断是否返回,减少点击次数 * 现在的策略是当红包文本和缓存不一致时,戳 * 文本一致且间隔大于MAX_CACHE_TOLERANCE时,戳 * * @param id 红包id * @param duration 红包到达与缓存的间隔 * @return 是否应该返回 */ private boolean shouldReturn(String id, long duration) { // ID为空 if (id == null) return true; // 名称和缓存不一致 if (duration < MAX_CACHE_TOLERANCE && id.equals(lastFetchedHongbaoId)) { return true; } return false; } @Override protected void onServiceConnected() { super.onServiceConnected(); HomeActivity.mIsStarted = true; Toast.makeText(this, "连接红包精灵服务", Toast.LENGTH_SHORT).show(); } @Override public boolean onUnbind(Intent intent) { Toast.makeText(this, "中断红包精灵服务", Toast.LENGTH_SHORT).show(); HomeActivity.mIsStarted = false; return super.onUnbind(intent); } } 
              
             
            
           
          
         
       
      
      
     
     
    
    
   
   

效果图:

这里写图片描述 这里写图片描述

关键Service代码:

这里写图片描述这里写图片描述这里写图片描述这里写图片描述

实现原理

1.1 状态说明

    private static final String WECHAT_OPEN_CH = "拆红包";
    private static final String WECHAT_VIEW_SELF_CH = "查看红包";
    private static final String WECHAT_VIEW_OTHERS_CH = "领取红包";
    private static final String WECHAT_NOTIFICATION_TIP = "[微信红包]";

1.2 根据阶段选择不同的入口

在每次窗体状态发生变化后,根据当前所在的阶段选择入口。

        /* 如果戳开但还未领取 */
        if (mNeedUnpack && (mUnpackNodeList != null)) {
            Log.d(TAG, "事件----> start performAction 戳开但还未领取");
            int size = mUnpackNodeList.size();
            if (size > 0) {
                AccessibilityNodeInfo cellNode = mUnpackNodeList.get(size - 1);
                cellNode.performAction(AccessibilityNodeInfo.ACTION_CLICK);
                mNeedUnpack = false;
            }
            Log.d(TAG, "事件----> end performAction 戳开但还未领取");
        }
  1. 屏幕内容检测和自动化点击的实现
    /** 打开通知栏消息*/
    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    private void openNotify(AccessibilityEvent event) {
        if(event.getParcelableData() == null || !(event.getParcelableData() instanceof Notification)) {
            return;
        }
        //将微信的通知栏消息打开
        Notification notification = (Notification) event.getParcelableData();
        PendingIntent pendingIntent = notification.contentIntent;
        Log.d(TAG, "事件----> 打开通知栏消息 " + event);
        try {
            pendingIntent.send();
        } catch (PendingIntent.CanceledException e) {
            e.printStackTrace();
        }
    }

和其他插件一样,这里使用的是Android API提供的AccessibilityService。这个类位于android.accessibilityservice包内,开启服务后就会自动做一些操作 像现在360的自动安装apk,也是开启了此服务。

AccessibilityService 服务在后台运行,等待系统在发生 AccessibilityEvent 事件时回调。这些事件指的是用户界面上发生的状态变化, 比如焦点变更、按钮按下等等。服务可以请求“查询当前窗口中内容”的能力。 开发辅助服务需要继承该类并实现其抽象方法。

2.1 配置AccessibilityService

在这个例子中,我们需要监听的事件是当红包来或者滑动屏幕时引起的屏幕内容变化,和点开红包时窗体状态的变化,因此我们只需要在配置XML的accessibility-service标签中加入一条

android:accessibilityEventTypes="typeWindowStateChanged|typeWindowContentChanged" 或在onAccessibilityEvent回调函数中对事件进行一次类型判断

final int eventType = event.getEventType(); if (eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED || eventType == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED) { // ... } 除此之外,由于我们只监听微信,还需要指定微信的包名

android:packageNames="com.tencent.mm" 为了获取窗口内容,我们还需要指定

android:canRetrieveWindowContent="true" 其他配置请看代码。

2.2 获取红包所在的节点

首先,我们要获取当前屏幕的根节点,下面两种方式效果是相同的:

      /* 聊天会话窗口,遍历节点匹配“领取红包”和"查看红包" */
        List<AccessibilityNodeInfo> nodes1 = this.findAccessibilityNodeInfosByTexts(
                this.rootNodeInfo, 
                new String[]{
                WECHAT_VIEW_OTHERS_CH, WECHAT_VIEW_SELF_CH });

        if (!nodes1.isEmpty()) {
            String nodeId = Integer.toHexString(System.identityHashCode(this.rootNodeInfo));
            if (!nodeId.equals(lastFetchedHongbaoId)) {
                mLuckyMoneyReceived = true;
                mReceiveNodeList = nodes1;
            }
            return;
        }

2.3 红包标识符

    /**
     * 将节点对象的id和红包上的内容合并
     * 用于表示一个唯一的红包
     *
     * @param node 任意对象
     * @return 红包标识字符串
     */
    private String getHongbaoText(AccessibilityNodeInfo node) {
        /* 获取红包上的文本 */
        String content;
        try {
            AccessibilityNodeInfo i = node.getParent().getChild(0);
            content = i.getText().toString();
        } catch (NullPointerException npe) {
            return null;
        }
        Log.d(TAG, "事件----> start getHongbaoText() " + content);
        return content;
    }

第一时间获得博客更新提醒,以及更多android干货,源码分析,欢迎关注我的微信公众号,扫一扫下方二维码,即可关注。还有各种福利

这里写图片描述

一个帮助你在微信抢红包时战无不胜的Android应用。自动检测并且拆开红包,速度超乎你的想象。 支持中英文。前往Release下载最新版本。已下载用户可直接在设置里面更新。 特性 监视选项任意组合,满足多样化的使用需求[?] 提供了系统通知/聊天列表/聊天页面三档选项,无论是想要谨慎不被察觉,还是想要高效志在必得,这个插件如你所愿。 不仅快人一步,红包识别更加智能 多种特征标识,聊天时不再重复点击红包。智能过滤红包关键字[?],避免落入“专属红包”、“抢到翻倍”的陷阱。还可以设置延时抢红包自动回复感谢语[?]。 紧跟微信更新 第一时间适配最新版本微信,应用内即可一键更新。 轻量、安全、值得信赖 安装包仅1M,无需ROOT,下载即用。代码公开透明,活跃的社区讨论,数万用户下载,值得你的信赖。 使用方法 打开『微信红包』应用,开启插件。 做你想做的事。 坐等红包进账。 实现原理 请见技术文档,注意文档描述的是dev分支(已弃用)的具体实现,而不是stable分支。若有疑问,请在ISSUES中提出。 更新日志 完整的更新日志请见CHANGELOG。 版权及免责声明 本项目源自小米去年秋季发布会时演示的MIUI 7抢红包测试代码。 插件可能会在一定程度上改变微信的交互方式。使用本项目中包含的代码及其生成物时,使用者自行承担随之而来的各种风险,包括但不限于“禁用红包功能”、“微信封号”。 本项目使用MIT许可证。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值