原创作品,转载请注明出处,尊重别人的劳动果实。

2017.2.7更新:

*现在适配微信版本更加容易了,只需要替换一个Recourse-ID即可

*可以知道对方发的是小视频还是语音,并获取秒数。

*可以区分聊天信息中的图片或者表情

实现效果:

实时监听当前聊天页面的最新一条消息,效果如图:

                       

实现原理:

同样是利用AccessibilityService辅助服务,关于这个服务类还不了解的同学可以先看下我上一篇关于抢红包的博客,原理都一样:

http://www.cnblogs.com/cxk1995/p/6363574.html

1.首先我们先来看一下微信聊天界面的布局,查看方法:

AndroidStudio--Tools--Android--Android Device Monitor,点击:

2.如图我们可以看到,其实每一条微信聊天记录都是一个RelativeLayout:

3.再往下看,我们又可以发现,其实每一个RelativeLayout下面,又包含了一个TextView,还有一个LinearLayout

TextView就是聊天的时间

LinearLayout下则包含了我们所需要的聊天对象以及聊天信息,除了文字聊天,语音,图片等的聊天信息都会在这个LinearLayout下,看图2

4.这里聊天对象比较容易获得,我们先放在前面讲,如上图我们可以看到有一个ImageView的描述内容里面包含着我们的聊天对象,可能后面还会有很多ImageView的参杂,将它与其他ImageView区分还有很重要两点,一是它是isClickable,二是它存在描述内容,并且描述内容是还包含有“头像”字眼。

综合过滤条件: "android.widget.ImageView"+"isClickable()"+"node.getContentDescription().toString().contains("头像")",代码如下:

    /**
* 遍历所有控件,找到头像Imagview,里面有对联系人的描述
*/
private void GetChatName(AccessibilityNodeInfo node) {
for (int i = 0; i < node.getChildCount(); i++) {
AccessibilityNodeInfo node1 = node.getChild(i);
if ("android.widget.ImageView".equals(node1.getClassName()) && node1.isClickable()) {
//获取聊天对象,这里两个if是为了确定找到的这个ImageView是头像的
if (!TextUtils.isEmpty(node1.getContentDescription())) {
ChatName = node1.getContentDescription().toString();
if (ChatName.contains("头像")) {
ChatName = ChatName.replace("头像", "");
}
} }
GetChatName(node1);
}
}

5.这里我们暂时把微信的聊天信息分为5种:

a.单纯的文字聊天信息:

其实从上面的右边图就可以看到,他就是一个TextView而已,如果你有去打印看看的话,你会发现他的parent父布局其实是一个RelativeLayout,后面同样可能会有其他的TeView干扰,例如我推荐了个名片或者发了个红包等,所以文字聊天TeView区分其他TextView的还有一个很重的过滤条件,就是它是可以长按的(isLongClickable()),这些属性都可以在Android Device Monitor中查看到。

综合过滤条件:"android.widget.TextView"+“android.widget.RelativeLayout”+“isLongClickable()”

b.发了一段语音聊天信息:

我们这里并没有办法获取到语音内容,只能获取语音的秒数,获取方法跟上面的一模一样,只不过这里它不能长按。我们知道语音的秒数格式都是:数字+双引号("),如60",所以我们只要判断取得的TextView内容是不是符合这种格式就行了,就能判断它是语音秒数。

综合过滤条件:"android.widget.TextView"+“android.widget.RelativeLayout”+“符合秒数格式”

c.发了一个表情:

这里的表情指的是收藏的或者自己下载的那些表情,不是指Emoji那些,他其实就是一个ImagView,父布局是一个LinearLayout。

综合过滤条件:"android.widget.ImageView"+"android.widget.LinearLayout"

d.发了一张图片:

这里目前只能做到监听到安装服务的这一端发过去的图片,不能监听到对面发过来的啧啧,其实也是一个ImageView,父布局是FrameLayout,而且还有很重要一点,该节点存在描述内容字眼:"图片"。

综合过滤条件:"android.widget.ImageView"+"android.widget.FrameLayout"+"node.getContentDescription().toString().contains("图片")"

e.发了一段小视频:

同样我们无法知道视频的内容,这里只是单纯的获取视频的秒数,和语音类似,但是获取过滤方法不同,其实小视频秒数也就是个TextView,并且它的父布局是FrameLayout,我们知道视频的秒数都是符合:00:00这种格式的,所以这个也是个很重要的过滤条件。

综合过滤条件:"android.widget.TextView"+“android.widget.FrameLayout”+“符合 00:00格式”

4.分析完后,我们思路就有了:

a.首先我们先取得根布局的节点,然后通过遍历获取到每个RelativeLayout下的LinearLayout,因为该LinearLayout存在resource-id(com.tencent.mm:id/p,微信版本6.5.4),所以我们可以很容易可以获取到符合该ID的所有LinearLayout,然后我们取出最后一个LinearLayout,这个也就是装载着我们最新的那条消息啦。

b.然后我们再在该LinearLayout下遍历它的所有控件,通过上面所讲的各种过滤条件,判断发的是什么类型的消息并取出我们所需要的即可。

注:关于resource-id直接在上一步的查看布局下可看到,因为resource-id随着版本的迭代可能也会发生改变,Demo中那个LinearLayout的resource-id是基于微信6.5.4滴,如果以后有版本更新的话我们直接修改代码中的那个ID就行啦。

获取聊天信息核心代码:

代码不多,也加了注释,直接看代码即可:

 /**
* 遍历所有控件:这里分四种情况
* 文字聊天: 一个TextView,并且他的父布局是android.widget.RelativeLayout
* 语音的秒数: 一个TextView,并且他的父布局是android.widget.RelativeLayout,但是他的格式是0"的格式,所以可以通过这个来区分
* 图片:一个ImageView,并且他的父布局是android.widget.FrameLayout,描述中包含“图片”字样(发过去的图片),发回来的图片现在还无法监听
* 表情:也是一个ImageView,并且他的父布局是android.widget.LinearLayout
* 小视频的秒数:一个TextView,并且他的父布局是android.widget.FrameLayout,但是他的格式是00:00"的格式,所以可以通过这个来区分
*
* @param node
*/
public void GetChatRecord(AccessibilityNodeInfo node) {
for (int i = 0; i < node.getChildCount(); i++) {
AccessibilityNodeInfo nodeChild = node.getChild(i); //聊天内容是:文字聊天(包含语音秒数)
if ("android.widget.TextView".equals(nodeChild.getClassName()) && "android.widget.RelativeLayout".equals(nodeChild.getParent().getClassName().toString())) {
if (!TextUtils.isEmpty(nodeChild.getText())) {
String RecordText = nodeChild.getText().toString();
//这里加个if是为了防止多次触发TYPE_VIEW_SCROLLED而打印重复的信息
if (!RecordText.equals(ChatRecord)) {
ChatRecord = RecordText;
//判断是语音秒数还是正常的文字聊天,语音的话秒数格式为5"
if (ChatRecord.contains("\"")) {
Toast.makeText(this, ChatName + "发了一条" + ChatRecord + "的语音", Toast.LENGTH_SHORT).show(); Log.e("WeChatLog",ChatName + "发了一条" + ChatRecord + "的语音");
} else {
//这里在加多一层过滤条件,确保得到的是聊天信息,因为有可能是其他TextView的干扰,例如名片等
if (nodeChild.isLongClickable()) {
Toast.makeText(this, ChatName + ":" + ChatRecord, Toast.LENGTH_SHORT).show(); Log.e("WeChatLog",ChatName + ":" + ChatRecord);
} }
return;
}
}
} //聊天内容是:表情
if ("android.widget.ImageView".equals(nodeChild.getClassName()) && "android.widget.LinearLayout".equals(nodeChild.getParent().getClassName().toString())) {
Toast.makeText(this, ChatName+"发的是表情", Toast.LENGTH_SHORT).show(); Log.e("WeChatLog",ChatName+"发的是表情"); return;
} //聊天内容是:图片
if ("android.widget.ImageView".equals(nodeChild.getClassName())) {
//安装软件的这一方发的图片(另一方发的暂时没实现)
if("android.widget.FrameLayout".equals(nodeChild.getParent().getClassName().toString())){
if(!TextUtils.isEmpty(nodeChild.getContentDescription())){
if(nodeChild.getContentDescription().toString().contains("图片")){
Toast.makeText(this, ChatName+"发的是图片", Toast.LENGTH_SHORT).show(); Log.e("WeChatLog",ChatName+"发的是图片");
}
}
}
} //聊天内容是:小视频秒数,格式为00:00
if ("android.widget.TextView".equals(nodeChild.getClassName()) && "android.widget.FrameLayout".equals(nodeChild.getParent().getClassName().toString())) {
if (!TextUtils.isEmpty(nodeChild.getText())) {
String second = nodeChild.getText().toString().replace(":", "");
//正则表达式,确定是不是纯数字,并且做重复判断
if (second.matches("[0-9]+") && !second.equals(VideoSecond)) {
VideoSecond = second;
Toast.makeText(this, ChatName + "发了一段" + nodeChild.getText().toString() + "的小视频", Toast.LENGTH_SHORT).show(); Log.e("WeChatLog","发了一段" + nodeChild.getText().toString() + "的小视频");
}
} } GetChatRecord(nodeChild);
}
}

使用方法:

设置-辅助功能-无障碍-点击WeChatLog开启即可(或者在设置中查找辅助功能等)

已知Bug:

没安装服务的另一方发的图片暂时无法监听到,后面改善

图片和表情没做重复信息过滤处理,所以如果触发了TYPE_VIEW_SCROLLED并且最新那条是这两个的话会出现重复。

华为7.0系统无法使用

写在最后:

个人兴趣研究,不建议用在非法途径上!!

上面是大部分的核心代码,不是完整的Demo,其实也就一个服务类而已,想要Demo的留言我发给你。

欢迎一起讨论学习:471497226@qq.com

Android几行代码实现监听微信聊天的更多相关文章

  1. Android几行代码实现实时监听微信聊天

    实现效果: 实时监听当前聊天页面的最新一条消息,如图:            实现原理: 同样是利用AccessibilityService辅助服务,关于这个服务类还不了解的同学可以先看下我上一篇关于 ...

  2. 拉仇恨!webhook + 企业微信给同事做了个代码提交监听工具

    本文案例收录在 https://github.com/chengxy-nds/Springboot-Notebook 大家好,我是小富~ 最近接个任务,用webhook做了个代码提交监听功能,就是有人 ...

  3. 微信浏览器返回刷新,监听微信浏览器返回事件,网页防复制,移动端禁止图片长按和vivo手机点击img标签放大图片

    以下代码都经过iphone7,华为MT7 ,谷歌浏览器,微信开发者工具,PC端微信验证.如有bug,还请在评论区留言. demo链接:https://pan.baidu.com/s/1c35mbjM ...

  4. android BroadcastReceiver ACTION_TIME_TICK 系统时间监听不到

    android BroadcastReceiver ACTION_TIME_TICK 系统时间监听不到 今天做android上的消息推送,启动了一个独立service,然后在里面监听系统的ACTION ...

  5. 监听微信端,手机端,ios端的浏览器返回事件,pc端关闭事件

    直接上代码了,可以监听微信端,手机端,iOS端的浏览器返回事件,关闭事件不支持 当进入该页面,我们就给这个history压入一个本地的连接.当点击返回.后退及上一页的操作时,就进行监听,在监听代码中实 ...

  6. android的Home键的监听封装工具类(一)

    android的Home键的监听封装: package com.gzcivil.utils; import android.content.BroadcastReceiver; import andr ...

  7. 《Android第一行代码》笔记

    学习Android开发差点儿相同有两年时间了.期间也做了大大小小的一些项目.近来抽出闲暇想把Android基础强化一下,之前在网上看到了郭霖郭大神的几篇博客.从中受益不少.于是花了近一周时间看完了郭神 ...

  8. 20 行代码极速为 App 加上聊天功能

    现在很多 App 都需要集成 IM 功能,今天就为大家分享一下集成 IM 基本功能的步骤.本文内容以 JMessage 为例.极光 IM ( JMessage ) = 极光推送 ( JPush ) + ...

  9. Android之自定义AlertDialog无法监听控件

    参考:http://www.cnblogs.com/511mr/archive/2011/10/21/2220253.html 要做一个自定义的弹出框,以前都是用一个Activity来实现,总觉得不是 ...

随机推荐

  1. 红帽7配置samba文件共享服务

    samba软件主要功能是为客户机提供共享使用的文件夹. 使用的协议是SMB(TCP 139).CIFS(TCP445). 所需的软件包:samba 系统服务:smb 1.安装samba服务 ~]#yu ...

  2. CSS| position定位和float浮动

    对基础知识再度做个巩固和梳理. 一.position定位 (一):position的属性 1.absolute:生成绝对定位的元素,相对于最近一级定位不是static的父元素来进行定位: 2.rela ...

  3. 【转载】Please configure Android Sdk(android studio)解决办法

    https://blog.csdn.net/u011622280/article/details/79005453 studio就报Please configure Android Sdk,重启and ...

  4. Linux 下解压 rar 文件

    网上下载rar 压缩文件的使用,在linux在我们需要对其进行解压缩,这个时候,我们需要安装 rar相关的文件,来进行解压缩. 1. 下载 我们进入rarlab网站,进行下载 rar for linu ...

  5. JDOM 操作XML

    http://www.cnblogs.com/hoojo/archive/2011/08/11/2134638.html 可扩展标记语言——eXtensible Markup Language 用户可 ...

  6. 数据库迁移之从oracle 到 MySQL最简单的方法

    数据库迁移之从oracle 到 MySQL最简单的方法 因工作需要将oracle数据库换到MySQL数据库,数据量比较大,百万级别的数据,表也比较多,有没有一种既快捷又安全的方法呢?答案是肯定的,下面 ...

  7. SQLSERVER无排序生成序号

    实现方式:ROW_NUMBER() SELECT RowID=(ROW_NUMBER() OVER(ORDER BY(SELECT ))) FROM dbo.tbl_name 实现方式:IDENTIT ...

  8. Springboot连接MySQL8.0出现的问题

    以前用的是5.7版本的MySQL,在学习实践Springboot的时候顺带升级了一下8.0,遇到了一些坑,在这记录一下,有碰到同类问题的童鞋需要自取. 使用 navicat连接发现报错1251- Cl ...

  9. Python os.md

    os 便携式访问操作系统的特定功能.os模块提供了对特定平台模块(如posix, nt, mac)的封装, 函数提供的api在很多平台上都可以相同使用, 所以使用os模块会变得很方便. 但不是所有函数 ...

  10. 死磕nginx系列--nginx 限流配置

    限流算法 令牌桶算法 算法思想是: 令牌以固定速率产生,并缓存到令牌桶中: 令牌桶放满时,多余的令牌被丢弃: 请求要消耗等比例的令牌才能被处理: 令牌不够时,请求被缓存. 漏桶算法 算法思想是: 水( ...