安卓使用无障碍服务监听微信和QQ的收款信息
导读:
资深的安卓程序员想必都了解,安卓的通知监听服务(NotificationListenerService)可以监听通知栏的信息,从通知栏信息里获取到我们想要的收款信息(比如收款类型、收款金额)。但是,这个通知监听服务有个弊端,如果APP没有发送通知,那就没办法知道有没有收到款,特别是现在的微信 和QQ,二维码收款不再发送通知,而是在自身的APP里给出提示,这样就没办法使用通知监听服务了。因此,我们需要使用安卓的无障碍服务(AccessibilityService),来监听APP界面的变化,实时获取微信、QQ的收款信息。
准备工作:
- Android Studio开发工具;
- 电脑安装安卓模拟器(夜神模拟器或者逍遥模拟器),模拟器里安装好微信、QQ;
步骤:
一、创建服务
1、新建一个Service类,继承自AccessibilityService,重写onServiceConnected()方法、onAccessibilityEvent()方法和onInterrupt()方法
package net.zy13.skhelper.service;import android.accessibilityservice.AccessibilityService;
import android.view.accessibility.AccessibilityEvent;
import net.zy13.skhelper.utils.LogUtil;
/*** 收款无障碍服务*/
public class SkAccessibilityService extends AccessibilityService{/*** 当服务启动的时候会被调用*/@Overridepublic void onServiceConnected(){LogUtil.debug( ":无障碍服务连接成功");}/*** 监听窗口变化的回调*/@Overridepublic void onAccessibilityEvent(AccessibilityEvent event) {// 获取事件类型,在对应的事件类型中对相应的节点进行操作int eventType = event.getEventType();//根据事件回调类型进行处理switch (eventType) {//当通知栏发生改变时case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:break;//当窗口的状态发生改变时(界面改变)case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:break;//内容改变case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED:break;//滑动变化case AccessibilityEvent.TYPE_VIEW_SCROLLED:break;}}/*** 中断服务的回调*/@Overridepublic void onInterrupt() {}
}
2、在AndroidManifest.xml清单里注册服务
<serviceandroid:name=".service.SkAccessibilityService"android:enabled="true"android:exported="true"android:label="收款助手"android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"><intent-filter><action android:name="android.accessibilityservice.AccessibilityService" /></intent-filter><meta-dataandroid:name="android.accessibilityservice"android:resource="@xml/config_accessibility" /></service>
3、在res资源目录里创建一个xml目录,然后在xml里创建config_accessibility.xml配置文件,这个config_accessibility.xml文件主要是保存AccessibilityService服务的配置参数,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"android:accessibilityEventTypes="typeAllMask"android:accessibilityFeedbackType="feedbackAllMask"android:canRetrieveWindowContent="true"android:notificationTimeout="100"android:packageNames="com.eg.android.AlipayGphone,com.tencent.mm,com.tencent.mobileqq"android:description="@string/access_desc" />
上面的android:description项要提取到strings.xml里,不要问为什么,因为不这样会报错。
<resources><string name="access_desc">收款助手使用无障碍服务监听收款信息</string>
</resources>
xml参数说明:
accessibilityEventTypes:表示该服务对界面中的哪些变化感兴趣,即哪些事件通知,比如窗口打开,滑动,焦点变化,长按等。具体的值可以在AccessibilityEvent类中查到,如typeAllMask表示接受所有的事件通知;
accessibilityFeedbackType:表示反馈方式,比如是语音播放,还是震动;
canRetrieveWindowContent:表示该服务能否访问活动窗口中的内容。也就是如果你希望在服务中获取窗体内容,则需要设置其值为true;
notificationTimeout:接受事件的时间间隔,通常将其设置为100即可;
packageNames:表示对该服务是用来监听哪个包的产生的事件,要监听多个APP包,就用逗号隔开(如果不设置就是接收所有的);
description:对该无障碍功能的描述,会显示在系统开启服务的设置界面;
typeAllMask | 接收所有事件 |
typeWindowStateChanged | 监听窗口状态变化,比如打开一个popupWindow,dialog,Activity切换等等 |
typeWindowContentChanged | 监听窗口内容改变,比如根布局子view的变化 |
typeWindowsChanged | 监听屏幕上显示的系统窗口中的事件更改,此事件类型只应由系统分派 |
typeNotificationStateChanged | 监听通知变化,比如notifacation和toast |
typeViewClicked | 监听view点击事件 |
typeViewLongClicked | 监听view长按事件 |
typeViewFocused | 监听view焦点事件 |
typeViewSelected | 监听AdapterView中的上下文选择事件 |
typeViewTextChanged | 监听EditText的文本改变事件 |
typeViewHoverEnter typeViewHoverExit |
监听view的视图悬停进入和退出事件 |
typeViewScrolled | 监听view滚动,此类事件通常不直接发送 |
typeViewTextSelectionChanged | 监听EditText选择改变事件 |
typeViewAccessibilityFocused | 监听view获得可访问性焦点事件 |
typeViewAccessibilityFocusCleared | 监听view清除可访问性焦点事件 |
typeGestureDetectionStart typeGestureDetectionEnd |
监听手势开始和结束事件 |
typeTouchInteractionStart typeTouchInteractionEnd |
监听用户触摸屏幕事件的开始和结束 |
typeTouchExplorationGestureStart typeTouchExplorationGestureEnd |
监听触摸探索手势的开始和结束 |
二、使用服务
1、当APP退出注销时,无障碍服务也会关闭,因此我们最好是在APP上判断服务是否开启,没开启的话,就提醒用户主动授权开启无障碍服务,判断无障碍服务是否开启的方法如下:
/*** 判断无障碍服务是否开启* @param mContext* @return*/private boolean isAccessibilitySettingsOn(Context mContext) {int accessibilityEnabled = 0;final String service = mContext.getPackageName() + "/" + 你自定义的无障碍服务.class.getCanonicalName();try {accessibilityEnabled = Settings.Secure.getInt(mContext.getApplicationContext().getContentResolver(),android.provider.Settings.Secure.ACCESSIBILITY_ENABLED);} catch (Settings.SettingNotFoundException e) {}TextUtils.SimpleStringSplitter mStringColonSplitter = new TextUtils.SimpleStringSplitter(':');if (accessibilityEnabled == 1) {String settingValue = Settings.Secure.getString(mContext.getApplicationContext().getContentResolver(),Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);if (settingValue != null) {mStringColonSplitter.setString(settingValue);while (mStringColonSplitter.hasNext()) {String accessibilityService = mStringColonSplitter.next();if (accessibilityService.equalsIgnoreCase(service)) {return true;}}}} else {}return false;}
2、只要用户授权,无障碍服务就会自动运行,下面这串代码是跳转到无障碍服务授权页面的,用户授权才可以真正使用无障碍服务
Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
startActivity(intent);
三、无障碍服务处理事件信息
AccessibilityService中常用的方法的介绍:
- disableSelf():禁用当前服务,也就是在服务可以通过该方法停止运行
- findFoucs(int falg):查找拥有特定焦点类型的控件
- getRootInActiveWindow():如果配置能够获取窗口内容,则会返回当前活动窗口的根结点
- getSeviceInfo():获取当前服务的配置信息
- onAccessibilityEvent(AccessibilityEvent event):有关AccessibilityEvent事件的回调函数,系统通过sendAccessibiliyEvent()不断的发送AccessibilityEvent到此处
- performGlobalAction(int action):执行全局操作,比如返回,回到主页,打开最近等操作
- setServiceInfo(AccessibilityServiceInfo info):设置当前服务的配置信息
- getSystemService(String name):获取系统服务
- onKeyEvent(KeyEvent event):如果允许服务监听按键操作,该方法是按键事件的回调,需要注意,这个过程发生了系统处理按键事件之前
- onServiceConnected():系统成功绑定该服务时被触发,也就是当你在设置中开启相应的服务,系统成功的绑定了该服务时会触发,通常我们可以在这里做一些初始化操作
- onInterrupt():服务中断时的回调
这里我们主要关注onAccessibilityEvent事件:
/*** 监听窗口变化的回调*/@Overridepublic void onAccessibilityEvent(AccessibilityEvent event) {// 获取事件类型,在对应的事件类型中对相应的节点进行操作int eventType = event.getEventType();//根据事件回调类型进行处理switch (eventType) {//当通知栏发生改变时case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:break;//当窗口的状态发生改变时(界面改变)case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED://主要是在这里监听支付宝、微信、QQ界面上的收款信息break;//内容改变case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED:break;//滑动变化case AccessibilityEvent.TYPE_VIEW_SCROLLED:break;}}
事件回调类型AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED,表示窗口的内容发生改变时,比如微信收到消息,会显示在消息列表里,就会触发这个事件的回调,我们就可以在这个回调里获取相应的信息。
四、获取节点信息
获取了界面窗口变化后,这个时候就要获取控件的节点,整个窗口的节点本质是个树结构,通过以下方式操作节点信息
1、获取窗口节点(根节点)
AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();
2、获取指定子节点(控件节点)
//通过文本找到对应的节点集合
List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByText(text);
//通过控件ID找到对应的节点集合,如com.tencent.mm:id/gd
List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByViewId(id);
通过控件ID获取节点是最方便的,下面开始教大家怎么获取APP的控件ID
五、获取APP的控件ID
先说明一下,控件ID实际上是我们在界面布局里设定的android:id的编译后的值,这个是唯一的。
1、前面的准备工作里,我们已经在安卓模拟器里安装了支付宝、微信和QQ,这里以微信为例子,我们在安卓模拟器里登录微信
2、在Android的Sdk目录里找到tools工具目录,双击打开里面的monitor.bat
3、在DDMS的Devices列表里会看到有一个安卓模拟器,如下图:
4、点击Dump View Hierarchy UI Automotor图标,会自动获取当前模拟器中打开的APP界面UI信息
5、打开后出现如下图,通过鼠标点击到界面的文字或图片上,可以在节点详情里看到控件ID
6、通过控件ID获取节点信息的方法
/*** 通过控件ID获取节点信息* @param id* @return*/@SuppressLint("NewApi")public String getNodeInfo(String id){String result="";// 获取当前整个活动窗口的根节点AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();// 获取父节点//nodeInfo.getParent();// 获取子节点//nodeInfo.getChild(0);if (nodeInfo != null) {// 通过文本找到对应的节点集合// List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByText("");// 通过控件ID找到对应的节点集合,如com.tencent.mm:id/gdList<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByViewId(id);for (AccessibilityNodeInfo item : list) {String str=item.getText().toString();if (str != null && str.length() != 0){result=str;break;}//模拟点击//item.performAction(AccessibilityNodeInfo.ACTION_CLICK);//模拟长按//item.performAction(AccessibilityNodeInfo.ACTION_LONG_CLICK);//模拟获取焦点//item.performAction(AccessibilityNodeInfo.ACTION_FOCUS);//模拟粘贴//item.performAction(AccessibilityNodeInfo.ACTION_PASTE);}}return result;}
六、通过无障碍服务获取微信收款信息的代码:
package net.zy13.skhelper.service;import android.accessibilityservice.AccessibilityService;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.PowerManager;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;import net.zy13.skhelper.MainApplication;
import net.zy13.skhelper.handle.PostHandle;
import net.zy13.skhelper.utils.LogUtil;
import net.zy13.skhelper.utils.PreferenceUtil;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;/*** 收款无障碍服务*/
public class SkAccessibilityService extends AccessibilityService{private PreferenceUtil preference;private PowerManager.WakeLock wakeLock = null;private PostHandle postHandle=null;/*** 当服务启动的时候会被调用*/@Overridepublic void onServiceConnected(){LogUtil.debug( ":无障碍服务连接成功");if(preference==null) {preference = PreferenceUtil.getInstance(MainApplication.getAppContext());}//开启wakelock,使CPU处于不休眠的状态,开启后需要重启手机if(preference.isWakelock()){PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, SkAccessibilityService.class.getName());wakeLock.acquire();}}/*** 监听窗口变化的回调*/@Overridepublic void onAccessibilityEvent(AccessibilityEvent event) {// 获取事件类型,在对应的事件类型中对相应的节点进行操作int eventType = event.getEventType();//根据事件回调类型进行处理switch (eventType) {//当通知栏发生改变时case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:break;//当窗口的状态发生改变时(界面改变)case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:String className = event.getClassName().toString();//获取界面信息getUiInfo(className);break;//内容改变case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED:break;//滑动变化case AccessibilityEvent.TYPE_VIEW_SCROLLED:break;}}/*** 中断服务的回调*/@Overridepublic void onInterrupt() {}/*** 通过控件ID获取节点信息* @param id* @return*/@SuppressLint("NewApi")public String getNodeInfo(String id){String result="";// 获取当前整个活动窗口的根节点AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();// 获取父节点//nodeInfo.getParent();// 获取子节点//nodeInfo.getChild(0);if (nodeInfo != null) {// 通过文本找到对应的节点集合// List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByText("");// 通过控件ID找到对应的节点集合,如com.tencent.mm:id/gdList<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByViewId(id);for (AccessibilityNodeInfo item : list) {String str=item.getText().toString();if (str != null && str.length() != 0){result=str;break;}//模拟点击//item.performAction(AccessibilityNodeInfo.ACTION_CLICK);//模拟长按//item.performAction(AccessibilityNodeInfo.ACTION_LONG_CLICK);//模拟获取焦点//item.performAction(AccessibilityNodeInfo.ACTION_FOCUS);//模拟粘贴//item.performAction(AccessibilityNodeInfo.ACTION_PASTE);}}return result;}/*** 获取界面信息* @param classname*/@SuppressLint("NewApi")public void getUiInfo(String classname){LogUtil.debug( "无障碍服务窗口状态改变,类名为"+classname);//通过类名判断是不是微信if(classname.equals("com.tencent.mm.ui.LauncherUI")) {LogUtil.debug( "正在使用无障碍服务获取微信收款信息:");String title=getNodeInfo("com.tencent.mm:id/fzg");if(title.contains("微信支付")||title.contains("微信收款助手")) {String content = getNodeInfo("com.tencent.mm:id/e7t");//String time = getNodeInfo("com.tencent.mm:id/j0l");Map<String,String> postmap=new HashMap<String,String>();postmap.put("type","weixin");postmap.put("title",title);postmap.put("money",extractMoney(content));postmap.put("content",content);SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");postmap.put("time",sdf.format(new Date()));LogUtil.debug("获取到的信息集合:"+postmap.toString());}}}/*** 从字符串里提取金额* @param content* @return*/protected String extractMoney(String content){Pattern pattern = Pattern.compile("(收款|收款¥|向你付款|向您付款|入账|到帐)(([1-9]{1}\\d*)|([0]{1}))(\\.(\\d){0,2})?元");Matcher matcher = pattern.matcher(content);List<String> list = new ArrayList<>();while(matcher.find()){list.add(matcher.group());}if(list.size()>0){String tmp=list.get(list.size()-1);Pattern patternnum = Pattern.compile("(([1-9]{1}\\d*)|([0]{1}))(\\.(\\d){0,2})?");Matcher matchernum = patternnum.matcher(tmp);if(matchernum.find())return matchernum.group();return null;}elsereturn null;}
}
1、现在在安卓模拟器里运行我们的APP,来监听微信的收款信息
2、通过查看debug日志 ,发现无障碍服务已经连接成功了,现在我用另一个微信号扫收款二维码进行付款,看看能不能监听到。
3、可以获取到收款信息,但有个bug,就是你要主动打开微信才可以获取到,如果微信退到后台或者关闭,无障碍服务就不能实时监听到收款信息了。
安卓使用无障碍服务监听微信和QQ的收款信息相关推荐
- linux系统中ntp服务监听端口是,Linux系统下测试UDP端口是否正常监听的办法
TCP端口,比如80端口,可使用 telnet 主机ip 80,来验证端口是否正常监听,那UDP端口是否可同样测试呢?关于UDP端口怎样测试监听的问题, 下面我们来进行测试,如:123端口是服务器 A ...
- 微信浏览器返回刷新,监听微信浏览器返回事件,网页防复制,移动端禁止图片长按和vivo手机点击img标签放大图片
以下代码都经过iphone7,华为MT7 ,谷歌浏览器,微信开发者工具,PC端微信验证.如有bug,还请在评论区留言. demo链接:https://pan.baidu.com/s/1c35mbjM ...
- 用Python机器人监听微信群聊
随着微信社交的兴起,我们加入的群也越来越多,一个不经意就被拉入好几个群,群是大家协同交流的平台,但是微信群却越来越泛滥,不知道大家有没有统计过自己浪费在毫无营养的群中的时间? 因为群质量太低或者群太吵 ...
- 用Python机器人监听微信群聊 !
随着微信社交的兴起,我们加入的群也越来越多,一个不经意就被拉入好几个群,群是大家协同交流的平台,但是微信群却越来越泛滥,不知道大家有没有统计过自己浪费在毫无营养的群中的时间? Python学习交流群: ...
- 用Python机器人监听微信群聊, 我看谁这么大的胆子敢调侃老板和前台小姐姐!
随着微信社交的兴起,我们加入的群也越来越多,一个不经意就被拉入好几个群,群是大家协同交流的平台,但是微信群却越来越泛滥,不知道大家有没有统计过自己浪费在毫无营养的群中的时间? 因为群质量太低或者群太吵 ...
- php h5支付没有返回app中,很多人都不知道的监听微信、支付宝等移动app及浏览器的返回、后退、上一页按钮的事件方法...
在实际的应用中,我们常常需要实现在移动app和浏览器中点击返回.后退.上一页等按钮实现自己的关闭页面.调整到指定页面或执行一些其它操作的 需求,那在代码中怎样监听当点击微信.支付宝.百度糯米.百度钱包 ...
- Flutter app 安卓iOS前后台切换监听
Flutter app 安卓iOS前后台切换监听 class _MyAppState extends State<MyApp> with WidgetsBindingObserver {@ ...
- php 微信监听返回键,JavaScript_JS监听微信、支付宝等移动app及浏览器的返回、后退、上一页按钮的事件方法,在实际的应用中,我们常常需 - phpStudy...
JS监听微信.支付宝等移动app及浏览器的返回.后退.上一页按钮的事件方法 在实际的应用中,我们常常需要实现在移动app和浏览器中点击返回.后退.上一页等按钮实现自己的关闭页面.调整到指定页面或执行一 ...
- 【监听微信公众号消息】
监听微信公众号消息 效果图如下: 环境要求 效果图如下: 环境要求 需要windows服务器,登陆上微信.获取到的消息是xml格式,需要解析内容获取自己想要的公众号推送的消息. 详情:https:// ...
最新文章
- android studio 通过界面快速查看md5
- opencv进阶学习笔记2:numpy操作图像,色彩空间,查找指定颜色范围,通道分离与合并
- 动画演示男性结扎手术 | 今日趣图
- DHCP+TFTP+PXE自动网络引导安装Linux操作系统
- Maxent猛犸反欺诈入选Gartner 2018 Cool Vendors
- Python with 结构
- 如何登录新浪微博html5,新浪微博怎么登陆
- EVE上传Dynamips和IOL
- MySQL索引面试题六连击
- 不会压测?没关系,手把手教你用jmeter做压力测试及结果分析
- 51单片机移植UCOS-II的方法与改进
- 键盘的基本使用(包含各种快捷键的使用)
- 对于产业互联网的认识,直接关系着我们究竟会以怎样的心态来看待它
- SEO小白学习与实践(二):SEO相关基础知识与自有网站诊断
- 作业1.老狼几点了。凌晨,上午,下午,晚上。
- 百度前端技术学院-斌斌学院-任务五
- 前端面试总结(持续更新中~~~~)
- LaTex 连字符多篇连续引用
- Insightful Thought on Load Balance Solution Choice
- 高通骁龙410C开发板基础知识
热门文章
- 天迹云通信短信分销系统源码
- php mysql首字母查询_MySQL应用技巧实现查询汉字的拼音首字母
- 大数据离线---网站日志流量分析系统(1)---简介及框架
- 2020年必知的15个中小型企业网络安全统计数据
- 高项论文练习--项目进度管理
- 前端实习一个月的收获与思考
- 【CSS】固定定位示例 ( 屏幕左右两侧广告栏 | 开发要点分析 | 代码示例 )
- 你有10万粉丝又如何,然而并没有什么卵用!
- 学计算机编程用苹果笔记本好么,苹果macbook好不好啊,想我想学编程用这电脑好不好,听别人说可能会不好用,是真的么...
- SQLIntegrityConstraintViolationException: Duplicate entry 'xxx' for key 'yyyzzz'异常解决