android抢红包代码解析支持微信与QQ

最近有一段时间没写博客了,一方面是工作比较忙,一方面也着实本人水平有限,没有太多能与大家分享的东西,也就是在最近公司要做一个抢红包的功能,老板发话了咋们就开干呗,本人就开始在网上收集资料,经过整理和实践,总算完美实现了功能,这里拿出本人一点微薄的成就与大家分享。

首先界面是这样的

开启自动抢红包只需点击相应的选项即可,下面我们进入正题,实现自动抢红包的原理,其实是借助android下的一个辅助服务AccessibilityService,这个服务是google公司为许多Android使用者因为各种情况导致他们要以不同的方式与手机交互。这包括了有些用户由于视力上,身体上,年龄上的问题致使他们不能看完整的屏幕或者使用触屏,也包括了无法很好接收到语音信息和提示的听力能力比较弱的用户。Android提供了Accessibility功能和服务帮助这些用户更加简单地操作设备,包括文字转语音(这个不支持中文),触觉反馈,手势操作,轨迹球和手柄操作。

而开发者可以利用这些服务使得程序更好用,我们来看代码:

<service
            android:name="com.hxwl.qhb.service.QiangHongBaoService"
            android:enabled="true"
            android:exported="true"
            android:label="XXXX"
            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/qianghongbao_service_config" />
        </service>

以上内容配置在AndroidManifest中,qianghongbao_service_config内容如下

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

android:description 这个是设置服务的描述,在用户授权的界面可以看到。

android:accessibilityEventTypes 这个是配置要监听的辅助事件,我们只需要用到typeNotificationStateChanged(通知变化事件)、typeWindowStateChanged(界面变化事件)

android:packageNames 这个是要监听应用的包名,如果要监听多个应用,则用","去分隔

android:accessibilityFeedbackType 这个是设置反馈方式,方式有如下几种

android:accessibilityFeedbackType="feedbackGeneric"通用的反馈类型

android:notificationTimeout="100"为事件回调的延迟时间

android:accessibilityFlags="flagDefault"默认标记

android:canRetrieveWindowContent="true"如果不设为true,AccessibilityEvent.getSource()获取的对象即为空

AccessibilityService类主要有以下四个方法:

@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
    //接收事件,如触发了通知栏变化、界面变化等
}

@Override
protected boolean onKeyEvent(KeyEvent event) {
    //接收按键事件
    return super.onKeyEvent(event);
}

@Override
public void onInterrupt() {
  //服务中断,如授权关闭或者将服务杀死
}

@Override
protected void onServiceConnected() {
    super.onServiceConnected();
    //连接服务后,一般是在授权成功后会接收到
}

当我们监听的包名发生通知或界面改变时,就会被onAccessibilityEvent方法捕抓到,我们先看看微信的实现:

@Override
	public void onReceiveJob(AccessibilityEvent event) {
		final int eventType = event.getEventType();
		if (eventType == AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED) {// 通知栏事件
			Parcelable data = event.getParcelableData();
			if (data == null || !(data instanceof Notification)) {
				return;
			}
			// 开启快速模式,不处理
			if (QiangHongBaoService.isNotificationServiceRunning() && getConfig().isEnableNotificationService()) {
				return;
			}
			List<CharSequence> texts = event.getText();
			if (!texts.isEmpty()) {
				String text = String.valueOf(texts.get(0));
				notificationEvent(text, (Notification) data);
			}
		} else if (eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {// 界面改变事件
			openHongBao(event);
		} else if (eventType == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED) {// 界面内容改变事件
			if (mCurrentWindow != WINDOW_LAUNCHER) { // 不在聊天界面或聊天列表,不处理
				return;
			}
			if (isReceivingHongbao) {
				handleChatListHongBao();
			}
		}
	}

这里我们做了简单的封装,onAccessibilityEvent方法触发时,便会进入AccessibilityEvent传入onReceiveJob方法中,在该方法中分别处理了通知栏事件,界面改变事件,界面内容改变事件,我们逐个看。

通知栏事件中,我们进行了判空,并拦截快速模式,重点是event.getText()获取通知中的文本,进行判断:

/** 所有通知栏事件,都在该方法处理 */
	private void notificationEvent(String ticker, Notification nf) {
		String text = ticker;
		int index = text.indexOf(":");
		if (index != -1) {
			text = text.substring(index + 1);
		}
		text = text.trim();
		if (text.contains("[微信红包]")) { // 红包消息
			newHongBaoNotification(nf);
		}
	}

如果有[微信红包]字眼,便进入下一步处理

/** 打开通知栏消息 */
	@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
	private void newHongBaoNotification(Notification notification) {
		isReceivingHongbao = true;
		// 以下是精华,将微信的通知栏消息打开
		PendingIntent pendingIntent = notification.contentIntent;
		boolean lock = NotifyHelper.isLockScreen(getContext());

		if (!lock) {// 未锁,自动点开通知
			NotifyHelper.send(pendingIntent);
		} else {// 锁屏,显示通知,微信自带,不作任何处理
			NotifyHelper.showNotify(getContext(), String.valueOf(notification.tickerText), pendingIntent);
		}

		// 锁屏 || 模式非自动抢
		if (lock || getConfig().getWechatMode() != Config.WX_MODE_0) {
			// 开启声音,震动提示
			NotifyHelper.playEffect(getContext(), getConfig());
		}
	}

获取到PendingIntent,并判断当前是否锁屏,这里主要用到了

/** 执行PendingIntent事件 */
	public static void send(PendingIntent pendingIntent) {
		try {
			pendingIntent.send();
		} catch (PendingIntent.CanceledException e) {
			e.printStackTrace();
		}
	}

该方法可以执行模拟点击,如同点击了红包通知一般,这样就会触发下一个内容改变事件

@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
	private void openHongBao(AccessibilityEvent event) {
		if ("com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI".equals(event.getClassName())) {
			mCurrentWindow = WINDOW_LUCKYMONEY_RECEIVEUI;
			// 点中了红包,下一步就是去拆红包
			handleLuckyMoneyReceive();
		} else if ("com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyDetailUI".equals(event.getClassName())) {
			mCurrentWindow = WINDOW_LUCKYMONEY_DETAIL;
			// 拆完红包后看详细的纪录界面
			if (getConfig().getWechatAfterGetHongBaoEvent() == Config.WX_AFTER_GET_GOHOME) { // 返回主界面,以便收到下一次的红包通知
				AccessibilityHelper.performHome(getService());
			}
		} else if ("com.tencent.mm.ui.LauncherUI".equals(event.getClassName())) {
			mCurrentWindow = WINDOW_LAUNCHER;
			// 在聊天界面,去点中红包
			handleChatListHongBao();
		} else {
			mCurrentWindow = WINDOW_OTHER;
		}
	}

我们根据当前的activity判断下一步处理

/**
	 * 收到聊天里的红包
	 * */
	@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
	private void handleChatListHongBao() {
		int mode = getConfig().getWechatMode();
		if (mode == Config.WX_MODE_3) { // 只通知模式
			return;
		}

		AccessibilityNodeInfo nodeInfo = getService().getRootInActiveWindow();
		if (nodeInfo == null) {
			return;
		}

		if (mode != Config.WX_MODE_0) {// 非自动抢
			boolean isMember = isMemberChatUi(nodeInfo);
			if (mode == Config.WX_MODE_1 && isMember) {// 过滤群聊
				return;
			} else if (mode == Config.WX_MODE_2 && !isMember) { // 过滤单聊
				return;
			}
		}

		// 下面就是,激动人心的抢红包代码
		List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByText("领取红包");

		if (list != null && list.isEmpty()) {// "领取红包"关键字节点获取不到
			// 从消息列表查找红包
			AccessibilityNodeInfo node = AccessibilityHelper.findNodeInfosByText(nodeInfo, HONGBAO_TEXT_KEY);
			if (node != null) {
				isReceivingHongbao = true;
				AccessibilityHelper.performClick(node);
			}
		} else if (list != null) {
			if (isReceivingHongbao) {
				// 最新的红包领起
				AccessibilityNodeInfo node = list.get(list.size() - 1);
				AccessibilityHelper.performClick(node);
				isReceivingHongbao = false;
			}
		}
	}

同样根据判断关键文本进入模拟点击,然后再次触发界面改变事件

/**
	 * 点击聊天里的红包后,显示的界面
	 * */
	@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
	private void handleLuckyMoneyReceive() {
		AccessibilityNodeInfo nodeInfo = getService().getRootInActiveWindow();

		AccessibilityNodeInfo targetNode = null;
		int event = getConfig().getWechatAfterOpenHongBaoEvent();
		int wechatVersion = getWechatVersion();
		if (event == Config.WX_AFTER_OPEN_HONGBAO) { // 拆红包
			if (wechatVersion < USE_ID_MIN_VERSION) {
				targetNode = AccessibilityHelper.findNodeInfosByText(nodeInfo, "拆红包");
			} else {
				String buttonId = "com.tencent.mm:id/b43";

				if (wechatVersion == 700) {
					buttonId = "com.tencent.mm:id/b2c";
				}

				if (buttonId != null) {
					targetNode = AccessibilityHelper.findNodeInfosById(nodeInfo, buttonId);
				}

				if (targetNode == null) {
					// 分别对应固定金额的红包 拼手气红包
					AccessibilityNodeInfo textNode = AccessibilityHelper.findNodeInfosByTexts(nodeInfo, "发了一个红包", "给你发了一个红包", "发了一个红包,金额随机");

					if (textNode != null) {
						for (int i = 0; i < textNode.getChildCount(); i++) {
							AccessibilityNodeInfo node = textNode.getChild(i);
							if ("android.widget.Button".equals(node.getClassName())) {
								targetNode = node;
								break;
							}
						}
					}
				}

				if (targetNode == null) { // 通过组件查找
					targetNode = AccessibilityHelper.findNodeInfosByClassName(nodeInfo, "android.widget.Button");
				}
			}
		} else if (event == Config.WX_AFTER_OPEN_SEE) { // 看一看
			if (getWechatVersion() < USE_ID_MIN_VERSION) { // 低版本才有 看大家手气的功能
				targetNode = AccessibilityHelper.findNodeInfosByText(nodeInfo, "看看大家的手气");
			}
		} else if (event == Config.WX_AFTER_OPEN_NONE) {// 静静地看着
			return;
		}

		if (targetNode != null) {
			final AccessibilityNodeInfo n = targetNode;
			long sDelayTime = getConfig().getWechatOpenDelayTime();
			if (sDelayTime != 0) {
				getHandler().postDelayed(new Runnable() {
					@Override
					public void run() {
						AccessibilityHelper.performClick(n);
					}
				}, sDelayTime);
			} else {
				AccessibilityHelper.performClick(n);
			}
		}
	}

微信中有两种红包,一种是固定金额的红包,还有一种是拼手气红包,两个的关键字不同,我们分开判断。

QQ的实现也基本相同,我们直接上代码

@Override
	public void onReceiveJob(AccessibilityEvent event) {
		openRed(event);
	}
private void openRed(AccessibilityEvent event) {
		this.rootNodeInfo = event.getSource();
		if (rootNodeInfo == null) {
			return;
		}
		mReceiveNode = null;
		checkNodeInfo();
		/* 如果已经接收到红包并且还没有戳开 */
		if (mLuckyMoneyReceived && (mReceiveNode != null)) {
			int size = mReceiveNode.size();
			if (size > 0) {
				String id = getHongbaoText(mReceiveNode.get(size - 1));
				long now = System.currentTimeMillis();
				if (this.shouldReturn(id, now - lastFetchedTime))
					return;
				lastFetchedHongbaoId = id;
				lastFetchedTime = now;
				AccessibilityNodeInfo cellNode = mReceiveNode.get(size - 1);
				if (cellNode.getText().toString().equals("口令红包已拆开")) {
					return;
				}
				cellNode.getParent().performAction(AccessibilityNodeInfo.ACTION_CLICK);
				if (cellNode.getText().toString().equals(QQ_HONG_BAO_PASSWORD)) {
					AccessibilityNodeInfo rowNode = getService().getRootInActiveWindow();
					if (rowNode == null) {
						return;
					} else {
						recycle(rowNode);
					}
				}
				mLuckyMoneyReceived = false;
			}
		}
	}
@TargetApi(Build.VERSION_CODES.KITKAT)
	public void recycle(AccessibilityNodeInfo info) {
		if (info.getChildCount() == 0) {
			/* 这个if代码的作用是:匹配“点击输入口令的节点,并点击这个节点” */
			if (info.getText() != null && info.getText().toString().equals(QQ_CLICK_TO_PASTE_PASSWORD)) {
				info.getParent().performAction(AccessibilityNodeInfo.ACTION_CLICK);
			}
			/* 这个if代码的作用是:匹配文本编辑框后面的发送按钮,并点击发送口令 */
			if (info.getClassName().toString().equals("android.widget.Button") && info.getText().toString().equals("发送")) {
				info.performAction(AccessibilityNodeInfo.ACTION_CLICK);
			}
		} else {
			for (int i = 0; i < info.getChildCount(); i++) {
				if (info.getChild(i) != null) {
					recycle(info.getChild(i));
				}
			}
		}
	}

可见这是一个递归方法,反复获取节点并判断节点文本,模拟点击红包按钮。

因为是公司的项目,不方便给Demo,这里附上一个大神链接,我便是在大神的基础上作得更改,点这里

时间: 2024-10-10 02:33:37

android抢红包代码解析支持微信与QQ的相关文章

在Android中使App高速、简单地支持新浪微博、微信、QQ、facebook等十几个主流社交平台的分享功能

前言 在如今的APP或者游戏中,分享功能差点儿已经成为标配.分享功能不但能够满足用户的需求.也能够为产品带来很多其它的用户,甚至能够对用户的行为.活跃度.年龄段等情况进行数据统计,使得软件公司能够对产品进行更精准的定位.今天我们就来简单剖析市场上一款优秀的分享SDK以及其集成过程.这款分享SDK就是友盟的社会化分享组件. 友盟社会化分享,帮助移动应用高速具备分享.登录.评论.喜欢等社交功能,提升用户粘度.助力产品推广,并提供实时.全面的社会化数据统计分析服务. 很多其它请訪问友盟社会化组件官网.

在Android中使App快速、简单地支持新浪微博、微信、QQ、facebook等十几个主流社交平台的分享功能

前言 在现在的APP或者游戏中,分享功能几乎已经成为标配.分享功能不但能够满足用户的需求,也能够为产品带来更多的用户,甚至可以对用户的行为.活跃度.年龄段等情况进行数据统计,使得软件公司可以对产品进行更精准的定位.今天我们就来简单剖析市场上一款优秀的分享SDK以及其集成过程,这款分享SDK就是友盟的社会化分享组件. 友盟社会化分享,帮助移动应用快速具备分享.登录.评论.喜欢等社交功能,提升用户粘度.助力产品推广,并提供实时.全面的社会化数据统计分析服务. 基本概念 根据友盟的集成文档,我们对于友

优化Recorder H5录音:可边录边转码上传服务器,支持微信提供Android IOS Hybrid App源码

目录 一.Recorder H5录音库的特性 (1)浏览器支持 (2)功能支持 二.使用预览截图 (1)移动端H5 (2)IOS Hybrid App (3)Android Hybrid App 三.应用场景 四.优化过程记录 (1)为什么要升级优化 (2)开始使用Web Worker加速转码 (3)剩下的问题 五.Hybrid App存在的意义 六.更多支持 Recorder H5 GitHub开源库随着支持功能的增多,音频转码处理效率渐渐的跟不上需求了,近期抽时间对音频转码部分进行了升级优化

基于Swift语言开发微信、QQ和微博的SSO授权登录代码分析

前言 Swift 语言,怎么说呢,有一种先接受后排斥,又欢迎的感觉,纵观国外大牛开源框架或项目演示,Swift几乎占据了多半,而国内虽然出现很多相关技术介绍和教程,但是在真正项目开发中使用的占据很少部分,原因一是目前熟练它的开发者并不多,二是版本不太稳定,还需要更成熟可靠的版本支持,但总之未来还是很有前景的,深有体会,不管是代码量还是编译效率,以及语言特性,现代性都优于Object-C,估计后续会被苹果作为官方开发语言,值得期待. 走起 鉴于此,笔者将之前用Object-C写的SSO授权登录:微

微信中QQ表情的解析(php)

微信公众平台接受的消息中,标签是用'/:'开头的字符串表示的,假设要在网页上显示(比方制作微信大屏幕),就须要进行转换. 所以我向微信公众平台按顺序发送了各个QQ表情,在微信公众平台后台能够看到接受的表情会被解析成https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/0.gif 这种图片.所以自己写一套解析函数就可以. 在微信公众平台后台发现,腾讯自己干了一件错误的事情:有一些表情没有被正确解析,这些标签的特点是有括号.引號这种字

shareSDK的初步使用(shareSDK中微信、qq等兼容问题,以及cocoapods支持架构冲突问题的解决)

第一次使用shareSDK来做第三方分享,可是.昨天一天都是在调试bug,一直错误不断! 先说下我的开发环境: xcode:5.1 真机调试:iPhone5s 我们都知道xcode5.1以后開始是支持arm64的,而shareSDK中的微信.qq是不支持armv64的,所以网上都有非常多方法都是正确的改变xcode的编译条件,改成armv7,armv7s:详细怎么改我就不多说了,报错直接Google一下就知道了.关键是这个问题攻克了,问题又来了我有的开源库管理工具cocoapods来实现的安装第

Android——分享文本给微博、微信、qq、邮箱、我的电脑等等手机上所安装的能够通信的软件

我们有时候需要将文本.图片等分享到微博.微信.qq好友等,网上有好多代码,实现复杂,对于初学者来说,读起来很困难,其实,分享这一功能是很容易实现的.实现代码如下(经过了测试): 1.分享文本: Intent intent = new Intent(Intent.ACTION_SEND); //启动分享发送的属性     intent.setType("text/plain");//分享发送的数据类型为文本   intent.putExtra(Intent.EXTRA_SUBJECT,

Android:Umeng(友盟) 微信,QQ,新浪分享 (三)

第一步: 下载并安装SDK 添加代码和资源引用,我们提供了添加资源文件和jar文件的两种方式,可以根据需求选择 解压SDK压缩包,将文件夹中的'main/libs'和'main/res'文件夹复制到你的项目工程根目录下(如使用'ADT 17'以下用户需要手动添加'libs'下的jar文件到工程Path中) 结合上两篇文章 PushSDKMoudle中添加QQ,微信,新浪分享后的libs结构: 注:除jar包外,其余文件夹均需添加对应分享的so包.没有则不添加,附图: 第二步: AndroidMa

android炫酷动画源码,QQ菜单、瀑布流、二维码源码

Android精选源码 自定义弹框封装,ProgressDialog,StatusDialog和Toast,支持自定义颜色 有深度感的fragment代码 在屏幕顶部或者底部显示提示 短信转发工具,自动转发短信到手机或邮箱 美观的菜单隐藏在主界面底部的抽屉导航. 仿QQ菜单.瀑布流.数据库.二维码生成 andorid实现"划词翻译"功能的项目 android界面切换的效果,输入文字时背景图缩放效果 android一款很炫的动画代码 Android雷达扫描图,超高仿QQ附近的人 Andr