美文网首页
微信抢红包助手实践

微信抢红包助手实践

作者: 狄仁杰666 | 来源:发表于2021-10-14 01:29 被阅读0次

    前言

    来啦老铁!

    咱们在前一阵子的文章 老铁,原来做个Android App这么简单~ 中学习了如何动手做 Android App,当时只是很简单的涉及了一下,今天咱们来稍微应用一下:

    • 微信抢红包助手实践;

    我个人挺好奇抢红包助手是怎么实现的,几年前有听过一个朋友说这个东西挺简单的,我倒想知道,到底有多简单,顺便为自己做个抢红包助手,他不香嘛~

    本文章代码已上传 LuckyDog GitHub 仓库,欢迎取阅。

    整体步骤

    1. 下载安装Android Studio;
    2. 新建Android项目;
    3. 创建 service 包;
    4. 创建 LuckyService 类;
    5. 在 LuckyService 类中完成抢红包逻辑;
    6. 编写无障碍配置;
    7. AndroidManifest.xml 中注册 LuckyService 类和无障碍配置;
    8. 创建配置信息文件;
    9. 安装抢红包助手;
    10. 设置抢红包助手;
    11. 自动抢红包;

    1. 下载安装Android Studio;

    参考前一阵子的文章 老铁,原来做个Android App这么简单~

    2. 新建Android项目;

    新建Android项目

    3. 创建 service 包;

    项目结构

    4. 创建 LuckyService 类;

    package com.dylanz.luckydog.service;
    
    import android.accessibilityservice.AccessibilityService;
    import android.view.accessibility.AccessibilityEvent;
    
    public class LuckyService extends AccessibilityService {
        @Override
        public void onAccessibilityEvent(AccessibilityEvent event) {
    
        }
    
        @Override
        public void onInterrupt() {
    
        }
    }
    

    5. 在 LuckyService 类中完成抢红包逻辑;

    package com.dylanz.luckydog.service;
    
    import android.accessibilityservice.AccessibilityService;
    import android.app.Notification;
    import android.app.PendingIntent;
    import android.util.Log;
    import android.view.accessibility.AccessibilityEvent;
    import android.view.accessibility.AccessibilityNodeInfo;
    import android.annotation.TargetApi;
    import android.os.Build;
    
    import androidx.annotation.RequiresApi;
    
    import java.util.List;
    
    public class LuckyService extends AccessibilityService {
        /**
         * 日志的 tag,随意
         */
        public static final String TAG = "LuckyService";
    
        /**
         * 红包是否打开的状态记录变量
         */
        private boolean isRedPacketOpened = false;
    
        /**
         * 红包消息辨别关键字
         */
        private static final String HONG_BAO_TXT = "[微信红包]";
    
        /**
         * 有"开"的那个小弹窗的 className
         */
        private static final String ACTIVITY_DIALOG_LUCKY_MONEY = "com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyNotHookReceiveUI";
    
        /**
         * 红包领取后的详情页面的 className
         */
        private static final String LUCKY_MONEY_DETAIL = "com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyDetailUI";
    
        /**
         * 收到的红包,整个控件的 id
         */
        private static final String RED_PACKET_ID = "com.tencent.mm:id/tv";
    
        /**
         * 已领过的红包有个"已领取"字眼,这个字眼对应的控件 id
         */
        private static final String OPENED_ID = "com.tencent.mm:id/tt";
    
        @RequiresApi(api = Build.VERSION_CODES.N)
        @Override
        public void onAccessibilityEvent(AccessibilityEvent event) {
            //获取当前界面包名
            CharSequence packageNameChar = event.getPackageName();
            Log.e(TAG, "packageNameChar:" + packageNameChar);
            if (packageNameChar == null || !packageNameChar.toString().equals("com.tencent.mm")) {
                return;
            }
            isRedPacketOpened = false;
            //获取当前类名
            String className = event.getClassName().toString();
    
            //红包领取后的详情页面,自动返回
            if (className.equals(LUCKY_MONEY_DETAIL)) {
                Log.e(TAG, "红包已领取,返回聊天页面:");
                performGlobalAction(GLOBAL_ACTION_BACK);
                return;
            }
    
            //当前为红包弹出窗(有"开"的那个小弹窗)
            if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED && className.equals(ACTIVITY_DIALOG_LUCKY_MONEY)) {
                //有可能会由于网络原因,"开"的那个小弹框会需要加载后才显示,我们此处最多等 5 秒钟
                for (int i = 0; i < 1000; i++) {
                    if (isRedPacketOpened) {
                        break;
                    }
                    AccessibilityNodeInfo rootNode = getRootInActiveWindow();
                    openRedPacket(rootNode);
                    try {
                        Thread.sleep(5);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                return;
            }
    
            //遍历消息列表的每个消息,点击红包控件
            AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();
            jumpIntoRedPacket(nodeInfo);
    
            //是否微信聊天页面的类
            if (event.getParcelableData() != null && event.getParcelableData() instanceof Notification) {
                Log.e(TAG, "接收到通知栏消息");
                Notification notification = (Notification) event.getParcelableData();
                //获取通知消息详情
                if (notification.tickerText == null) {
                    return;
                }
                String content = notification.tickerText.toString();
                //解析消息
                String[] msg = content.split(":");
                if (msg.length == 0) {
                    return;
                }
                String text = msg[1].trim();
                if (text.contains(HONG_BAO_TXT)) {
                    Log.e(TAG, "接收到通知栏红包消息,点击消息,进入聊天界面");
                    //打开通知栏的intent,即打开对应的聊天界面
                    PendingIntent pendingIntent = notification.contentIntent;
                    try {
                        pendingIntent.send();
                    } catch (PendingIntent.CanceledException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    
        /**
         * 点击"开"按钮
         *
         * @param rootNode rootNode
         */
        private void openRedPacket(AccessibilityNodeInfo rootNode) {
            if (rootNode == null) {
                return;
            }
            for (int i = 0; i < rootNode.getChildCount(); i++) {
                AccessibilityNodeInfo node = rootNode.getChild(i);
                if ("android.widget.Button".equals(node.getClassName().toString())) {
                    node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
                    isRedPacketOpened = true;
                    break;
                }
                openRedPacket(node);
            }
        }
    
        @Override
        public void onInterrupt() {
    
        }
    
        /**
         * 点击"开"按钮
         *
         * @param rootNode rootNode
         */
        private void jumpIntoRedPacket(AccessibilityNodeInfo rootNode) {
            if (rootNode == null) {
                return;
            }
            for (int i = 0; i < rootNode.getChildCount(); i++) {
                //获取到子控件
                AccessibilityNodeInfo node = rootNode.getChild(i);
                //获取红包控件
                AccessibilityNodeInfo target = findViewByID(node, RED_PACKET_ID);
                if (target != null) {
                    //已领取这个控件为空,红包还没有被领取
                    if (findViewByID(node, OPENED_ID) == null) {
                        Log.e(TAG, "找到未领取的红包,点击红包");
                        performViewClick(target);
                        break;
                    }
                }
                jumpIntoRedPacket(node);
            }
        }
    
        /**
         * 模拟点击事件
         *
         * @param nodeInfo nodeInfo
         */
        public void performViewClick(AccessibilityNodeInfo nodeInfo) {
            if (nodeInfo == null) {
                return;
            }
            while (nodeInfo != null) {
                Log.e(TAG, "打开红包1");
                if (nodeInfo.isClickable()) {
                    Log.e(TAG, "打开红包2");
                    nodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK);
                    break;
                }
                nodeInfo = nodeInfo.getParent();
            }
        }
    
        /**
         * 查找对应ID的View
         *
         * @param accessibilityNodeInfo AccessibilityNodeInfo
         * @param id                    id
         * @return View
         */
        @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
        public AccessibilityNodeInfo findViewByID(AccessibilityNodeInfo accessibilityNodeInfo, String id) {
            if (accessibilityNodeInfo == null) {
                return null;
            }
            List<AccessibilityNodeInfo> nodeInfoList = accessibilityNodeInfo.findAccessibilityNodeInfosByViewId(id);
            if (nodeInfoList != null && !nodeInfoList.isEmpty()) {
                for (AccessibilityNodeInfo nodeInfo : nodeInfoList) {
                    if (nodeInfo != null) {
                        return nodeInfo;
                    }
                }
            }
            return null;
        }
    }
    

    6. 编写无障碍配置;

    res 下新建 xml 文件夹(名字任意),创建一个 xml 文件,如:accessible_service_wx_config.xml,写入:

    <?xml version="1.0" encoding="utf-8"?>
    <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
        android:accessibilityEventTypes="typeAllMask"
        android:accessibilityFeedbackType="feedbackGeneric"
        android:accessibilityFlags="flagDefault"
        android:canRetrieveWindowContent="true"
        android:description="@string/app_description"
        android:notificationTimeout="10"/>
    
    参数解读:后续有机会补上;

    7. AndroidManifest.xml 中注册 LuckyService 类和无障碍配置;

    修改前:

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.dylanz.luckydog">
    
        <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:roundIcon="@mipmap/ic_launcher_round"
            android:supportsRtl="true"
            android:theme="@style/Theme.LuckyDog">
            <activity
                android:name=".MainActivity"
                android:exported="true">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
        </application>
    
    </manifest>
    

    修改后:

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.dylanz.luckydog">
    
        <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
        <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:roundIcon="@mipmap/ic_launcher_round"
            android:supportsRtl="true"
            android:theme="@style/Theme.LuckyDog">
            <activity
                android:name=".MainActivity"
                android:exported="true">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
    
            <service
                android:name=".service.LuckyService"
                android:label="@string/app_description"
                android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
                android:exported="true">
                <intent-filter>
                    <action android:name="android.accessibilityservice.AccessibilityService" />
                </intent-filter>
                <meta-data
                    android:name="android.accessibilityservice"
                    android:resource="@xml/accessible_service_wx_config" />
            </service>
        </application>
    
    </manifest>
    
    注:android:exported 代表是否支持其它应用调用当前组件,默认值:若包含有intent-filter 默认值为true,若没有intent-filter 则默认值为false,我们此处应设置为true;

    8. 创建配置信息文件;

    res/values 下创建 strings.xml 文件,内容如下:

    <resources>
        <string name="app_name">LuckyDog微信红包助手 - 基础版</string>
        <string name="app_description">LuckyDog抢红包助手</string>
    </resources>
    
    注:这里头的配置信息,就是 accessible_service_wx_config.xml 文件和 AndroidManifest.xml 文件中会用到的 @string/app_name 和 @string/app_description,此步骤根据 Android Studio 提示完成;

    9. 安装抢红包助手;

    • 在 Android Studio 中编译后,手机弹出安装提示后,安装助手;
    安装调试
    咱们也可以找到编译出来的 apk 文件,后续就不用再在 Android Studio 里头编译安装了,直接用 apk 文件安装就好啦!
    apk 文件

    apk 文件安装法也是将红包助手与其他人分享的手段。

    • 安装过程:
    安装过程1 安装过程2

    这个页面不要怕,咱们自己开发的软件,没有发布,都会这样,当然咱们没有做什么见不得人的功能;

    安装完成

    10. 设置抢红包助手;

    点击抢红包助手页面上的“红包助手 - 无障碍设置”,打开设置页面后,选择“LuckyDog抢红包助手”,打开“使用LuckyDog抢红包助手”即可;

    11. 自动抢红包;

    以上设置好了之后,返回检查助手是否是启动状态,是的话就开始了自动抢红包辅助了!不难吧~

    助手启动状态

    目前支持的自动抢红包场景:

    • 在聊天页面自动抢红包;
    • 未开启消息免打扰时,微信后台运行;
    • 未开启消息免打扰时,退出微信;
    • 开启消息免打扰,微信界面后台运行,人工进入聊天页面;
    • 聊天界面有多个红包,自动全部抢;

    咱们出发点就想做个简单的抢红包助手,没有考虑特别多的其他交互,如发感谢信、红包统计等功能,如果您有兴趣,可以自行研究,原理与本文章介绍的类似。

    感谢其他简书作者的参考文献,包括并不限于:Android微信抢红包辅助

    好了,熬了好几个晚上的助手终于可以告一段落了,敬默默努力的每一位朋友~

    如果本文对您有帮助,麻烦动动手指点点赞?

    非常感谢!

    相关文章

      网友评论

          本文标题:微信抢红包助手实践

          本文链接:https://www.haomeiwen.com/subject/swxanltx.html