竟然可以检查微信是否被删了好友?(Android Accessibility 了解一下)
前言
最近在研究Android辅助服务,实现了这个小工具,也算是对最近学习的一个总结。
原理
通过Android 无障碍辅助功能实现模拟点击控件来实现
检查被删好友有两种方法:
- 向好友发送一条消息,如果对方已经把你删除,则消息发送失败。
- 建群法:新建一个不大于40人的群,如果其中有好友已经把你删除,微信会有条消息提示
- 整体执行步骤:启动微信->点击+号->发起群聊->选择35个联系人->点击确定->点击群里详情->删除并退出,依次轮询执行,知道所有好友轮询结束。
本文采用建群的方式进行检查。
本人微信有300好友,全部检测一遍只需3分钟即可,亲测已经成功,
但是建群没有超过40人 会有个别好友会受到打扰消息,可能是微信哪里的bug,具体原因未知。
说明和app预览
此软件通过无障碍辅助进行模拟点击,无任何外挂木马,无封号风险
使用方法
- Android 手机一部,登录微信账号
- 安装辅助软件apk下载地址请点击这里
- 打开辅助软件-点击打开辅助功能按钮,跳转到无障碍辅助设置把辅助开关打开。
- 点击开始检查按钮,开始一系列的模拟点击,检查完成后会跳转到一个列表会把被删好友列表展示出来。
实现步骤:
- 新建Android Studio 工程,新建一个Services类集成AccessibilityService,实现对应方法,详细介绍见代码注释
/*** Created by wanglj on 16/10/20.*/public class InspectWechatFriendService extends AccessibilityService{@Overrideprotected void onServiceConnected() {//辅助服务被打开后 执行此方法super.onServiceConnected();}@Overridepublic void onAccessibilityEvent(AccessibilityEvent accessibilityEvent) {//监听手机当前窗口状态改变 比如 Activity 跳转,内容变化,按钮点击等事件}@Overridepublic void onInterrupt() {//辅助服务被关闭 执行此方法}
}
- 在manifests.xml文件中注册此服务:
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.wanglj.inspectwechatfriend"xmlns:android="http://schemas.android.com/apk/res/android"><applicationandroid:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:supportsRtl="true"android:theme="@style/AppTheme"><activity android:name=".MainActivity"><intent-filter><action android:name="android.intent.action.MAIN"/><category android:name="android.intent.category.LAUNCHER"/></intent-filter></activity><serviceandroid:name=".accessibility.InspectWechatFriendService"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-dataandroid:name="android.accessibilityservice"android:resource="@xml/inspect_wechat_friend"/></service></application></manifest>
- 新建res/xml/inspect_wechat_friend.xml文件
<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"android:accessibilityEventTypes="typeWindowStateChanged" android:accessibilityFeedbackType="feedbackGeneric"android:accessibilityFlags=""android:canRetrieveWindowContent="true"android:notificationTimeout="100"android:description="@string/accessibility"/>
- 实现对某个控件的点击
通过getRootInActiveWindow方法获取当前窗口信息,通过findAccessibilityNodeInfosByText方法找到当前对应控件进行模拟点击
public class PerformClickUtils {/*** 在当前页面查找文字内容并点击** @param text*/public static void findTextAndClick(AccessibilityService accessibilityService,String text) {AccessibilityNodeInfo accessibilityNodeInfo = accessibilityService.getRootInActiveWindow();if (accessibilityNodeInfo == null) {return;}List<AccessibilityNodeInfo> nodeInfoList = accessibilityNodeInfo.findAccessibilityNodeInfosByText(text);if (nodeInfoList != null && !nodeInfoList.isEmpty()) {for (AccessibilityNodeInfo nodeInfo : nodeInfoList) {if (nodeInfo != null && (text.equals(nodeInfo.getText()) || text.equals(nodeInfo.getContentDescription()))) {performClick(nodeInfo);break;}}}}/*** 检查viewId进行点击* * @param accessibilityService* @param id*/public static void findViewIdAndClick(AccessibilityService accessibilityService,String id) {AccessibilityNodeInfo accessibilityNodeInfo = accessibilityService.getRootInActiveWindow();if (accessibilityNodeInfo == null) {return;}List<AccessibilityNodeInfo> nodeInfoList = accessibilityNodeInfo.findAccessibilityNodeInfosByViewId(id);if (nodeInfoList != null && !nodeInfoList.isEmpty()) {for (AccessibilityNodeInfo nodeInfo : nodeInfoList) {if (nodeInfo != null) {performClick(nodeInfo);break;}}}}/*** 在当前页面查找对话框文字内容并点击** @param text1 默认点击text1* @param text2*/public static void findDialogAndClick(AccessibilityService accessibilityService,String text1, String text2) {AccessibilityNodeInfo accessibilityNodeInfo = accessibilityService.getRootInActiveWindow();if (accessibilityNodeInfo == null) {return;}List<AccessibilityNodeInfo> dialogWait = accessibilityNodeInfo.findAccessibilityNodeInfosByText(text1);List<AccessibilityNodeInfo> dialogConfirm = accessibilityNodeInfo.findAccessibilityNodeInfosByText(text2);if (!dialogWait.isEmpty() && !dialogConfirm.isEmpty()) {for (AccessibilityNodeInfo nodeInfo : dialogWait) {if (nodeInfo != null && text1.equals(nodeInfo.getText())) {performClick(nodeInfo);break;}}}}//模拟点击事件public static void performClick(AccessibilityNodeInfo nodeInfo) {if (nodeInfo == null) {return;}if (nodeInfo.isClickable()) {nodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK);} else {performClick(nodeInfo.getParent());}} //模拟返回事件public static void performBack(AccessibilityService service) {if (service == null) {return;}if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {try {Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}service.performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK);}}
}
- 监听窗口事件
获取当前窗口的classname 通过classname进行判断当前手机处于某个界面
下面代码逻辑:
- 如果当前为微信主页面,则点击+号然后点击发起群聊
- 如果当前页面为创建群聊选择联系人界面,则开启一个while循环模拟滚动时间以及点击选择框,当选择用户到39人时,则模拟点击确定按钮发起群聊。
- 发起群聊后,微信会返回哪些用户不是你的好友,这个时候,取到当前控件的字符串并截取用户列表保存到本地。
- 获取到不是好友的用户后,点击右上角进入群聊详情,点击删除并退出
- 退出后又回到微信主页面,依次执行1 2 3 4步骤,直到滚动到联系人最底部为止。
- 当所有用户执行完成后,则启动检查结果界面,列出所有被删好友。
下面为对应逻辑代码:
@Overridepublic void onAccessibilityEvent(AccessibilityEvent accessibilityEvent) {//监听手机当前窗口状态改变 比如 Activity 跳转,内容变化,按钮点击等事件//如果手机当前界面的窗口发送变化if (accessibilityEvent.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {//获取当前activity的类名:String currentWindowActivity = accessibilityEvent.getClassName().toString();if(!hasComplete){if ("com.tencent.mm.ui.contact.SelectContactUI".equals(currentWindowActivity)) {canChecked = true;createGroup();} else if ("com.tencent.mm.ui.chatting.ChattingUI".equals(currentWindowActivity)) {getDeleteFriend();} else if ("com.tencent.mm.plugin.chatroom.ui.ChatroomInfoUI".equals(currentWindowActivity)) {deleteGroup();}else if("com.tencent.mm.ui.LauncherUI".equals(currentWindowActivity)){PerformClickUtils.findTextAndClick(this,"更多功能按钮");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}PerformClickUtils.findTextAndClick(this,"发起群聊");}}else{nickNameList.clear();deleteList.clear();sortItems.clear();startActivity(new Intent(this, DeleteFriendListActivity.class));}}}/*** 模拟创建群组步骤*/private void createGroup() {AccessibilityNodeInfo accessibilityNodeInfo = getRootInActiveWindow();if (accessibilityNodeInfo == null) {return;}List<AccessibilityNodeInfo> listview = accessibilityNodeInfo.findAccessibilityNodeInfosByViewId(selectUI_listview_id);int count = 0;if (!listview.isEmpty()) {while (canChecked) {List<AccessibilityNodeInfo> checkboxList = accessibilityNodeInfo.findAccessibilityNodeInfosByViewId(selectUI_checkbox_id);List<AccessibilityNodeInfo> sortList = accessibilityNodeInfo.findAccessibilityNodeInfosByViewId(selectUI_sortitem_id);for(AccessibilityNodeInfo nodeInfo:sortList){if(nodeInfo != null && nodeInfo.getText()!= null){sortItems.add(nodeInfo.getText().toString());}}for (AccessibilityNodeInfo nodeInfo : checkboxList) {String nickname = nodeInfo.getParent().findAccessibilityNodeInfosByViewId(selectUI_nickname_id).get(0).getText().toString();Log.e(TAG, "nickname = " + nickname);if (!nickNameList.contains(nickname)) {nickNameList.add(nickname);performClick(nodeInfo);count++;if (count >= GROUP_COUNT || nickNameList.size() >= listview.get(0).getCollectionInfo().getRowCount() - sortItems.size() - 2) {List<AccessibilityNodeInfo> createButtons = accessibilityNodeInfo.findAccessibilityNodeInfosByViewId(selectUI_create_button_id);if (!createButtons.isEmpty()) {performClick(createButtons.get(0));}if(nickNameList.size() >= listview.get(0).getCollectionInfo().getRowCount() - sortItems.size() - 2){hasComplete = true;}return;}}}listview.get(0).performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}}/*** 模拟获取被删好友列表步骤*/private void getDeleteFriend() {AccessibilityNodeInfo accessibilityNodeInfo = getRootInActiveWindow();if (accessibilityNodeInfo == null) {return;}List<AccessibilityNodeInfo> nodeInfoList = accessibilityNodeInfo.findAccessibilityNodeInfosByViewId(chattingUI_message_id);for (AccessibilityNodeInfo nodeInfo : nodeInfoList) {if (nodeInfo != null && nodeInfo.getText() != null && nodeInfo.getText().toString().contains("你无法邀请未添加你为好友的用户进去群聊,请先向")) {String str = nodeInfo.getText().toString();str = str.replace("你无法邀请未添加你为好友的用户进去群聊,请先向", "");str = str.replace("发送朋友验证申请。对方通过验证后,才能加入群聊。", "");String[] arr = str.split("、");deleteList.addAll(Arrays.asList(arr));Preferences.saveDeleteFriends(this);Log.e(TAG, "deleteList.size():" + deleteList.size());Toast.makeText(this, "僵尸粉数量:" + deleteList.size(), Toast.LENGTH_SHORT).show();break;}}PerformClickUtils.findTextAndClick(this,"聊天信息");}/*** 退出群组步骤*/private void deleteGroup(){AccessibilityNodeInfo accessibilityNodeInfo = getRootInActiveWindow();if (accessibilityNodeInfo == null) {return;}List<AccessibilityNodeInfo> nodeInfoList = accessibilityNodeInfo.findAccessibilityNodeInfosByViewId(groupinfoUI_listview_id);if (!nodeInfoList.isEmpty()) {nodeInfoList.get(0).performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}nodeInfoList.get(0).performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);PerformClickUtils.findTextAndClick(this,"删除并退出");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}PerformClickUtils.findTextAndClick(this,"删除并退出");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}
// if(Utils.getVersion(this).equals(WECHAT_VERSION_27)){
// PerformClickUtils.findTextAndClick(this,"确定");
// }else{PerformClickUtils.findTextAndClick(this,"离开群聊");PerformClickUtils.findTextAndClick(this,"确定");
// }}}
ui automator viewer的使用
uiautomatorviewer可以检查当前手机的布局结构,如果想更精确的找到控件位置,uiautomatorviewer必不可少!
使用方法:
- 搭建Android开发环境,并设置环境变量,这里就不说了。
- 在Android Studio 中打开 terminal 窗口,或者在终端直接执行命令
$uiautomatorviewer
整体效果图:
竟然可以检查微信是否被删了好友?(Android Accessibility 了解一下)相关推荐
- 微信如何恢复删掉的好友,巧妙添加好友的方法汇总
好友删除了,微信如何恢复删掉的好友?最直接的办法当然是联系上好友,然后再重新加回TA.然而状况往往总是有点复杂,很多时候并不是因为一时手滑把好友删除了.而是和朋友闹了一点小矛盾,一时生气把TA删除了, ...
- 你好,我的Dev-C++ 5.15被烦人的360杀毒软件给删了,放到白名单里之后就360竟然把所有的浏览器全部删掉了,请问怎么办
markdown 你好,我的Dev-C++ 5.15被烦人的360杀毒软件给删了,放到白名单里之后就360竟然把所有的浏览器全部删掉了,请问怎么办 1+2=3 hello, world! 文章目录 m ...
- 微信无感知检测单项好友【WeTool 免费版】【微信如何检测单向好友?】
一.微信无感知检测单项好友 1.1 背景 因为最近业务的原因加了好多的陌生微信好友,难免有很多好友删了我,但是我还有他的微信,岂不是很占地方? 所以我琢磨如何检测这些单项好友[利用转账.拉群的方法太费 ...
- 微信APP支付服务端和Android 端详解及其demo
最近在开发APP微信支付和支付宝支付,Android 端和后端都是我自己开发的,发现两家公司的文档都不是很友好,特别是微信,接触过或者开发过的人都应该有所体会.因此我特意把开发的过程梳理了,做下记录, ...
- cocos2dx3.15接入微信SDK实现登录和分享android studio2.3.3
cocos2dx3.15接入微信SDK实现登录和分享android studio2.3.3,首先开始呢,我必须得吐槽一下网上的教程以及微信开放平台官网,网上的教程主要是太老了代码虽然都能用但是不完整有 ...
- 30 | 图的表示:如何存储微博、微信等社交网络中的好友关系?
列出功能需求->翻译成逻辑算法->抽象出数据结构->确定物理存储结构 后面的不会脱离前面的独立存在,只存在于工作流的运用中,所以不能把它们独立地看. 问题引入 在微博中,两个人可以互 ...
- 图的表示:如何存储微博、微信等社交网络中的好友关系
微博.微信.LinkedIn 这些社交软件我想你肯定都玩过吧.在微博中,两个人可以互相关注:在微信中,两个人可以互加好友.那你知道,如何存储微博.微信等这些社交网络的好友关系吗? 这就要用到我们今天要 ...
- python clicknium 微信发送消息以及获取好友列表
需求说明 给指定微信好友发消息 获取所有微信好友的微信号 环境准备 Windows 10 Visual Studio Code 1.69.2 Clicknium 0.1.2 Python 3.10.5 ...
- 【图的表示】:如何存储微博、微信等社交网络中的好友关系?
微博.微信.LinkedIn 这些社交软件我想你肯定都玩过吧.在微博中,两个人可以互相关注:在微信中,两个人可以互加好友.那你知道,如何存储微博.微信等这些社交网络的好友关系吗? 这就要用到我们今天要 ...
最新文章
- 漏洞高危 中危 低危的划分标准
- C语言 一个字符常量占几个字节
- php万年历月份处理_php实现万年历的完整代码
- 第三次学JAVA再学不好就吃翔(part17)--数组
- esb 和mq_使用保险丝结构管理MQ和ESB的大型部署,第一部分
- 计算机网络应用云计算,计算机网络云计算的类型
- python求解多元方程最优解_Python实现梯度下降算法求多元线性回归(二)
- DataGridView的单元格内容即时更新方法
- SAP License:SAP PM的应用从实施开始
- 在java中String类为什么要设计成final
- mysql binlog 备份_linux定时备份mysql数据库
- 《未来编年史》——关于地球未来2000年的预言
- chip_seq数据分析专题
- i.MX6ULL驱动开发 | 10 - 修改LCD驱动点亮LCD显示小企鹅logo
- 使用python合并多个pdf文件
- win10升级助手_Win10自带杀毒软件如此强大,大家却不爱用,究竟是为什么呢?...
- 新华三:照耀城市的数字演进之路
- 计算机sci检索,计算机方向国内EI检索、SCI检索的期刊目录
- 【商业信息】PNP ID注册名单 2019-05-21
- 微信指数来了,营销人又有新工具