update 机器人速度大幅度提升

This commit is contained in:
尹甲仑
2022-06-20 10:03:36 +08:00
parent 097a917349
commit 65aafa7f3c
14 changed files with 656 additions and 297 deletions

1
.gitignore vendored
View File

@@ -15,6 +15,7 @@ out/
# Gradle files # Gradle files
.gradle/ .gradle/
build/ build/
release/
# Local configuration file (sdk path, etc) # Local configuration file (sdk path, etc)
local.properties local.properties

Binary file not shown.

View File

@@ -1,20 +0,0 @@
{
"version": 1,
"artifactType": {
"type": "APK",
"kind": "Directory"
},
"applicationId": "org.yameida.worktool",
"variantName": "release",
"elements": [
{
"type": "SINGLE",
"filters": [],
"properties": [],
"versionCode": 1,
"versionName": "1",
"enabled": true,
"outputFile": "app-release.apk"
}
]
}

View File

@@ -20,6 +20,15 @@ object Demo {
//打印当前视图树 //打印当前视图树
// AccessibilityUtil.printNodeClazzTree(getRoot()) // AccessibilityUtil.printNodeClazzTree(getRoot())
//手机号添加好友
// WeworkOperationImpl.addFriendByPhone(WeworkMessageBean.Friend().apply {
// this.phone = "13010001000"
// this.markName = "hhh"
// this.markCorp = "corp"
// this.markExtra = "ex"
// this.tagList = arrayListOf("tag1", "tag2")
// })
//自动通过好友 //自动通过好友
// WeworkLoopImpl.getFriendRequest() // WeworkLoopImpl.getFriendRequest()

View File

@@ -167,6 +167,9 @@ public class WeworkMessageBean {
//对象名称(图片、文件、小程序等) //对象名称(图片、文件、小程序等)
public String objectName; public String objectName;
//添加好友
public Friend friend;
public WeworkMessageBean() {} public WeworkMessageBean() {}
public WeworkMessageBean(String receivedName, String receivedContent, int type, Integer roomType, List<String> titleList, List<SubMessageBean> messageList, String log) { public WeworkMessageBean(String receivedName, String receivedContent, int type, Integer roomType, List<String> titleList, List<SubMessageBean> messageList, String log) {
@@ -224,6 +227,22 @@ public class WeworkMessageBean {
public String job; public String job;
} }
//添加好友
public static class Friend {
//按手机号搜索
public String phone;
//好友姓名
public String name;
//备注名
public String markName;
//备注企业
public String markCorp;
//备注更多描述
public String markExtra;
//备注标签(推荐)
public List<String> tagList;
}
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) return true; if (this == o) return true;

View File

@@ -30,7 +30,7 @@ fun goHomeTab(title: String): Boolean {
var atHome = false var atHome = false
var find = false var find = false
while (!atHome) { while (!atHome) {
val list = getRoot().findAccessibilityNodeInfosByText("消息") val list = AccessibilityUtil.findAllByText(getRoot(), "消息", timeout = 0)
for (item in list) { for (item in list) {
if (item.parent.parent.parent.childCount == 5) { if (item.parent.parent.parent.childCount == 5) {
//处理侧边栏抽屉打开 //处理侧边栏抽屉打开
@@ -42,7 +42,7 @@ fun goHomeTab(title: String): Boolean {
} }
} }
atHome = true atHome = true
val tempList = getRoot().findAccessibilityNodeInfosByText(title) val tempList = AccessibilityUtil.findAllByText(getRoot(), title, timeout = 0)
for (tempItem in tempList) { for (tempItem in tempList) {
if (tempItem.parent.parent.parent.childCount == 5) { if (tempItem.parent.parent.parent.childCount == 5) {
AccessibilityUtil.performClick(tempItem) AccessibilityUtil.performClick(tempItem)
@@ -54,7 +54,6 @@ fun goHomeTab(title: String): Boolean {
} }
if (!atHome) { if (!atHome) {
backPress() backPress()
sleep(1500)
} }
} }
LogUtils.v("进入首页-${title}") LogUtils.v("进入首页-${title}")
@@ -65,7 +64,7 @@ fun goHomeTab(title: String): Boolean {
* 当前是否在首页 * 当前是否在首页
*/ */
fun isAtHome(): Boolean { fun isAtHome(): Boolean {
val list = getRoot().findAccessibilityNodeInfosByText("消息") val list = AccessibilityUtil.findAllByText(getRoot(), "消息", timeout = 0)
return list.count { it.parent.parent.parent.childCount == 5 } > 0 return list.count { it.parent.parent.parent.childCount == 5 } > 0
} }
@@ -106,30 +105,32 @@ fun getRoot(ignoreCheck: Boolean): AccessibilityNodeInfo {
* 后退 * 后退
*/ */
fun backPress() { fun backPress() {
val textView = AccessibilityUtil.findOneByClazz(getRoot(), Views.TextView) val textView = AccessibilityUtil.findOnceByClazz(getRoot(), Views.TextView)
if (textView != null && textView.text.isNullOrBlank() && AccessibilityUtil.performClick(textView)) { if (textView != null && textView.text.isNullOrBlank() && AccessibilityUtil.performClick(textView)) {
LogUtils.v("找到回退按钮") LogUtils.v("找到回退按钮")
} else { } else {
val ivButton = AccessibilityUtil.findOneByClazz(getRoot(), Views.ImageView) val ivButton = AccessibilityUtil.findOnceByClazz(getRoot(), Views.ImageView)
if (ivButton != null && ivButton.isClickable && AccessibilityUtil.findFrontNode(ivButton) == null) { if (ivButton != null && ivButton.isClickable && AccessibilityUtil.findFrontNode(ivButton) == null) {
LogUtils.d("未找到回退按钮 点击第一个IV按钮") LogUtils.d("未找到回退按钮 点击第一个IV按钮")
AccessibilityUtil.performClick(ivButton) AccessibilityUtil.performClick(ivButton)
} else { } else {
LogUtils.d("未找到回退按钮 点击第一个BT按钮") LogUtils.d("未找到回退按钮 点击第一个BT按钮")
val button = AccessibilityUtil.findOneByClazz(getRoot(), Views.Button) val button = AccessibilityUtil.findOnceByClazz(getRoot(), Views.Button)
if (button != null && button.childCount > 0) { if (button != null && button.childCount > 0) {
AccessibilityUtil.performClick(button.getChild(0)) AccessibilityUtil.performClick(button.getChild(0))
} else if (button != null) { } else if (button != null) {
AccessibilityUtil.performClick(button) AccessibilityUtil.performClick(button)
} else { } else {
LogUtils.d("未找到BT按钮") LogUtils.d("未找到BT按钮")
if (AccessibilityUtil.findTextAndClick(getRoot(), "确定")) { val confirm = AccessibilityUtil.findOnceByText(getRoot(), "确定")
if (confirm != null) {
LogUtils.d("尝试点击确定") LogUtils.d("尝试点击确定")
AccessibilityUtil.performClick(confirm)
} }
} }
} }
} }
sleep(1000) sleep(500)
} }
/** /**

View File

@@ -78,6 +78,7 @@ object MyLooper {
for (message in LinkedHashSet(messageList.list)) { for (message in LinkedHashSet(messageList.list)) {
getInstance().removeMessages(WeworkMessageBean.LOOP_RECEIVE_NEW_MESSAGE) getInstance().removeMessages(WeworkMessageBean.LOOP_RECEIVE_NEW_MESSAGE)
if (message.type == WeworkMessageBean.LOOP_RECEIVE_NEW_MESSAGE) { if (message.type == WeworkMessageBean.LOOP_RECEIVE_NEW_MESSAGE) {
WeworkController.enableLoopRunning = true
if (!WeworkController.mainLoopRunning) { if (!WeworkController.mainLoopRunning) {
getInstance().sendMessage(Message.obtain().apply { getInstance().sendMessage(Message.obtain().apply {
what = WeworkMessageBean.LOOP_RECEIVE_NEW_MESSAGE what = WeworkMessageBean.LOOP_RECEIVE_NEW_MESSAGE
@@ -134,6 +135,7 @@ object MyLooper {
WeworkMessageBean.PASS_ALL_FRIEND_REQUEST -> { WeworkMessageBean.PASS_ALL_FRIEND_REQUEST -> {
} }
WeworkMessageBean.ADD_FRIEND_BY_PHONE -> { WeworkMessageBean.ADD_FRIEND_BY_PHONE -> {
WeworkController.addFriendByPhone(message)
} }
WeworkMessageBean.SHOW_GROUP_INFO -> { WeworkMessageBean.SHOW_GROUP_INFO -> {
WeworkController.showGroupInfo(message) WeworkController.showGroupInfo(message)

View File

@@ -12,6 +12,7 @@ import org.yameida.worktool.model.WeworkMessageBean
object WeworkController { object WeworkController {
lateinit var weworkService: WeworkService lateinit var weworkService: WeworkService
var enableLoopRunning = false
var mainLoopRunning = false var mainLoopRunning = false
/** /**
@@ -21,6 +22,7 @@ object WeworkController {
@RequestMapping @RequestMapping
fun stopAndGoHome() { fun stopAndGoHome() {
LogUtils.d("stopAndGoHome()") LogUtils.d("stopAndGoHome()")
enableLoopRunning = false
mainLoopRunning = false mainLoopRunning = false
goHome() goHome()
} }
@@ -60,7 +62,7 @@ object WeworkController {
*/ */
@RequestMapping @RequestMapping
fun replyMessage(message: WeworkMessageBean): Boolean { fun replyMessage(message: WeworkMessageBean): Boolean {
LogUtils.d("replyMessage(): ${message.receivedContent}") LogUtils.d("replyMessage(): ${message.receivedName} ${message.originalContent} ${message.receivedContent}")
return WeworkOperationImpl.replyMessage( return WeworkOperationImpl.replyMessage(
message.titleList, message.titleList,
message.receivedName, message.receivedName,
@@ -214,6 +216,17 @@ object WeworkController {
) )
} }
/**
* 按手机号添加好友
* @see WeworkMessageBean.ADD_FRIEND_BY_PHONE
* @param message#friend 待添加用户列表
*/
@RequestMapping
fun addFriendByPhone(message: WeworkMessageBean): Boolean {
LogUtils.d("addFriendByPhone(): ${message.friend}")
return WeworkOperationImpl.addFriendByPhone(message.friend)
}
/** /**
* 展示群信息 * 展示群信息
* @see WeworkMessageBean.SHOW_GROUP_INFO * @see WeworkMessageBean.SHOW_GROUP_INFO

View File

@@ -47,7 +47,7 @@ object WeworkGetImpl {
if (!goHomeTab("")) { if (!goHomeTab("")) {
LogUtils.d("未找到我的信息") LogUtils.d("未找到我的信息")
goHomeTab("消息") goHomeTab("消息")
val firstTv = AccessibilityUtil.findAllByClazz(getRoot(), Views.TextView) val firstTv = AccessibilityUtil.findAllByClazz(getRoot(), Views.TextView, root = true)
.firstOrNull { it.text == null } .firstOrNull { it.text == null }
AccessibilityUtil.performClick(firstTv) AccessibilityUtil.performClick(firstTv)
sleep(1000) sleep(1000)
@@ -67,11 +67,10 @@ object WeworkGetImpl {
} }
} }
AccessibilityUtil.performClick(AccessibilityUtil.findOneByClazz(getRoot(), Views.ImageView)) AccessibilityUtil.performClick(AccessibilityUtil.findOneByClazz(getRoot(), Views.ImageView))
sleep(1500) val relativeLayoutList = AccessibilityUtil.findAllByClazz(getRoot(), Views.RelativeLayout, minSize = 70)
val relativeLayoutList = AccessibilityUtil.findAllByClazz(getRoot(), Views.RelativeLayout)
val myInfo = WeworkMessageBean.MyInfo() val myInfo = WeworkMessageBean.MyInfo()
for (relativeLayout in relativeLayoutList.filter { it.childCount >= 2 }) { for (relativeLayout in relativeLayoutList.filter { it.childCount >= 2 }) {
val textViewList = AccessibilityUtil.findAllByClazz(relativeLayout, Views.TextView) val textViewList = AccessibilityUtil.findAllOnceByClazz(relativeLayout, Views.TextView)
if (textViewList.size >= 2) { if (textViewList.size >= 2) {
val firstText = textViewList[0].text?.toString() val firstText = textViewList[0].text?.toString()
if (firstText == "姓名" && myInfo.name == null) { if (firstText == "姓名" && myInfo.name == null) {
@@ -105,6 +104,7 @@ object WeworkGetImpl {
weworkMessageBean.type = WeworkMessageBean.GET_MY_INFO weworkMessageBean.type = WeworkMessageBean.GET_MY_INFO
weworkMessageBean.myInfo = myInfo weworkMessageBean.myInfo = myInfo
WeworkController.weworkService.webSocketManager.send(weworkMessageBean) WeworkController.weworkService.webSocketManager.send(weworkMessageBean)
WeworkLoopImpl.startLoop()
return true return true
} }
@@ -114,30 +114,44 @@ object WeworkGetImpl {
fun getGroupInfoDetail(): WeworkMessageBean { fun getGroupInfoDetail(): WeworkMessageBean {
val weworkMessageBean = WeworkMessageBean() val weworkMessageBean = WeworkMessageBean()
weworkMessageBean.type = WeworkMessageBean.GET_GROUP_INFO weworkMessageBean.type = WeworkMessageBean.GET_GROUP_INFO
val tvManagerFlag = val tvManagerFlag = AccessibilityUtil.findOneByText(getRoot(), "微信用户创建", timeout = 2000)
AccessibilityUtil.findOneByText(getRoot(), "微信用户创建") //不是管理员的群可能没有微信用户创建 todo
// AccessibilityUtil.findOneByText(getRoot(), "全部群成员", timeout = 2000)
val button = AccessibilityUtil.findFrontNode(tvManagerFlag) val button = AccessibilityUtil.findFrontNode(tvManagerFlag)
val tvGroupName = AccessibilityUtil.findOneByClazz(button, Views.TextView) val tvGroupName = AccessibilityUtil.findOnceByClazz(button, Views.TextView)
if (tvGroupName != null && tvGroupName.text != null) { if (tvGroupName != null && tvGroupName.text != null) {
LogUtils.d("群名: " + tvGroupName.text) LogUtils.d("群名: " + tvGroupName.text)
weworkMessageBean.groupName = tvGroupName.text.toString() weworkMessageBean.groupName = tvGroupName.text.toString()
} else {
val groupNameTv = AccessibilityUtil.findOnceByText(getRoot(), "群聊名称")
if (groupNameTv != null) {
val tvList = AccessibilityUtil.findAllOnceByClazz(
groupNameTv.parent.parent.parent,
Views.TextView
)
if (tvList.size >= 2) {
val groupName = tvList[1]
LogUtils.d("群名: " + groupName.text)
weworkMessageBean.groupName = groupName.text.toString()
}
}
} }
val gridView = AccessibilityUtil.findOneByClazz(getRoot(), Views.GridView) val gridView = AccessibilityUtil.findOneByClazz(getRoot(), Views.GridView)
if (gridView != null && gridView.childCount >= 2) { if (gridView != null && gridView.childCount >= 2) {
val tvOwnerName = AccessibilityUtil.findOneByClazz(gridView.getChild(0), Views.TextView) val tvOwnerName = AccessibilityUtil.findOnceByClazz(gridView.getChild(0), Views.TextView)
if (tvOwnerName != null && tvOwnerName.text != null) { if (tvOwnerName != null && tvOwnerName.text != null) {
LogUtils.d("群主: " + tvOwnerName.text) LogUtils.d("群主: " + tvOwnerName.text)
weworkMessageBean.groupOwner = tvOwnerName.text.toString() weworkMessageBean.groupOwner = tvOwnerName.text.toString()
} }
} }
val tvCountFlag = AccessibilityUtil.findOneByText(getRoot(), "查看全部群成员") val tvCountFlag = AccessibilityUtil.findOnceByText(getRoot(), "查看全部群成员")
val tvCount = AccessibilityUtil.findBackNode(tvCountFlag) val tvCount = AccessibilityUtil.findBackNode(tvCountFlag)
if (tvCount != null && tvCount.text != null) { if (tvCount != null && tvCount.text != null) {
LogUtils.d("群成员: " + tvCount.text) LogUtils.d("群成员: " + tvCount.text)
val count = tvCount.text.toString().replace("", "") val count = tvCount.text.toString().replace("", "")
weworkMessageBean.groupNumber = count.toIntOrNull() weworkMessageBean.groupNumber = count.toIntOrNull()
} }
val tvAnnouncementFlag = AccessibilityUtil.findOneByText(getRoot(), "群公告") val tvAnnouncementFlag = AccessibilityUtil.findOnceByText(getRoot(), "群公告")
val tvAnnouncement = AccessibilityUtil.findBackNode(tvAnnouncementFlag) val tvAnnouncement = AccessibilityUtil.findBackNode(tvAnnouncementFlag)
if (tvAnnouncement != null && tvAnnouncement.text != null) { if (tvAnnouncement != null && tvAnnouncement.text != null) {
LogUtils.d("群公告: " + tvAnnouncement.text) LogUtils.d("群公告: " + tvAnnouncement.text)

View File

@@ -1,9 +1,9 @@
package org.yameida.worktool.service package org.yameida.worktool.service
import android.os.Message
import android.view.accessibility.AccessibilityNodeInfo import android.view.accessibility.AccessibilityNodeInfo
import androidx.core.text.isDigitsOnly import androidx.core.text.isDigitsOnly
import com.blankj.utilcode.util.LogUtils import com.blankj.utilcode.util.LogUtils
import org.yameida.worktool.Demo
import org.yameida.worktool.model.WeworkMessageBean import org.yameida.worktool.model.WeworkMessageBean
import org.yameida.worktool.service.WeworkController.mainLoopRunning import org.yameida.worktool.service.WeworkController.mainLoopRunning
import org.yameida.worktool.utils.* import org.yameida.worktool.utils.*
@@ -15,14 +15,35 @@ import java.lang.StringBuilder
*/ */
object WeworkLoopImpl { object WeworkLoopImpl {
val stopWords = arrayListOf("解析中")
var logIndex = 0 var logIndex = 0
/**
* 如果远端开启接收新消息则本地自动在队列任务结束后调用接收新消息
* 该方法在每个任务结束时调用
*/
fun startLoop(delay: Long = 0) {
LogUtils.d("startLoop() delay: $delay")
val myLooper = MyLooper.getInstance()
if (WeworkController.enableLoopRunning) {
myLooper.removeMessages(WeworkMessageBean.LOOP_RECEIVE_NEW_MESSAGE)
if (!mainLoopRunning) {
myLooper.sendMessageDelayed(Message.obtain().apply {
what = WeworkMessageBean.LOOP_RECEIVE_NEW_MESSAGE
obj = WeworkMessageBean().apply { type = WeworkMessageBean.LOOP_RECEIVE_NEW_MESSAGE }
}, delay)
}
}
}
fun mainLoop() { fun mainLoop() {
mainLoopRunning = true mainLoopRunning = true
try { try {
while (mainLoopRunning) { while (mainLoopRunning) {
if (WeworkRoomUtil.getRoomType(getRoot()) != WeworkMessageBean.ROOM_TYPE_UNKNOWN
&& getChatMessageList()) {
}
goHomeTab("消息") goHomeTab("消息")
//todo 下上滚动
if (getChatroomList() && getChatMessageList()) { if (getChatroomList() && getChatMessageList()) {
mainLoopRunning = false mainLoopRunning = false
break break
@@ -31,7 +52,7 @@ object WeworkLoopImpl {
mainLoopRunning = false mainLoopRunning = false
break break
} }
sleep(1000) sleep(500)
} }
} catch (e: Exception) { } catch (e: Exception) {
mainLoopRunning = false mainLoopRunning = false
@@ -43,13 +64,12 @@ object WeworkLoopImpl {
* 读取通讯录好友请求 * 读取通讯录好友请求
*/ */
fun getFriendRequest(): Boolean { fun getFriendRequest(): Boolean {
val list = getRoot().findAccessibilityNodeInfosByText("通讯录") val list = AccessibilityUtil.findAllByText(getRoot(), "通讯录", timeout = 0)
for (item in list) { for (item in list) {
if (item.parent.parent.parent.childCount == 5) { if (item.parent.parent.parent.childCount == 5) {
if (item.parent.childCount > 1) { if (item.parent.childCount > 1) {
LogUtils.d("通讯录有红点") LogUtils.d("通讯录有红点")
AccessibilityUtil.performClick(item) AccessibilityUtil.performClick(item)
sleep(500)
val addButton = AccessibilityUtil.findOneByText(getRoot(), "添加客户") val addButton = AccessibilityUtil.findOneByText(getRoot(), "添加客户")
val backNode = AccessibilityUtil.findBackNode(addButton) val backNode = AccessibilityUtil.findBackNode(addButton)
LogUtils.d(backNode?.className) LogUtils.d(backNode?.className)
@@ -81,6 +101,54 @@ object WeworkLoopImpl {
return false return false
} }
/**
* 聊天页
* 1.获取群名
* 2.获取消息列表
*/
fun getChatMessageList(): Boolean {
AccessibilityUtil.performScrollDown(getRoot(), 0)
val roomType = WeworkRoomUtil.getRoomType(getRoot())
var titleList = WeworkRoomUtil.getRoomTitle(getRoot())
if (titleList.contains("对方正在输入…")) {
titleList = WeworkRoomUtil.getFriendName()
}
if (titleList.size > 0) {
val title = titleList.joinToString()
LogUtils.i("聊天: $title")
log("聊天: $title")
val list = AccessibilityUtil.findOneByClazz(getRoot(), Views.ListView)
if (list != null) {
LogUtils.d("消息条数: " + list.childCount)
val messageList = arrayListOf<WeworkMessageBean.SubMessageBean>()
for (i in 0 until list.childCount) {
val item = list.getChild(i)
if (item != null && item.childCount > 0) {
messageList.add(parseChatMessageItem(item, roomType))
}
}
WeworkController.weworkService.webSocketManager.send(
WeworkMessageBean(
null, null,
WeworkMessageBean.TYPE_RECEIVE_MESSAGE_LIST,
roomType,
titleList,
messageList,
null
)
)
//todo 推迟执行获取新消息
//检查如果当前房间最后一条消息未变化则不推迟
startLoop(3500)
return true
} else {
LogUtils.e("未找到聊天消息列表")
error("未找到聊天消息列表")
}
}
return false
}
/** /**
* 查看好友请求并通过 * 查看好友请求并通过
*/ */
@@ -88,7 +156,7 @@ object WeworkLoopImpl {
val nameList = arrayListOf<String>() val nameList = arrayListOf<String>()
val imageView = AccessibilityUtil.findOneByClazz(getRoot(), Views.ImageView) val imageView = AccessibilityUtil.findOneByClazz(getRoot(), Views.ImageView)
if (imageView != null) { if (imageView != null) {
val textViewList = AccessibilityUtil.findAllByClazz(imageView.parent, Views.TextView) val textViewList = AccessibilityUtil.findAllOnceByClazz(imageView.parent, Views.TextView)
val filter = textViewList.filter { it.text != null && it.text.toString() != "微信" } val filter = textViewList.filter { it.text != null && it.text.toString() != "微信" }
if (filter.isNotEmpty()) { if (filter.isNotEmpty()) {
val tvNick = filter[0] val tvNick = filter[0]
@@ -110,7 +178,7 @@ object WeworkLoopImpl {
//回到上一页 //回到上一页
var retry = 5 var retry = 5
while (retry-- > 0 && !isAtHome()) { while (retry-- > 0 && !isAtHome()) {
val textView = AccessibilityUtil.findOneByText(getRoot(), "新的客户") val textView = AccessibilityUtil.findOnceByText(getRoot(), "新的客户")
if (textView == null) { if (textView == null) {
backPress() backPress()
} }
@@ -124,6 +192,13 @@ object WeworkLoopImpl {
* 读取聊天列表 * 读取聊天列表
*/ */
private fun getChatroomList(): Boolean { private fun getChatroomList(): Boolean {
if (logIndex % 3 == 0) {
AccessibilityUtil.performScrollUp(getRoot(), 0)
AccessibilityUtil.performScrollUp(getRoot(), 0)
AccessibilityUtil.performScrollUp(getRoot(), 0)
} else if (logIndex % 120 < 3) {
AccessibilityUtil.performScrollDown(getRoot(), 0)
}
if (logIndex++ % 15 == 0) { if (logIndex++ % 15 == 0) {
LogUtils.i("读取首页聊天列表") LogUtils.i("读取首页聊天列表")
log("读取首页聊天列表") log("读取首页聊天列表")
@@ -176,58 +251,12 @@ object WeworkLoopImpl {
break break
} }
} }
sleep(1000)
return true return true
} else { } else {
return false return false
} }
} }
/**
* 聊天页
* 1.获取群名
* 2.获取消息列表
*/
private fun getChatMessageList(): Boolean {
AccessibilityUtil.performScrollDown(getRoot(), 0)
val roomType = WeworkRoomUtil.getRoomType(getRoot())
var titleList = WeworkRoomUtil.getRoomTitle(getRoot())
if (titleList.contains("对方正在输入…")) {
titleList = WeworkRoomUtil.getFriendName()
}
if (titleList.size > 0) {
val title = titleList.joinToString()
LogUtils.i("聊天: $title")
log("聊天: $title")
val list = AccessibilityUtil.findOneByClazz(getRoot(), Views.ListView)
if (list != null) {
LogUtils.d("消息条数: " + list.childCount)
val messageList = arrayListOf<WeworkMessageBean.SubMessageBean>()
for (i in 0 until list.childCount) {
val item = list.getChild(i)
if (item != null && item.childCount > 0) {
messageList.add(parseChatMessageItem(item, roomType))
}
}
WeworkController.weworkService.webSocketManager.send(
WeworkMessageBean(
null, null,
WeworkMessageBean.TYPE_RECEIVE_MESSAGE_LIST,
roomType,
titleList,
messageList,
null
)
)
return true
} else {
LogUtils.e("未找到聊天消息列表")
error("未找到聊天消息列表")
}
}
return false
}
/** /**
* 解析消息列表里的一条消息 * 解析消息列表里的一条消息
*/ */
@@ -240,10 +269,10 @@ object WeworkLoopImpl {
val itemMessageList = arrayListOf<WeworkMessageBean.ItemMessageBean>() val itemMessageList = arrayListOf<WeworkMessageBean.ItemMessageBean>()
LogUtils.d("开始解析一条消息...") LogUtils.d("开始解析一条消息...")
//消息头(在消息主体上方 如时间信息) //消息头(在消息主体上方 如时间信息)
val linearLayoutItem = AccessibilityUtil.findOneByClazz(node, Views.LinearLayout, 1) val linearLayoutItem = AccessibilityUtil.findOnceByClazz(node, Views.LinearLayout, 1)
if (linearLayoutItem != null) { if (linearLayoutItem != null) {
val sb = StringBuilder("消息头: ") val sb = StringBuilder("消息头: ")
val tvList = AccessibilityUtil.findAllByClazz(linearLayoutItem, Views.TextView) val tvList = AccessibilityUtil.findAllOnceByClazz(linearLayoutItem, Views.TextView)
for (item in tvList.filter { it.text != null && !it.text.isNullOrBlank() }) { for (item in tvList.filter { it.text != null && !it.text.isNullOrBlank() }) {
val text = item.text.toString() val text = item.text.toString()
val itemMessage = WeworkMessageBean.ItemMessageBean(0, text) val itemMessage = WeworkMessageBean.ItemMessageBean(0, text)
@@ -253,41 +282,44 @@ object WeworkLoopImpl {
LogUtils.d(sb.toString()) LogUtils.d(sb.toString())
} }
//消息主体 //消息主体
val relativeLayoutItem = AccessibilityUtil.findOneByClazz(node, Views.RelativeLayout, 1) val relativeLayoutItem = AccessibilityUtil.findOnceByClazz(node, Views.RelativeLayout, 1)
if (relativeLayoutItem != null && relativeLayoutItem.childCount >= 2) { if (relativeLayoutItem != null && relativeLayoutItem.childCount >= 2) {
if (Views.ImageView.equals(relativeLayoutItem.getChild(0).className)) { if (Views.ImageView.equals(relativeLayoutItem.getChild(0).className)) {
LogUtils.v("头像在左边 本条消息发送者为其他联系人") LogUtils.v("头像在左边 本条消息发送者为其他联系人")
nameList.addAll(WeworkTextUtil.getNameList(node)) nameList.addAll(WeworkTextUtil.getNameList(node))
var textType = WeworkMessageBean.TEXT_TYPE_UNKNOWN var textType = WeworkMessageBean.TEXT_TYPE_UNKNOWN
val relativeLayoutContent = val relativeLayoutContent =
AccessibilityUtil.findOneByClazz(relativeLayoutItem, Views.RelativeLayout, 2) AccessibilityUtil.findOnceByClazz(relativeLayoutItem, Views.RelativeLayout, 2)
if (relativeLayoutContent != null) { if (relativeLayoutContent != null) {
// AccessibilityUtil.printNodeClazzTree(relativeLayoutContent)
textType = WeworkTextUtil.getTextType(relativeLayoutContent) textType = WeworkTextUtil.getTextType(relativeLayoutContent)
LogUtils.v("textType: $textType") LogUtils.v("textType: $textType")
val tvList = val tvList =
AccessibilityUtil.findAllByClazz(relativeLayoutContent, Views.TextView) AccessibilityUtil.findAllOnceByClazz(relativeLayoutContent, Views.TextView)
for (item in tvList.filter { it.text != null && !it.text.isNullOrBlank() }) { for (item in tvList.filter { it.text != null && !it.text.isNullOrBlank() }) {
val text = item.text.toString() val text = item.text.toString()
LogUtils.d(text) LogUtils.d(text)
if (text !in stopWords) {
val itemMessage = WeworkMessageBean.ItemMessageBean(2, text) val itemMessage = WeworkMessageBean.ItemMessageBean(2, text)
itemMessageList.add(itemMessage) itemMessageList.add(itemMessage)
} }
} }
}
message = WeworkMessageBean.SubMessageBean(0, textType, itemMessageList, nameList) message = WeworkMessageBean.SubMessageBean(0, textType, itemMessageList, nameList)
} else if (Views.ImageView.equals(relativeLayoutItem.getChild(1).className)) { } else if (Views.ImageView.equals(relativeLayoutItem.getChild(1).className)) {
LogUtils.v("头像在右边 本条消息发送者为自己") LogUtils.v("头像在右边 本条消息发送者为自己")
val tvList = AccessibilityUtil.findAllByClazz(relativeLayoutItem, Views.TextView) val tvList = AccessibilityUtil.findAllOnceByClazz(relativeLayoutItem, Views.TextView)
for (item in tvList.filter { it.text != null && !it.text.isNullOrBlank() }) { for (item in tvList.filter { it.text != null && !it.text.isNullOrBlank() }) {
val text = item.text.toString() val text = item.text.toString()
LogUtils.d(text) LogUtils.d(text)
if (text !in stopWords) {
val itemMessage = WeworkMessageBean.ItemMessageBean(2, text) val itemMessage = WeworkMessageBean.ItemMessageBean(2, text)
itemMessageList.add(itemMessage) itemMessageList.add(itemMessage)
} }
}
message = WeworkMessageBean.SubMessageBean(1, 0, itemMessageList, nameList) message = WeworkMessageBean.SubMessageBean(1, 0, itemMessageList, nameList)
} else { } else {
// 没有头像的消息(撤销消息、其他可能的系统消息) // 没有头像的消息(撤销消息、其他可能的系统消息)
val tvList = AccessibilityUtil.findAllByClazz(node, Views.TextView) val tvList = AccessibilityUtil.findAllOnceByClazz(node, Views.TextView)
for (item in tvList.filter { it.text != null && !it.text.isNullOrBlank() }) { for (item in tvList.filter { it.text != null && !it.text.isNullOrBlank() }) {
val text = item.text.toString() val text = item.text.toString()
LogUtils.d(text) LogUtils.d(text)
@@ -300,7 +332,7 @@ object WeworkLoopImpl {
} else { } else {
// 没有头像的消息(撤销消息、其他可能的系统消息) // 没有头像的消息(撤销消息、其他可能的系统消息)
val sb = StringBuilder("未发现头像 本条消息发送者未知") val sb = StringBuilder("未发现头像 本条消息发送者未知")
val tvList = AccessibilityUtil.findAllByClazz(node, Views.TextView) val tvList = AccessibilityUtil.findAllOnceByClazz(node, Views.TextView)
for (item in tvList.filter { it.text != null && !it.text.isNullOrBlank() }) { for (item in tvList.filter { it.text != null && !it.text.isNullOrBlank() }) {
val text = item.text.toString() val text = item.text.toString()
sb.append(text).append("/t") sb.append(text).append("/t")

View File

@@ -59,13 +59,15 @@ object WeworkOperationImpl {
) )
) { ) {
LogUtils.d("开始回复") LogUtils.d("开始回复")
sleep(1000)
sendChatMessage(receivedContent, "[自动回复]") sendChatMessage(receivedContent, "[自动回复]")
LogUtils.d("$title: 回复成功") LogUtils.d("$title: 回复成功")
WeworkLoopImpl.getChatMessageList()
return true return true
} else { } else {
LogUtils.d("$title: 回复失败") LogUtils.d("$title: 回复失败 直接发送答案")
error("$title: 回复失败 $receivedContent") error("$title: 回复失败 直接发送答案 $receivedContent")
sendChatMessage(receivedContent, "[自动回复]【$originalContent】@$receivedName\n")
WeworkLoopImpl.getChatMessageList()
} }
} else { } else {
LogUtils.d("$title: 回复失败") LogUtils.d("$title: 回复失败")
@@ -171,10 +173,10 @@ object WeworkOperationImpl {
if (newGroupName != null) { if (newGroupName != null) {
groupRename(newGroupName) groupRename(newGroupName)
} }
if (selectList != null) { if (!selectList.isNullOrEmpty()) {
groupAddMember(selectList, showMessageHistory) groupAddMember(selectList, showMessageHistory)
} }
if (removeList != null) { if (!removeList.isNullOrEmpty()) {
groupRemoveMember(removeList) groupRemoveMember(removeList)
} }
if (newGroupAnnouncement != null) { if (newGroupAnnouncement != null) {
@@ -201,32 +203,23 @@ object WeworkOperationImpl {
val node = AccessibilityUtil.scrollAndFindByText(getRoot(), "微盘") val node = AccessibilityUtil.scrollAndFindByText(getRoot(), "微盘")
if (node != null) { if (node != null) {
AccessibilityUtil.performClick(node) AccessibilityUtil.performClick(node)
sleep(2000) val buttonList = AccessibilityUtil.findAllByClazz(getRoot(), Views.Button, root = true)
val buttonList = AccessibilityUtil.findAllByClazz(getRoot(), Views.Button)
if (buttonList.size >= 4) { if (buttonList.size >= 4) {
AccessibilityUtil.performClick(buttonList[2]) AccessibilityUtil.performClick(buttonList[2])
sleep(1000)
AccessibilityUtil.findTextInput(getRoot(), objectName) AccessibilityUtil.findTextInput(getRoot(), objectName)
sleep(2000) val imageViewList = AccessibilityUtil.findAllByClazz(getRoot(), Views.ImageView, root = true)
val editText = AccessibilityUtil.findOneByClazz(getRoot(), Views.EditText)
val backNode = AccessibilityUtil.findBackNode(editText)
val imageViewList = AccessibilityUtil.findAllByClazz(backNode, Views.ImageView)
if (imageViewList.size >= 2) { if (imageViewList.size >= 2) {
AccessibilityUtil.performClick(imageViewList[1]) AccessibilityUtil.performClick(imageViewList[1])
sleep(2000)
val shareFileButton = AccessibilityUtil.findOneByDesc(getRoot(), "以原文件分享") val shareFileButton = AccessibilityUtil.findOneByDesc(getRoot(), "以原文件分享")
AccessibilityUtil.performClick(shareFileButton) AccessibilityUtil.performClick(shareFileButton)
sleep(2000)
val shareToWorkButton = AccessibilityUtil.findOneByText(getRoot(true), "发送给同事") val shareToWorkButton = AccessibilityUtil.findOneByText(getRoot(true), "发送给同事")
AccessibilityUtil.performClick(shareToWorkButton) AccessibilityUtil.performClick(shareToWorkButton)
sleep(2000)
relaySelectTarget(titleList, extraText) relaySelectTarget(titleList, extraText)
sleep(2000)
val stayButton = AccessibilityUtil.findOneByText(getRoot(), "留在企业微信") val stayButton = AccessibilityUtil.findOneByText(getRoot(), "留在企业微信")
AccessibilityUtil.performClick(stayButton) AccessibilityUtil.performClick(stayButton)
return true return true
} else { } else {
LogUtils.e("微盘未搜索到相关文件: $objectName") LogUtils.e("微盘未搜索到相关图片: $objectName")
} }
} else { } else {
LogUtils.e("未找到微盘内搜索") LogUtils.e("未找到微盘内搜索")
@@ -252,24 +245,16 @@ object WeworkOperationImpl {
val node = AccessibilityUtil.scrollAndFindByText(getRoot(), "微盘") val node = AccessibilityUtil.scrollAndFindByText(getRoot(), "微盘")
if (node != null) { if (node != null) {
AccessibilityUtil.performClick(node) AccessibilityUtil.performClick(node)
sleep(2000) val buttonList = AccessibilityUtil.findAllByClazz(getRoot(), Views.Button, root = true)
val buttonList = AccessibilityUtil.findAllByClazz(getRoot(), Views.Button)
if (buttonList.size >= 4) { if (buttonList.size >= 4) {
AccessibilityUtil.performClick(buttonList[2]) AccessibilityUtil.performClick(buttonList[2])
sleep(1000)
AccessibilityUtil.findTextInput(getRoot(), objectName) AccessibilityUtil.findTextInput(getRoot(), objectName)
sleep(2000) val imageViewList = AccessibilityUtil.findAllByClazz(getRoot(), Views.ImageView, root = true)
val editText = AccessibilityUtil.findOneByClazz(getRoot(), Views.EditText)
val backNode = AccessibilityUtil.findBackNode(editText)
val imageViewList = AccessibilityUtil.findAllByClazz(backNode, Views.ImageView)
if (imageViewList.size >= 2) { if (imageViewList.size >= 2) {
AccessibilityUtil.performClick(imageViewList[1]) AccessibilityUtil.performClick(imageViewList[1])
sleep(2000)
val shareFileButton = AccessibilityUtil.findOneByDesc(getRoot(), "转发") val shareFileButton = AccessibilityUtil.findOneByDesc(getRoot(), "转发")
AccessibilityUtil.performClick(shareFileButton) AccessibilityUtil.performClick(shareFileButton)
sleep(2000)
relaySelectTarget(titleList, extraText) relaySelectTarget(titleList, extraText)
sleep(2000)
return true return true
} else { } else {
LogUtils.e("微盘未搜索到相关文件: $objectName") LogUtils.e("微盘未搜索到相关文件: $objectName")
@@ -298,11 +283,9 @@ object WeworkOperationImpl {
val node = AccessibilityUtil.scrollAndFindByText(getRoot(), "用过的小程序") val node = AccessibilityUtil.scrollAndFindByText(getRoot(), "用过的小程序")
if (node != null) { if (node != null) {
AccessibilityUtil.performClick(node) AccessibilityUtil.performClick(node)
sleep(2000) val textViewList = AccessibilityUtil.findAllByClazz(getRoot(), Views.TextView, root = true)
val textViewList = AccessibilityUtil.findAllByClazz(getRoot(), Views.TextView)
if (textViewList.size > 3) { if (textViewList.size > 3) {
AccessibilityUtil.performClick(textViewList[2]) AccessibilityUtil.performClick(textViewList[2])
sleep(1000)
AccessibilityUtil.findTextInput(getRoot(), objectName) AccessibilityUtil.findTextInput(getRoot(), objectName)
sleep(2000) sleep(2000)
AccessibilityUtil.findListOneAndClick(getRoot(), 1) AccessibilityUtil.findListOneAndClick(getRoot(), 1)
@@ -336,31 +319,22 @@ object WeworkOperationImpl {
return false return false
} }
AccessibilityUtil.performClick(allButton) AccessibilityUtil.performClick(allButton)
sleep(1000)
val myFileButton = AccessibilityUtil.findOneByText(getRoot(), "共享空间") val myFileButton = AccessibilityUtil.findOneByText(getRoot(), "共享空间")
if (myFileButton == null) { if (myFileButton == null) {
LogUtils.e("未找到共享空间按钮") LogUtils.e("未找到共享空间按钮")
return false return false
} }
AccessibilityUtil.performClick(myFileButton) AccessibilityUtil.performClick(myFileButton)
sleep(2000) val buttonList = AccessibilityUtil.findAllByClazz(getRoot(), Views.Button, root = true)
val buttonList = AccessibilityUtil.findAllByClazz(getRoot(), Views.Button)
if (buttonList.size >= 4) { if (buttonList.size >= 4) {
AccessibilityUtil.performClick(buttonList[3]) AccessibilityUtil.performClick(buttonList[3])
sleep(1000)
AccessibilityUtil.findTextInput(getRoot(), objectName) AccessibilityUtil.findTextInput(getRoot(), objectName)
sleep(2000) val imageViewList = AccessibilityUtil.findAllByClazz(getRoot(), Views.ImageView, root = true)
val editText = AccessibilityUtil.findOneByClazz(getRoot(), Views.EditText)
val backNode = AccessibilityUtil.findBackNode(editText)
val imageViewList = AccessibilityUtil.findAllByClazz(backNode, Views.ImageView)
if (imageViewList.size >= 2) { if (imageViewList.size >= 2) {
AccessibilityUtil.performClick(imageViewList[1]) AccessibilityUtil.performClick(imageViewList[1])
sleep(2000)
val shareFileButton = AccessibilityUtil.findOneByDesc(getRoot(), "转发") val shareFileButton = AccessibilityUtil.findOneByDesc(getRoot(), "转发")
AccessibilityUtil.performClick(shareFileButton) AccessibilityUtil.performClick(shareFileButton)
sleep(2000)
relaySelectTarget(titleList, extraText) relaySelectTarget(titleList, extraText)
sleep(2000)
return true return true
} else { } else {
LogUtils.e("文档未搜索到相关文件: $objectName") LogUtils.e("文档未搜索到相关文件: $objectName")
@@ -371,6 +345,130 @@ object WeworkOperationImpl {
return false return false
} }
/**
* 手机号添加好友
* @see WeworkMessageBean.ADD_FRIEND_BY_PHONE
* @param friend 待添加用户列表
*/
fun addFriendByPhone(
friend: WeworkMessageBean.Friend
): Boolean {
goHome()
val list = AccessibilityUtil.findOneByClazz(getRoot(), Views.ListView)
if (list != null) {
val frontNode = AccessibilityUtil.findFrontNode(list)
val textViewList = AccessibilityUtil.findAllOnceByClazz(frontNode, Views.TextView)
if (textViewList.size >= 2) {
val searchButton: AccessibilityNodeInfo = textViewList[textViewList.size - 2]
val multiButton: AccessibilityNodeInfo = textViewList[textViewList.size - 1]
AccessibilityUtil.performClick(multiButton)
sleep(500)
val listViewList = AccessibilityUtil.findAllByClazz(getRoot(), Views.ListView, root = true)
if (!listViewList.isNullOrEmpty()) {
if (AccessibilityUtil.findTextAndClick(listViewList.last(), "添加客户")) {
AccessibilityUtil.findTextAndClick(getRoot(), "搜索手机号添加")
AccessibilityUtil.findTextInput(getRoot(), friend.phone.trim())
if (AccessibilityUtil.findTextAndClick(getRoot(), "网络查找手机")) {
val bothUsedTv = AccessibilityUtil.findOneByText(getRoot(), "对方同时使用")
if (bothUsedTv != null) {
if (AccessibilityUtil.performClick(
AccessibilityUtil.findOnceByClazz(
AccessibilityUtil.findBackNode(bothUsedTv),
Views.ImageView
)
)
) {
sleep(2000)
} else {
LogUtils.e("未找到可点击图标")
}
}
} else {
LogUtils.e("未找到查找手机选项")
}
if (AccessibilityUtil.findOneByText(getRoot(), "标签") != null) {
var markTv = AccessibilityUtil.findOnceByText(getRoot(), "设置备注和描述")
if (markTv == null) {
markTv = AccessibilityUtil.findOnceByText(getRoot(), "企业")
}
if (markTv == null) {
markTv = AccessibilityUtil.findOnceByText(getRoot(), "描述")
}
//设置备注
if (markTv != null && (friend.markName != null
|| friend.markCorp != null || friend.markExtra != null)
) {
AccessibilityUtil.performClick(markTv)
val etList =
AccessibilityUtil.findAllByClazz(getRoot(), Views.EditText, root = true, minSize = 5)
if (etList.size >= 5) {
if (friend.markName != null) {
AccessibilityUtil.editTextInput(etList[0], friend.markName)
}
if (friend.markCorp != null) {
AccessibilityUtil.editTextInput(etList[1], friend.markCorp)
}
if (friend.markExtra != null) {
AccessibilityUtil.editTextInput(etList[4], friend.markExtra)
}
}
AccessibilityUtil.findTextAndClick(getRoot(), "保存")
sleep(2000)
}
//设置标签
if (!friend.tagList.isNullOrEmpty()) {
if (AccessibilityUtil.findTextAndClick(getRoot(), "标签")) {
sleep(1000)
setFriendTags(friend.tagList)
sleep(1000)
}
}
//添加联系人
val imageView =
AccessibilityUtil.findOneByClazz(getRoot(), Views.ImageView)
if (imageView != null) {
val textViewList = AccessibilityUtil.findAllOnceByClazz(
imageView.parent,
Views.TextView
)
val filter =
textViewList.filter { it.text != null && it.text.toString() != "微信" }
if (filter.isNotEmpty()) {
val tvNick = filter[0]
LogUtils.d("好友昵称或备注名: " + tvNick.text)
}
}
if (AccessibilityUtil.findTextAndClick(getRoot(), "添加为联系人")) {
LogUtils.d("添加好友成功: " + friend.phone)
sleep(2000)
if (AccessibilityUtil.findTextAndClick(getRoot(), "发送添加邀请")) {
LogUtils.d("发送添加邀请成功: " + friend.phone)
}
} else {
if (AccessibilityUtil.findOnceByText(getRoot(), "发消息") != null) {
LogUtils.e("已经添加联系人,请勿重复添加")
} else {
LogUtils.e("未找到添加为联系人")
}
}
} else {
LogUtils.e("未找到标签")
}
} else {
LogUtils.e("未找到添加客户按钮")
}
} else {
LogUtils.e("未找到添加客户列表")
}
return true
} else {
LogUtils.e("未找到搜索按钮")
}
}
LogUtils.e("未找到聊天列表")
return false
}
/** /**
* 展示群信息 * 展示群信息
* @see WeworkMessageBean.SHOW_GROUP_INFO * @see WeworkMessageBean.SHOW_GROUP_INFO
@@ -409,20 +507,21 @@ object WeworkOperationImpl {
val list = AccessibilityUtil.findOneByClazz(getRoot(), Views.ListView) val list = AccessibilityUtil.findOneByClazz(getRoot(), Views.ListView)
if (list != null) { if (list != null) {
val frontNode = AccessibilityUtil.findFrontNode(list) val frontNode = AccessibilityUtil.findFrontNode(list)
val textViewList = AccessibilityUtil.findAllByClazz(frontNode, Views.TextView) val textViewList = AccessibilityUtil.findAllOnceByClazz(frontNode, Views.TextView)
if (textViewList.size >= 2) { if (textViewList.size >= 2) {
val searchButton: AccessibilityNodeInfo = textViewList[textViewList.size - 2] val searchButton: AccessibilityNodeInfo = textViewList[textViewList.size - 2]
val multiButton: AccessibilityNodeInfo = textViewList[textViewList.size - 1] val multiButton: AccessibilityNodeInfo = textViewList[textViewList.size - 1]
AccessibilityUtil.performClick(multiButton) AccessibilityUtil.performClick(multiButton)
sleep(1000) sleep(1000)
AccessibilityUtil.performClick(searchButton) AccessibilityUtil.performClick(searchButton)
//todo 搜索需要在循环内
sleep(1000) sleep(1000)
for (select in selectList) { for (select in selectList) {
AccessibilityUtil.findTextInput(getRoot(), select) AccessibilityUtil.findTextInput(getRoot(), select)
sleep(2000) sleep(2000)
val selectListView = AccessibilityUtil.findOneByClazz(getRoot(), Views.ListView) val selectListView = AccessibilityUtil.findOneByClazz(getRoot(), Views.ListView)
val imageView = val imageView =
AccessibilityUtil.findOneByClazz(selectListView, Views.ImageView) AccessibilityUtil.findOnceByClazz(selectListView, Views.ImageView)
if (imageView != null) { if (imageView != null) {
AccessibilityUtil.performClick(imageView) AccessibilityUtil.performClick(imageView)
} }
@@ -438,7 +537,7 @@ object WeworkOperationImpl {
AccessibilityUtil.findTextInput(getRoot(), extraText) AccessibilityUtil.findTextInput(getRoot(), extraText)
sleep(1000) sleep(1000)
} }
val sendButtonList = getRoot().findAccessibilityNodeInfosByText("发送") val sendButtonList = AccessibilityUtil.findAllByText(getRoot(), "发送", timeout = 0)
for (sendButton in sendButtonList.filter { it.text != null }) { for (sendButton in sendButtonList.filter { it.text != null }) {
if (sendButton.text == "发送" || sendButton.text == "发送(${selectList.size})") { if (sendButton.text == "发送" || sendButton.text == "发送(${selectList.size})") {
AccessibilityUtil.performClick(sendButton) AccessibilityUtil.performClick(sendButton)
@@ -468,10 +567,8 @@ object WeworkOperationImpl {
val textViewGroup = AccessibilityUtil.scrollAndFindByText(getRoot(), "客户群") val textViewGroup = AccessibilityUtil.scrollAndFindByText(getRoot(), "客户群")
if (AccessibilityUtil.performClick(textViewGroup)) { if (AccessibilityUtil.performClick(textViewGroup)) {
LogUtils.d("进入客户群应用") LogUtils.d("进入客户群应用")
sleep(2000)
val textView = AccessibilityUtil.findOneByText(getRoot(), "创建一个客户群") val textView = AccessibilityUtil.findOneByText(getRoot(), "创建一个客户群")
AccessibilityUtil.performClick(textView) AccessibilityUtil.performClick(textView)
sleep(3000)
return true return true
} else { } else {
LogUtils.d("未找到客户群应用") LogUtils.d("未找到客户群应用")
@@ -484,12 +581,11 @@ object WeworkOperationImpl {
*/ */
private fun groupRename(groupName: String): Boolean { private fun groupRename(groupName: String): Boolean {
if (WeworkRoomUtil.intoGroupManager()) { if (WeworkRoomUtil.intoGroupManager()) {
val textView = val textView = AccessibilityUtil.findOneByText(getRoot(), "微信用户创建")
AccessibilityUtil.findOneByText(getRoot(), "微信用户创建") //todo 微信用户创建可能找不到
val button = AccessibilityUtil.findFrontNode(textView) val button = AccessibilityUtil.findFrontNode(textView)
if (button != null) { if (button != null) {
AccessibilityUtil.performClick(button) AccessibilityUtil.performClick(button)
sleep(1000)
AccessibilityUtil.findTextInput(getRoot(), groupName) AccessibilityUtil.findTextInput(getRoot(), groupName)
val confirmButton = AccessibilityUtil.findOneByText(getRoot(), "确定") val confirmButton = AccessibilityUtil.findOneByText(getRoot(), "确定")
AccessibilityUtil.performClick(confirmButton) AccessibilityUtil.performClick(confirmButton)
@@ -520,7 +616,6 @@ object WeworkOperationImpl {
} else { } else {
AccessibilityUtil.performClick(gridView.getChild(gridView.childCount - 2)) AccessibilityUtil.performClick(gridView.getChild(gridView.childCount - 2))
} }
sleep(1000)
} else { } else {
LogUtils.e("未找到添加成员按钮") LogUtils.e("未找到添加成员按钮")
return false return false
@@ -528,32 +623,29 @@ object WeworkOperationImpl {
val list = AccessibilityUtil.findOneByClazz(getRoot(), Views.ListView) val list = AccessibilityUtil.findOneByClazz(getRoot(), Views.ListView)
if (list != null) { if (list != null) {
val frontNode = AccessibilityUtil.findFrontNode(list) val frontNode = AccessibilityUtil.findFrontNode(list)
val textViewList = AccessibilityUtil.findAllByClazz(frontNode, Views.TextView) val textViewList = AccessibilityUtil.findAllOnceByClazz(frontNode, Views.TextView)
if (textViewList.size >= 2) { if (textViewList.size >= 2) {
val multiButton = textViewList.lastOrNull() val multiButton = textViewList.lastOrNull()
AccessibilityUtil.performClick(multiButton)
sleep(1000)
for (select in selectList) { for (select in selectList) {
AccessibilityUtil.performClick(multiButton)
AccessibilityUtil.findTextInput(getRoot(), select) AccessibilityUtil.findTextInput(getRoot(), select)
sleep(2000)
val selectListView = val selectListView =
AccessibilityUtil.findOneByClazz(getRoot(), Views.ListView) AccessibilityUtil.findOneByClazz(getRoot(), Views.ListView)
val imageView = val imageView =
AccessibilityUtil.findOneByClazz(selectListView, Views.ImageView) AccessibilityUtil.findOneByClazz(selectListView, Views.ImageView, root = false)
if (imageView != null) {
AccessibilityUtil.performClick(imageView) AccessibilityUtil.performClick(imageView)
val textView = AccessibilityUtil.findOnceByClazz(getRoot(), Views.TextView)
if (textView != null && textView.text.isNullOrBlank()) {
AccessibilityUtil.performClick(textView)
} }
sleep(1000)
} }
if (showMessageHistory) { if (showMessageHistory) {
val button = AccessibilityUtil.findOneByText(getRoot(), "附带聊天记录") AccessibilityUtil.findTextAndClick(getRoot(), "聊天记录")
if (button != null) AccessibilityUtil.performClick(button)
} }
val confirmButton = val confirmButton =
AccessibilityUtil.findOneByText(getRoot(), "确定(${selectList.size})") AccessibilityUtil.findOneByText(getRoot(), "确定(${selectList.size})")
if (confirmButton != null) { if (confirmButton != null) {
AccessibilityUtil.performClick(confirmButton) AccessibilityUtil.performClick(confirmButton)
sleep(1000)
} else { } else {
LogUtils.e("未发现确认按钮: ") LogUtils.e("未发现确认按钮: ")
return false return false
@@ -574,6 +666,7 @@ object WeworkOperationImpl {
* 移除群成员/踢人 * 移除群成员/踢人
*/ */
private fun groupRemoveMember(removeList: List<String>): Boolean { private fun groupRemoveMember(removeList: List<String>): Boolean {
if (removeList.isNullOrEmpty()) return true
if (WeworkRoomUtil.intoGroupManager()) { if (WeworkRoomUtil.intoGroupManager()) {
val gridView = AccessibilityUtil.findOneByClazz(getRoot(), Views.GridView) val gridView = AccessibilityUtil.findOneByClazz(getRoot(), Views.GridView)
if (gridView != null && gridView.childCount >= 2) { if (gridView != null && gridView.childCount >= 2) {
@@ -582,7 +675,6 @@ object WeworkOperationImpl {
} else { } else {
AccessibilityUtil.performClick(gridView.getChild(gridView.childCount - 1)) AccessibilityUtil.performClick(gridView.getChild(gridView.childCount - 1))
} }
sleep(1000)
} else { } else {
LogUtils.e("未找到删除成员按钮") LogUtils.e("未找到删除成员按钮")
return false return false
@@ -590,28 +682,26 @@ object WeworkOperationImpl {
val list = AccessibilityUtil.findOneByClazz(getRoot(), Views.ListView) val list = AccessibilityUtil.findOneByClazz(getRoot(), Views.ListView)
if (list != null) { if (list != null) {
val frontNode = AccessibilityUtil.findFrontNode(list) val frontNode = AccessibilityUtil.findFrontNode(list)
val textViewList = AccessibilityUtil.findAllByClazz(frontNode, Views.TextView) val textViewList = AccessibilityUtil.findAllOnceByClazz(frontNode, Views.TextView)
if (textViewList.size >= 2) { if (textViewList.size >= 2) {
val multiButton = textViewList.lastOrNull() val multiButton = textViewList.lastOrNull()
AccessibilityUtil.performClick(multiButton)
sleep(1000)
for (select in removeList) { for (select in removeList) {
AccessibilityUtil.performClick(multiButton)
AccessibilityUtil.findTextInput(getRoot(), select) AccessibilityUtil.findTextInput(getRoot(), select)
sleep(2000)
val selectListView = val selectListView =
AccessibilityUtil.findOneByClazz(getRoot(), Views.ListView) AccessibilityUtil.findOneByClazz(getRoot(), Views.ListView)
val imageView = val imageView =
AccessibilityUtil.findOneByClazz(selectListView, Views.ImageView) AccessibilityUtil.findOneByClazz(selectListView, Views.ImageView, root = false)
if (imageView != null) {
AccessibilityUtil.performClick(imageView) AccessibilityUtil.performClick(imageView)
val textView = AccessibilityUtil.findOnceByClazz(getRoot(), Views.TextView)
if (textView != null && textView.text.isNullOrBlank()) {
AccessibilityUtil.performClick(textView)
} }
sleep(1000)
} }
val confirmButton = val confirmButton =
AccessibilityUtil.findOneByText(getRoot(), "移出(${removeList.size})") AccessibilityUtil.findOneByText(getRoot(), "移出(${removeList.size})")
if (confirmButton != null) { if (confirmButton != null) {
AccessibilityUtil.performClick(confirmButton) AccessibilityUtil.performClick(confirmButton)
sleep(1000)
} else { } else {
LogUtils.e("未发现移出按钮: ") LogUtils.e("未发现移出按钮: ")
return false return false
@@ -631,6 +721,7 @@ object WeworkOperationImpl {
/** /**
* 修改群公告 * 修改群公告
* 注:首次为发布 后续为编辑 * 注:首次为发布 后续为编辑
* 注2外部群为edittext 内部群为webview(只能追加文本)
*/ */
private fun groupChangeAnnouncement(groupAnnouncement: String? = null): Boolean { private fun groupChangeAnnouncement(groupAnnouncement: String? = null): Boolean {
if (groupAnnouncement == null) return true if (groupAnnouncement == null) return true
@@ -638,8 +729,7 @@ object WeworkOperationImpl {
val textView = AccessibilityUtil.findOneByText(getRoot(), "群公告") val textView = AccessibilityUtil.findOneByText(getRoot(), "群公告")
if (textView != null) { if (textView != null) {
AccessibilityUtil.performClick(textView) AccessibilityUtil.performClick(textView)
sleep(1000) val editButton = AccessibilityUtil.findOneByText(getRoot(), "编辑", timeout = 2000)
val editButton = AccessibilityUtil.findOneByText(getRoot(), "编辑")
if (editButton != null) { if (editButton != null) {
LogUtils.d("群公告编辑中: $groupAnnouncement") LogUtils.d("群公告编辑中: $groupAnnouncement")
AccessibilityUtil.performClick(editButton) AccessibilityUtil.performClick(editButton)
@@ -647,15 +737,15 @@ object WeworkOperationImpl {
} }
if (AccessibilityUtil.findTextInput(getRoot(), groupAnnouncement)) { if (AccessibilityUtil.findTextInput(getRoot(), groupAnnouncement)) {
LogUtils.d("群公告发布中: $groupAnnouncement") LogUtils.d("群公告发布中: $groupAnnouncement")
sleep(500) if (AccessibilityUtil.findTextAndClick(getRoot(), "发布")) {
val button = AccessibilityUtil.findOneByText(getRoot(), "发布") val publishButtonList = AccessibilityUtil.findAllByText(getRoot(), "发布")
AccessibilityUtil.performClick(button)
sleep(1000)
val publishButtonList = getRoot().findAccessibilityNodeInfosByText("发布")
if (publishButtonList.size >= 2) { if (publishButtonList.size >= 2) {
AccessibilityUtil.performClick(publishButtonList[1]) AccessibilityUtil.performClick(publishButtonList[1])
} }
sleep(3000) sleep(3000)
} else {
LogUtils.e("无法进行群公告发布: ")
}
} else { } else {
LogUtils.e("无法进行群公告发布和编辑: ") LogUtils.e("无法进行群公告发布和编辑: ")
return false return false
@@ -664,6 +754,8 @@ object WeworkOperationImpl {
LogUtils.e("未找到群公告按钮") LogUtils.e("未找到群公告按钮")
return false return false
} }
} else {
LogUtils.e("进入群管理页失败")
} }
return true return true
} }
@@ -672,36 +764,78 @@ object WeworkOperationImpl {
* 发送消息 * 发送消息
*/ */
private fun sendChatMessage(text: String, prefix: String = "") { private fun sendChatMessage(text: String, prefix: String = "") {
val editText = AccessibilityUtil.findOneByClazz(getRoot(), Views.EditText) if (AccessibilityUtil.findTextInput(getRoot(), prefix + text)) {
if (editText != null) { val sendButton = AccessibilityUtil.findAllByClazz(getRoot(), Views.Button)
AccessibilityUtil.editTextInput(editText, prefix + text) .firstOrNull { it.text == "发送" }
//输入完文字等待出现发送按钮
sleep(500)
} else {
LogUtils.e("未找到输入框")
error("未找到输入框")
}
var index = 0
while (index++ < 5) {
val buttonList = getRoot().findAccessibilityNodeInfosByText("发送")
var sendButton: AccessibilityNodeInfo? = null
for (button in buttonList) {
if (button.className == Views.Button) {
sendButton = button
}
}
if (sendButton != null) { if (sendButton != null) {
LogUtils.i("发送消息: \n$text") LogUtils.d("发送消息: \n$text")
log("发送消息: \n$text") log("发送消息: \n$text")
AccessibilityUtil.performClick(sendButton) AccessibilityUtil.performClick(sendButton)
sleep(500)
break
} else { } else {
LogUtils.e("未找到发送按钮") LogUtils.e("未找到发送按钮")
error("未找到发送按钮") error("未找到发送按钮")
} }
sleep(500) } else {
LogUtils.e("未找到输入框")
error("未找到输入框")
} }
} }
/**
* 设置好友标签
*/
private fun setFriendTags(tagList: List<String>): Boolean {
val tagList = if (tagList.size > 5) tagList.subList(0, 5) else tagList
val tvTag = AccessibilityUtil.findAllByText(getRoot(), "个人标签").lastOrNull()
if (tvTag != null) {
val list = AccessibilityUtil.findBackNode(tvTag)
if (list != null && list.childCount > 0) {
LogUtils.v("list.childCount: " + list.childCount)
val tvAdd = list.getChild(0)
val oldTagList = arrayListOf<String>()
for (i in 0 until list.childCount) {
val child = list.getChild(i)
if (child.className.equals(Views.TextView) && child.text != null) {
oldTagList.add(child.text.toString())
}
}
//不存在的标签先添加
for (tag in tagList) {
if (!oldTagList.contains(tag)) {
AccessibilityUtil.performClick(tvAdd)
sleep(500)
AccessibilityUtil.findTextInput(getRoot(), tag)
AccessibilityUtil.findTextAndClick(getRoot(), "确定")
sleep(1000)
}
}
//确认只选择列表里的标签
val count = list.childCount
for (i in 0 until count) {
val child = list.getChild(i)
if (child != null) {
val text = child.text
val selected = child.isSelected
LogUtils.v("text: $text selected: $selected")
if (tagList.count { it == text } > 0) {
if (!selected) {
AccessibilityUtil.performClick(child)
}
} else {
if (selected) {
AccessibilityUtil.performClick(child)
}
}
}
list.refresh()
}
if (AccessibilityUtil.findTextAndClick(getRoot(), "确定")) {
return true
}
}
}
LogUtils.e("未找到个人标签")
return false
}
} }

View File

@@ -11,6 +11,10 @@ import android.os.Bundle
import android.util.Log import android.util.Log
import android.view.accessibility.AccessibilityEvent import android.view.accessibility.AccessibilityEvent
import android.view.accessibility.AccessibilityNodeInfo import android.view.accessibility.AccessibilityNodeInfo
import com.blankj.utilcode.util.LogUtils
import org.yameida.worktool.service.getRoot
import java.lang.Exception
import java.lang.Thread.sleep
/** /**
* 1.查询类 * 1.查询类
@@ -45,6 +49,8 @@ import android.view.accessibility.AccessibilityNodeInfo
*/ */
object AccessibilityUtil { object AccessibilityUtil {
private const val tag = "AccessibilityUtil" private const val tag = "AccessibilityUtil"
private const val SHORT_INTERVAL = 100L
private const val SCROLL_INTERVAL = 300L
//编辑EditView(非粘贴 推荐) //编辑EditView(非粘贴 推荐)
fun editTextInput(nodeInfo: AccessibilityNodeInfo?, text: String): Boolean { fun editTextInput(nodeInfo: AccessibilityNodeInfo?, text: String): Boolean {
@@ -65,8 +71,8 @@ object AccessibilityUtil {
} }
//寻找第一个EditView编辑框并输入文本 //寻找第一个EditView编辑框并输入文本
fun findTextInput(nodeInfo: AccessibilityNodeInfo?, text: String): Boolean { fun findTextInput(nodeInfo: AccessibilityNodeInfo?, text: String, root: Boolean = true): Boolean {
val nodeInfo: AccessibilityNodeInfo = nodeInfo ?: return false if (root) {
val editText = findOneByClazz(nodeInfo, "android.widget.EditText") ?: return false val editText = findOneByClazz(nodeInfo, "android.widget.EditText") ?: return false
val arguments = Bundle() val arguments = Bundle()
arguments.putCharSequence( arguments.putCharSequence(
@@ -74,13 +80,22 @@ object AccessibilityUtil {
text text
) )
editText.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments) editText.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments)
} else {
val editText = findOnceByClazz(nodeInfo, "android.widget.EditText") ?: return false
val arguments = Bundle()
arguments.putCharSequence(
AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE,
text
)
editText.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments)
}
return true return true
} }
//寻找第一个列表并点击指定条目(默认点击第一个条目) //寻找第一个列表并点击指定条目(默认点击第一个条目)
fun findListOneAndClick(nodeInfo: AccessibilityNodeInfo, index: Int = 0): Boolean { fun findListOneAndClick(nodeInfo: AccessibilityNodeInfo, index: Int = 0): Boolean {
val rv = findOneByClazz(nodeInfo, "androidx.recyclerview.widget.RecyclerView") val rv = findOnceByClazz(nodeInfo, "androidx.recyclerview.widget.RecyclerView")
val lv = findOneByClazz(nodeInfo, "android.widget.ListView") val lv = findOnceByClazz(nodeInfo, "android.widget.ListView")
if (rv == null && lv == null) return false if (rv == null && lv == null) return false
if (rv != null && rv.childCount > index) { if (rv != null && rv.childCount > index) {
performClick(rv.getChild(index)) performClick(rv.getChild(index))
@@ -99,16 +114,14 @@ object AccessibilityUtil {
var index = 0 var index = 0
while (index++ < maxRetry) { while (index++ < maxRetry) {
performScrollUp(nodeInfo, 0) performScrollUp(nodeInfo, 0)
Thread.sleep(300) val node = findOnceByText(nodeInfo, text)
val node = findOneByText(nodeInfo, text)
if (node != null) { if (node != null) {
return node return node
} }
} }
while (index++ < maxRetry * 2) { while (index++ < maxRetry * 2) {
performScrollDown(nodeInfo, 0) performScrollDown(nodeInfo, 0)
Thread.sleep(300) val node = findOnceByText(nodeInfo, text)
val node = findOneByText(nodeInfo, text)
if (node != null) { if (node != null) {
return node return node
} }
@@ -249,6 +262,7 @@ object AccessibilityUtil {
val canScrollNodeList = findCanScrollNode(node) val canScrollNodeList = findCanScrollNode(node)
if (canScrollNodeList.size > index) { if (canScrollNodeList.size > index) {
canScrollNodeList[index].performAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD) canScrollNodeList[index].performAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD)
sleep(SCROLL_INTERVAL)
return true return true
} }
return false return false
@@ -260,6 +274,7 @@ object AccessibilityUtil {
val canScrollNodeList = findCanScrollNode(node) val canScrollNodeList = findCanScrollNode(node)
if (canScrollNodeList.size > index) { if (canScrollNodeList.size > index) {
canScrollNodeList[index].performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD) canScrollNodeList[index].performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD)
sleep(SCROLL_INTERVAL)
return true return true
} }
return false return false
@@ -301,56 +316,134 @@ object AccessibilityUtil {
service.performGlobalAction(AccessibilityService.GLOBAL_ACTION_HOME) service.performGlobalAction(AccessibilityService.GLOBAL_ACTION_HOME)
} }
//按描述寻找节点和子节点内的一个匹配项 /**
* 按描述寻找节点和子节点内的一个匹配项
* @param node 节点
* @param desc 描述
* @param timeout 检查超时时间
*/
fun findOneByDesc( fun findOneByDesc(
node: AccessibilityNodeInfo?, node: AccessibilityNodeInfo?,
desc: String, desc: String,
desc2: String? = null timeout: Long = 5000
): AccessibilityNodeInfo? {
var node = node ?: return null
val description = node.contentDescription?.toString()
if (description == desc) {
return node
}
val startTime = System.currentTimeMillis()
var currentTime = startTime
while (currentTime - startTime <= timeout) {
val result = findOnceByDesc(node, desc)
if (result != null) return result
sleep(SHORT_INTERVAL)
node = getRoot(true)
currentTime = System.currentTimeMillis()
}
Log.e(tag, "findOneByDesc: not found: $desc")
return null
}
fun findOnceByDesc(
node: AccessibilityNodeInfo?,
desc: String
): AccessibilityNodeInfo? { ): AccessibilityNodeInfo? {
if (node == null) return null if (node == null) return null
val description = node.contentDescription?.toString() val description = node.contentDescription?.toString()
if (description == desc || (desc2 != null && description == desc2)) { if (description == desc) {
return node return node
} }
for (i in 0 until node.childCount) { for (i in 0 until node.childCount) {
val result = findOneByDesc(node.getChild(i), desc, desc2) val result = findOnceByDesc(node.getChild(i), desc)
if (result != null) return result if (result != null) return result
} }
return null return null
} }
//按文本(关键词)寻找节点和子节点内的一个匹配项 /**
* 按文本(关键词)寻找节点和子节点内的一个匹配项
* @param node 节点
* @param text 关键词
* @param timeout 检查超时时间
*/
fun findOneByText( fun findOneByText(
node: AccessibilityNodeInfo?, node: AccessibilityNodeInfo?,
text: String, text: String,
text2: String? = null exact: Boolean = false,
timeout: Long = 5000,
root: Boolean = true
): AccessibilityNodeInfo? { ): AccessibilityNodeInfo? {
if (node == null) return null var node = node ?: return null
val startTime = System.currentTimeMillis()
var currentTime = startTime
while (currentTime - startTime <= timeout) {
val textViewList = node.findAccessibilityNodeInfosByText(text) val textViewList = node.findAccessibilityNodeInfosByText(text)
LogUtils.v("text: $text count: " + textViewList.size)
if (textViewList != null && textViewList.size > 0) { if (textViewList != null && textViewList.size > 0) {
for (textView in textViewList) { for (textView in textViewList) {
if (textView.text == text) { if (textView.text == text) {
return textView return textView
} }
} }
if (!exact) {
return textViewList[0] return textViewList[0]
} else if (text2 != null) }
return findOneByText(node, text2) }
sleep(SHORT_INTERVAL)
if (root) {
node = getRoot(true)
} else {
node.refresh()
}
currentTime = System.currentTimeMillis()
}
Log.e(tag, "findOneByText: not found: $text")
return null return null
} }
//按文本(关键词)寻找节点和子节点内的所有匹配项 fun findOnceByText(
node: AccessibilityNodeInfo?,
text: String,
exact: Boolean = false
): AccessibilityNodeInfo? {
return findOneByText(node, text, exact, 0)
}
/**
* 按文本(关键词)寻找节点和子节点内的所有匹配项
* @param node 节点
* @param text 关键词
* @param timeout 检查超时时间
*/
fun findAllByText( fun findAllByText(
node: AccessibilityNodeInfo?, node: AccessibilityNodeInfo?,
text: String, text: String,
text2: String? = null exact: Boolean = false,
timeout: Long = 5000,
root: Boolean = true
): List<AccessibilityNodeInfo> { ): List<AccessibilityNodeInfo> {
if (node == null) return arrayListOf() var node = node ?: return arrayListOf()
val textViewList = node.findAccessibilityNodeInfosByText(text) val startTime = System.currentTimeMillis()
if (textViewList != null && textViewList.size > 0) var currentTime = startTime
return textViewList while (currentTime - startTime <= timeout) {
else if (text2 != null) val tvList = node.findAccessibilityNodeInfosByText(text)
return findAllByText(node, text2) if (tvList != null && tvList.size > 0) {
if (!exact) {
return tvList
} else if (tvList.count { it.text == text } > 0) {
return tvList.filter { it.text == text }
}
}
sleep(SHORT_INTERVAL)
if (root) {
node = getRoot(true)
} else {
node.refresh()
}
currentTime = System.currentTimeMillis()
}
Log.e(tag, "findAllByText: not found: $text")
return arrayListOf() return arrayListOf()
} }
@@ -359,21 +452,59 @@ object AccessibilityUtil {
* node 节点 * node 节点
* clazz 类名 * clazz 类名
* limitDepth 深度 限制深度搜索深度必须匹配提供值且类名相同才返回 不填默认不限制 * limitDepth 深度 限制深度搜索深度必须匹配提供值且类名相同才返回 不填默认不限制
* timeout 检查超时时间
*/ */
fun findOneByClazz( fun findOneByClazz(
node: AccessibilityNodeInfo?,
clazz: String,
limitDepth: Int? = null,
depth: Int = 0,
timeout: Long = 5000,
root: Boolean = true
): AccessibilityNodeInfo? {
var node = node ?: return null
if (node.className == clazz) {
if (limitDepth == null || limitDepth == depth)
return node
}
val startTime = System.currentTimeMillis()
var currentTime = startTime
while (currentTime - startTime <= timeout) {
val result = findOnceByClazz(node, clazz, limitDepth, depth)
LogUtils.v("clazz: $clazz result == null: ${result == null}")
if (result != null) return result
sleep(SHORT_INTERVAL)
if (root) {
node = getRoot(true)
} else {
node.refresh()
}
currentTime = System.currentTimeMillis()
}
LogUtils.e("findOneByClazz Exception()")
Exception().printStackTrace()
return null
}
/**
* 按类名寻找节点和子节点内的一个匹配项
* node 节点
* clazz 类名
* limitDepth 深度 限制深度搜索深度必须匹配提供值且类名相同才返回 不填默认不限制
*/
fun findOnceByClazz(
node: AccessibilityNodeInfo?, node: AccessibilityNodeInfo?,
clazz: String, clazz: String,
limitDepth: Int? = null, limitDepth: Int? = null,
depth: Int = 0 depth: Int = 0
): AccessibilityNodeInfo? { ): AccessibilityNodeInfo? {
if (node == null) return null if (node == null) return null
// Log.d(tag, "node.className: " + node.className)
if (node.className == clazz) { if (node.className == clazz) {
if (limitDepth == null || limitDepth == depth) if (limitDepth == null || limitDepth == depth)
return node return node
} }
for (i in 0 until node.childCount) { for (i in 0 until node.childCount) {
val result = findOneByClazz(node.getChild(i), clazz, limitDepth, depth + 1) val result = findOnceByClazz(node.getChild(i), clazz, limitDepth, depth + 1)
if (result != null) return result if (result != null) return result
} }
return null return null
@@ -386,15 +517,48 @@ object AccessibilityUtil {
* limitDepth 深度 限制深度搜索深度必须匹配提供值且类名相同才返回 不填默认不限制 * limitDepth 深度 限制深度搜索深度必须匹配提供值且类名相同才返回 不填默认不限制
*/ */
fun findAllByClazz( fun findAllByClazz(
node: AccessibilityNodeInfo?,
clazz: String,
list: ArrayList<AccessibilityNodeInfo> = ArrayList(),
timeout: Long = 5000,
root: Boolean = true,
minSize: Int = 1
): ArrayList<AccessibilityNodeInfo> {
var node = node ?: return list
val startTime = System.currentTimeMillis()
var currentTime = startTime
while (currentTime - startTime <= timeout) {
val result = findAllOnceByClazz(node, clazz)
LogUtils.v("clazz: $clazz count: " + result.size)
if (result.size >= minSize) return result
sleep(SHORT_INTERVAL)
if (root) {
node = getRoot(true)
} else {
node.refresh()
}
currentTime = System.currentTimeMillis()
}
LogUtils.e("findAllByClazz Exception()")
Exception().printStackTrace()
return list
}
/**
* 按类名寻找节点和子节点内的所有匹配项
* node 节点
* clazz 类名
* limitDepth 深度 限制深度搜索深度必须匹配提供值且类名相同才返回 不填默认不限制
*/
fun findAllOnceByClazz(
node: AccessibilityNodeInfo?, node: AccessibilityNodeInfo?,
clazz: String, clazz: String,
list: ArrayList<AccessibilityNodeInfo> = ArrayList() list: ArrayList<AccessibilityNodeInfo> = ArrayList()
): ArrayList<AccessibilityNodeInfo> { ): ArrayList<AccessibilityNodeInfo> {
if (node == null) return list if (node == null) return list
// Log.d(tag, "node.className: " + node.className)
if (node.className == clazz) list.add(node) if (node.className == clazz) list.add(node)
for (i in 0 until node.childCount) { for (i in 0 until node.childCount) {
findAllByClazz(node.getChild(i), clazz, list) findAllOnceByClazz(node.getChild(i), clazz, list)
} }
return list return list
} }
@@ -405,7 +569,6 @@ object AccessibilityUtil {
*/ */
fun findFrontNode(node: AccessibilityNodeInfo?): AccessibilityNodeInfo? { fun findFrontNode(node: AccessibilityNodeInfo?): AccessibilityNodeInfo? {
if (node == null) return null if (node == null) return null
// Log.d(tag, "node.className: " + node.className)
var parent: AccessibilityNodeInfo? = node.parent var parent: AccessibilityNodeInfo? = node.parent
var son: AccessibilityNodeInfo? = node var son: AccessibilityNodeInfo? = node
while (parent != null) { while (parent != null) {
@@ -431,7 +594,6 @@ object AccessibilityUtil {
*/ */
fun findBackNode(node: AccessibilityNodeInfo?): AccessibilityNodeInfo? { fun findBackNode(node: AccessibilityNodeInfo?): AccessibilityNodeInfo? {
if (node == null) return null if (node == null) return null
// Log.d(tag, "node.className: " + node.className)
var parent: AccessibilityNodeInfo? = node.parent var parent: AccessibilityNodeInfo? = node.parent
var son: AccessibilityNodeInfo? = node var son: AccessibilityNodeInfo? = node
while (parent != null) { while (parent != null) {

View File

@@ -9,7 +9,7 @@ import org.yameida.worktool.service.backPress
import org.yameida.worktool.service.getRoot import org.yameida.worktool.service.getRoot
import org.yameida.worktool.service.goHome import org.yameida.worktool.service.goHome
import org.yameida.worktool.service.sleep import org.yameida.worktool.service.sleep
import org.yameida.worktool.utils.AccessibilityUtil.findAllByClazz import org.yameida.worktool.utils.AccessibilityUtil.findAllOnceByClazz
/** /**
* 房间特征分析工具类 * 房间特征分析工具类
@@ -56,10 +56,10 @@ object WeworkRoomUtil {
*/ */
fun getRoomTitle(root: AccessibilityNodeInfo): ArrayList<String> { fun getRoomTitle(root: AccessibilityNodeInfo): ArrayList<String> {
val titleList = arrayListOf<String>() val titleList = arrayListOf<String>()
val list = findOneByClazz(root, Views.ListView) val list = AccessibilityUtil.findOnceByClazz(root, Views.ListView)
if (list != null) { if (list != null) {
val frontNode = findFrontNode(list.parent.parent) val frontNode = findFrontNode(list.parent.parent)
val textViewList = findAllByClazz(frontNode, Views.TextView) val textViewList = findAllOnceByClazz(frontNode, Views.TextView)
for (textView in textViewList) { for (textView in textViewList) {
if (!textView.text.isNullOrBlank()) { if (!textView.text.isNullOrBlank()) {
titleList.add(textView.text.toString().replace("\\(\\d+\\)$".toRegex(), "")) titleList.add(textView.text.toString().replace("\\(\\d+\\)$".toRegex(), ""))
@@ -78,7 +78,11 @@ object WeworkRoomUtil {
val titleList = getRoomTitle(getRoot()) val titleList = getRoomTitle(getRoot())
val roomType = getRoomType(getRoot()) val roomType = getRoomType(getRoot())
if (roomType != WeworkMessageBean.ROOM_TYPE_UNKNOWN if (roomType != WeworkMessageBean.ROOM_TYPE_UNKNOWN
&& titleList.count { it.replace("", "") == title.replace("", "") } > 0) { && titleList.count {
it.replace("", "").replace("\\(.*?\\)".toRegex(), "") == title.replace("", "")
.replace("\\(.*?\\)".toRegex(), "")
} > 0
) {
LogUtils.d("当前正在房间") LogUtils.d("当前正在房间")
return true return true
} }
@@ -86,25 +90,16 @@ object WeworkRoomUtil {
val list = findOneByClazz(getRoot(), Views.ListView) val list = findOneByClazz(getRoot(), Views.ListView)
if (list != null) { if (list != null) {
val frontNode = findFrontNode(list) val frontNode = findFrontNode(list)
val textViewList = findAllByClazz(frontNode, Views.TextView) val textViewList = findAllOnceByClazz(frontNode, Views.TextView)
if (textViewList.size >= 2) { if (textViewList.size >= 2) {
val searchButton: AccessibilityNodeInfo = textViewList[textViewList.size - 2] val searchButton: AccessibilityNodeInfo = textViewList[textViewList.size - 2]
val multiButton: AccessibilityNodeInfo = textViewList[textViewList.size - 1] val multiButton: AccessibilityNodeInfo = textViewList[textViewList.size - 1]
AccessibilityUtil.performClick(searchButton) AccessibilityUtil.performClick(searchButton)
sleep(1000)
AccessibilityUtil.findTextInput(getRoot(), title.replace("", "")) AccessibilityUtil.findTextInput(getRoot(), title.replace("", ""))
sleep(1000) sleep(1000)
var selectListView: AccessibilityNodeInfo? = null val selectListView = findOneByClazz(getRoot(), Views.ListView)
while (selectListView == null) { val imageView = AccessibilityUtil.findOnceByClazz(selectListView, Views.ImageView)
LogUtils.d("未找到搜索结果列表")
selectListView = findOneByClazz(getRoot(), Views.ListView)
sleep(500)
}
val imageView = findOneByClazz(selectListView, Views.ImageView)
if (imageView != null) {
AccessibilityUtil.performClick(imageView) AccessibilityUtil.performClick(imageView)
}
sleep(2000)
return true return true
} else { } else {
LogUtils.e("未找到搜索按钮") LogUtils.e("未找到搜索按钮")
@@ -119,17 +114,17 @@ object WeworkRoomUtil {
* @return true 成功进入群管理页 * @return true 成功进入群管理页
*/ */
fun intoGroupManager(): Boolean { fun intoGroupManager(): Boolean {
if (AccessibilityUtil.findOneByText(getRoot(), "微信用户创建") != null) { if (AccessibilityUtil.findOnceByText(getRoot(), "全部群成员") != null
|| AccessibilityUtil.findOnceByText(getRoot(), "微信用户创建") != null) {
return true return true
} }
val list = findOneByClazz(getRoot(), Views.ListView) val list = findOneByClazz(getRoot(), Views.ListView)
if (list != null) { if (list != null) {
val frontNode = AccessibilityUtil.findFrontNode(list.parent.parent) val frontNode = AccessibilityUtil.findFrontNode(list.parent.parent)
val textViewList = findAllByClazz(frontNode, Views.TextView) val textViewList = findAllOnceByClazz(frontNode, Views.TextView)
if (textViewList.size >= 2) { if (textViewList.size >= 2) {
val multiButton = textViewList.lastOrNull() val multiButton = textViewList.lastOrNull()
AccessibilityUtil.performClick(multiButton) AccessibilityUtil.performClick(multiButton)
sleep(2000)
return true return true
} else { } else {
LogUtils.e("未找到群管理按钮") LogUtils.e("未找到群管理按钮")
@@ -149,11 +144,10 @@ object WeworkRoomUtil {
val list = findOneByClazz(getRoot(), Views.ListView) val list = findOneByClazz(getRoot(), Views.ListView)
if (list != null) { if (list != null) {
val frontNode = AccessibilityUtil.findFrontNode(list.parent.parent) val frontNode = AccessibilityUtil.findFrontNode(list.parent.parent)
val textViewList = findAllByClazz(frontNode, Views.TextView) val textViewList = findAllOnceByClazz(frontNode, Views.TextView)
if (textViewList.size >= 2) { if (textViewList.size >= 2) {
val multiButton = textViewList.lastOrNull() val multiButton = textViewList.lastOrNull()
AccessibilityUtil.performClick(multiButton) AccessibilityUtil.performClick(multiButton)
sleep(2000)
return true return true
} else { } else {
LogUtils.e("未找到好友详情按钮") LogUtils.e("未找到好友详情按钮")
@@ -172,7 +166,7 @@ object WeworkRoomUtil {
if (intoFriendDetail()) { if (intoFriendDetail()) {
val gridView = findOneByClazz(getRoot(), Views.GridView) val gridView = findOneByClazz(getRoot(), Views.GridView)
if (gridView != null && gridView.childCount >= 2) { if (gridView != null && gridView.childCount >= 2) {
val tvList = findAllByClazz(gridView.getChild(0), Views.TextView) val tvList = findAllOnceByClazz(gridView.getChild(0), Views.TextView)
for (textView in tvList) { for (textView in tvList) {
if (textView.text != null) { if (textView.text != null) {
titleList.add(textView.text.toString()) titleList.add(textView.text.toString())
@@ -189,12 +183,12 @@ object WeworkRoomUtil {
* 群右上角有两个按钮(快速会议按钮、更多按钮) * 群右上角有两个按钮(快速会议按钮、更多按钮)
*/ */
private fun isGroupChat(root: AccessibilityNodeInfo): Boolean { private fun isGroupChat(root: AccessibilityNodeInfo): Boolean {
val list = findOneByClazz(root, Views.ListView) val list = AccessibilityUtil.findOnceByClazz(root, Views.ListView)
if (list != null) { if (list != null) {
val frontNode = findFrontNode(list.parent.parent) val frontNode = findFrontNode(list.parent.parent)
val textViewList = findAllByClazz(frontNode, Views.TextView) val textViewList = findAllOnceByClazz(frontNode, Views.TextView)
if (textViewList.size >= 2) { if (textViewList.size >= 2) {
val buttonList = findAllByClazz(textViewList.last().parent.parent, Views.TextView) val buttonList = findAllOnceByClazz(textViewList.last().parent.parent, Views.TextView)
return buttonList.size == 2 return buttonList.size == 2
} else { } else {
LogUtils.d("未找到群管理按钮") LogUtils.d("未找到群管理按钮")
@@ -210,12 +204,12 @@ object WeworkRoomUtil {
* listview前兄弟控件 && text包含外部群 * listview前兄弟控件 && text包含外部群
*/ */
private fun isExternalGroup(root: AccessibilityNodeInfo): Boolean { private fun isExternalGroup(root: AccessibilityNodeInfo): Boolean {
val listView = findOneByClazz(root, Views.ListView, null, 0) val listView = AccessibilityUtil.findOnceByClazz(root, Views.ListView, null, 0)
if (listView != null) { if (listView != null) {
val frontNode = findFrontNode(listView) val frontNode = findFrontNode(listView)
if (frontNode != null) { if (frontNode != null) {
val nodeList = frontNode.findAccessibilityNodeInfosByText("外部群") val nodeList = AccessibilityUtil.findAllByText(frontNode, "外部群", timeout = 0)
return nodeList.size > 0 return nodeList.isNotEmpty()
} }
} }
return false return false
@@ -226,8 +220,8 @@ object WeworkRoomUtil {
* 有列表和输入框 * 有列表和输入框
*/ */
private fun isSingleChat(root: AccessibilityNodeInfo): Boolean { private fun isSingleChat(root: AccessibilityNodeInfo): Boolean {
val list = findOneByClazz(root, Views.ListView) val list = AccessibilityUtil.findOnceByClazz(root, Views.ListView)
val editText = findOneByClazz(root, Views.EditText) val editText = AccessibilityUtil.findOnceByClazz(root, Views.EditText)
if (list != null && editText != null) { if (list != null && editText != null) {
return true return true
} }

View File

@@ -4,9 +4,8 @@ import android.view.accessibility.AccessibilityNodeInfo
import com.blankj.utilcode.util.LogUtils import com.blankj.utilcode.util.LogUtils
import org.yameida.worktool.model.WeworkMessageBean import org.yameida.worktool.model.WeworkMessageBean
import org.yameida.worktool.service.getRoot import org.yameida.worktool.service.getRoot
import org.yameida.worktool.service.sleep
import org.yameida.worktool.utils.AccessibilityUtil.findOneByClazz
import org.yameida.worktool.utils.AccessibilityUtil.findAllByClazz import org.yameida.worktool.utils.AccessibilityUtil.findAllByClazz
import org.yameida.worktool.utils.AccessibilityUtil.findAllOnceByClazz
import java.util.* import java.util.*
/** /**
@@ -204,9 +203,9 @@ object WeworkTextUtil {
* @see WeworkMessageBean.TEXT_TYPE * @see WeworkMessageBean.TEXT_TYPE
*/ */
fun getTextType(node: AccessibilityNodeInfo, isGroup: Boolean = true): Int { fun getTextType(node: AccessibilityNodeInfo, isGroup: Boolean = true): Int {
val tvList = findAllByClazz(node, Views.TextView) val tvList = findAllOnceByClazz(node, Views.TextView)
val tvCount = tvList.size val tvCount = tvList.size
val ivCount = findAllByClazz(node, Views.ImageView).size val ivCount = findAllOnceByClazz(node, Views.ImageView).size
return when { return when {
tvCount == 1 && ivCount == 0 -> WeworkMessageBean.TEXT_TYPE_PLAIN tvCount == 1 && ivCount == 0 -> WeworkMessageBean.TEXT_TYPE_PLAIN
tvCount == 0 && ivCount == 1 -> WeworkMessageBean.TEXT_TYPE_IMAGE tvCount == 0 && ivCount == 1 -> WeworkMessageBean.TEXT_TYPE_IMAGE
@@ -258,9 +257,9 @@ object WeworkTextUtil {
*/ */
fun getNameList(item: AccessibilityNodeInfo): List<String> { fun getNameList(item: AccessibilityNodeInfo): List<String> {
val nameList = ArrayList<String>() val nameList = ArrayList<String>()
val node = findOneByClazz(item, Views.ViewGroup) val node = AccessibilityUtil.findOnceByClazz(item, Views.ViewGroup)
if (node != null) { if (node != null) {
val textViewList = findAllByClazz(node, Views.TextView) val textViewList = findAllOnceByClazz(node, Views.TextView)
for (textView in textViewList) { for (textView in textViewList) {
if (textView.text != null) { if (textView.text != null) {
nameList.add(textView.text.toString()) nameList.add(textView.text.toString())
@@ -289,13 +288,13 @@ object WeworkTextUtil {
): Boolean { ): Boolean {
if (node == null) return false if (node == null) return false
for (i in 0 until node.childCount) { for (i in 0 until node.childCount) {
val item = node.getChild(i) val item = node.getChild(node.childCount - 1 - i) ?: continue
val nameList = getNameList(item) val nameList = getNameList(item)
for (name in nameList.reversed()) { for (name in nameList) {
if (name == replyNick) { if (name == replyNick) {
val backNode = getMessageListNode(item) val backNode = getMessageListNode(item)
if (backNode != null) { if (backNode != null) {
val textNode = AccessibilityUtil.findOneByText(backNode, replyContent) val textNode = AccessibilityUtil.findOnceByText(backNode, replyContent)
if (textNode != null) { if (textNode != null) {
LogUtils.d("nameList: $nameList\nreplyContent: $replyContent") LogUtils.d("nameList: $nameList\nreplyContent: $replyContent")
return longClickMessageItem(item, key) return longClickMessageItem(item, key)
@@ -310,10 +309,9 @@ object WeworkTextUtil {
private fun longClickMessageItem(item: AccessibilityNodeInfo, key: String): Boolean { private fun longClickMessageItem(item: AccessibilityNodeInfo, key: String): Boolean {
val backNode = getMessageListNode(item) val backNode = getMessageListNode(item)
AccessibilityUtil.performLongClickWithSon(backNode) AccessibilityUtil.performLongClickWithSon(backNode)
sleep(1000) val optionRvList = findAllByClazz(getRoot(), Views.RecyclerView, root = true)
val optionRvList = findAllByClazz(getRoot(), Views.RecyclerView)
for (optionRv in optionRvList) { for (optionRv in optionRvList) {
val optionTvList = findAllByClazz(optionRv, Views.TextView) val optionTvList = findAllOnceByClazz(optionRv, Views.TextView)
for (optionTv in optionTvList) { for (optionTv in optionTvList) {
if (optionTv.text == key) { if (optionTv.text == key) {
AccessibilityUtil.performClick(optionTv) AccessibilityUtil.performClick(optionTv)
@@ -330,7 +328,7 @@ object WeworkTextUtil {
* @param item 消息item节点 * @param item 消息item节点
*/ */
private fun getMessageListNode(item: AccessibilityNodeInfo): AccessibilityNodeInfo? { private fun getMessageListNode(item: AccessibilityNodeInfo): AccessibilityNodeInfo? {
val node = findOneByClazz(item, Views.ViewGroup) val node = AccessibilityUtil.findOnceByClazz(item, Views.ViewGroup)
if (node != null) { if (node != null) {
return AccessibilityUtil.findBackNode(node) return AccessibilityUtil.findBackNode(node)
} }