利用AccessibilityService实现“微信红包”插件

一、什么是AccessibilityService?

AccessibilityService是android的一个辅助服务抽象类,它是继承Service类,可以用于监听通知栏的变化、窗体改变、窗体内容改变、EditText光标移动、TextView内容改变等事件。


二、基本实现流程。

如下图:



三、主要代码段。

1、RedPacket类继承AccessibilityService,实现如下:

package com.nicksong.luckymoney;

import java.io.IOException;
import java.util.List;
import android.accessibilityservice.AccessibilityService;
import android.annotation.SuppressLint;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.PendingIntent.CanceledException;
import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
import android.media.MediaPlayer;
import android.util.Log;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;

public class RedPacket extends AccessibilityService {
	
	private MediaPlayer player;
	private boolean isFirst = false;

	@Override
	public void onAccessibilityEvent(AccessibilityEvent event) {
		int eventType = event.getEventType();
		switch (eventType) {
		//第一步:监听通知栏消息
		case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:
			List<CharSequence> texts = event.getText();
			if (!texts.isEmpty()) {
				for (CharSequence text : texts) {
					String content = text.toString();
					Log.i("demo", "text:"+content);
					if (content.contains("[微信红包]")) {
						isFirst = true;
						playSound4LuckyMoneyComing();
						//模拟打开通知栏消息
						if (event.getParcelableData() != null && event.getParcelableData() instanceof Notification) {
							Notification notification = (Notification) event.getParcelableData();
							PendingIntent pendingIntent = notification.contentIntent;
							try {
								pendingIntent.send();
							} catch (CanceledException e) {
								e.printStackTrace();
							}
						}
					}
				}
			}
			break;
		//第二步:监听是否进入微信红包消息界面
		case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
			String className = event.getClassName().toString();
			if (className.equals("com.tencent.mm.ui.LauncherUI")) {
				Log.i("demo", "开始抢红包");
				//开始抢红包
				getPacket();
			} else if (className.equals("com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI")) {
				Log.i("demo", "开始拆红包");
				isFirst = false;
				//开始打开红包
				openPacket();
			}
//			else if (className.equals("com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyDetailUI")) {
//				Log.i("demo", "在红包详情直接返回");
//				//模拟点击返回
//				goBackFromLmUi();
//			}
			break;
		}
	}
	
	/**
	 * 在红包详情界面模拟返回
	 */
	private void goBackFromLmUi() {
		isFirst = false;
		AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();
		if (nodeInfo != null) {
			for (int i = 0; i < nodeInfo.getChildCount(); i++) {
				Log.i("demo", "goBackFromLmUi i = " + i);
				Log.i("demo", "goBackFromLmUi Click"+",isClickable:" + nodeInfo.getChild(i).isClickable());
				Log.i("demo", "goBackFromLmUi View type = " + nodeInfo.getChild(i).getClassName());
				if ("android.widget.LinearLayout".equals(nodeInfo.getChild(i).getClassName()) && nodeInfo.getChild(i).isClickable()) {
					nodeInfo.getChild(i).performAction(AccessibilityNodeInfo.ACTION_CLICK);
				}
			}
		}
	}

	/**
	 * 查找到
	 */
	@SuppressLint("NewApi")
	private void openPacket() {
		AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();
		if (nodeInfo != null) {
			for (int i = 0; i < nodeInfo.getChildCount(); i++) {
				Log.i("demo", "openPacket i = " + i);
				Log.i("demo", "Click"+",isClickable:" + nodeInfo.getChild(i).isClickable());
				Log.i("demo", "View type = " + nodeInfo.getChild(i).getClassName());
				if ("android.widget.Button".equals(nodeInfo.getChild(i).getClassName()) && nodeInfo.getChild(i).isClickable()) {
					nodeInfo.getChild(i).performAction(AccessibilityNodeInfo.ACTION_CLICK);
				}
			}
		}

	}

	@SuppressLint("NewApi")
	private void getPacket() {
		AccessibilityNodeInfo rootNode = getRootInActiveWindow();
		recycle(rootNode);
	}
	
	/**
	 * 打印一个节点的结构
	 * @param info
	 */
	@SuppressLint("NewApi")
	public void recycle(AccessibilityNodeInfo info) {
        if (info.getChildCount() == 0) { 
        	if(info.getText() != null){
        		if("领取红包".equals(info.getText().toString())){
        			//这里有一个问题需要注意,就是需要找到一个可以点击的View
                	Log.i("demo", "Click"+",isClick:"+info.isClickable());
                	info.performAction(AccessibilityNodeInfo.ACTION_CLICK);
                	AccessibilityNodeInfo parent = info.getParent();
                	while(parent != null){
                		Log.i("demo", "parent isClick:"+parent.isClickable());
                		if(parent.isClickable() && isFirst){ 
                			parent.performAction(AccessibilityNodeInfo.ACTION_CLICK);
                			break;
                		}
                		parent = parent.getParent();
                	}
                	
            	}
        	}
        	
        } else {  
            for (int i = 0; i < info.getChildCount(); i++) {  
                if(info.getChild(i)!=null){  
                    recycle(info.getChild(i));
                }  
            }  
        }  
    }  

	@Override
	public void onInterrupt() {
	}

	private void playSound4LuckyMoneyComing() {
		try {
			AssetManager assetManager = this.getAssets();
			AssetFileDescriptor aFileDescriptor = assetManager.openFd("luckymoney_sound.ogg");
			player = new MediaPlayer();
			player.setDataSource(aFileDescriptor.getFileDescriptor(), aFileDescriptor.getStartOffset(), aFileDescriptor.getLength());
			player.prepare();
			player.start();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
}

2、res/xml/accessibility.xml如下:

<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
    android:accessibilityEventTypes="typeNotificationStateChanged|typeWindowStateChanged"
    android:accessibilityFeedbackType="feedbackGeneric"
    android:accessibilityFlags="flagDefault"
    android:canRetrieveWindowContent="true"
    android:description="@string/desc"
    android:notificationTimeout="100"
    android:packageNames="com.tencent.mm" />


3、manifest清单文件主要部分如下:

<service
            android:name="com.nicksong.luckymoney.RedPacket"
            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-data
                android:name="android.accessibilityservice"
                android:resource="@xml/accessibility" />
</service>


四、“设置-辅助服务”打开“微信红包插件”服务。

不同的手机,这个辅助服务的名称、位置可能都不一样,这个就要大家在自己手机里找一下,应该很快能找到。如,魅族的一款手机,这个菜单位置如下截图:

   

五、源码。

https://github.com/Ericsongyl/WechatRedPacket

欢迎star、fork,并提出宝贵意见和建议。

注意:Android系统4.1以上、微信版本6.3.28及以上。


---------------------------------------------------------------------------------------------------------------

声明:请勿商用,否则后果由投入商用者本人承担!

猜你喜欢

转载自blog.csdn.net/weiren1101/article/details/53112439
今日推荐