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/
build/
release/
# Local configuration file (sdk path, etc)
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())
//手机号添加好友
// WeworkOperationImpl.addFriendByPhone(WeworkMessageBean.Friend().apply {
// this.phone = "13010001000"
// this.markName = "hhh"
// this.markCorp = "corp"
// this.markExtra = "ex"
// this.tagList = arrayListOf("tag1", "tag2")
// })
//自动通过好友
// WeworkLoopImpl.getFriendRequest()

View File

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

View File

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

View File

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

View File

@@ -12,6 +12,7 @@ import org.yameida.worktool.model.WeworkMessageBean
object WeworkController {
lateinit var weworkService: WeworkService
var enableLoopRunning = false
var mainLoopRunning = false
/**
@@ -21,6 +22,7 @@ object WeworkController {
@RequestMapping
fun stopAndGoHome() {
LogUtils.d("stopAndGoHome()")
enableLoopRunning = false
mainLoopRunning = false
goHome()
}
@@ -60,7 +62,7 @@ object WeworkController {
*/
@RequestMapping
fun replyMessage(message: WeworkMessageBean): Boolean {
LogUtils.d("replyMessage(): ${message.receivedContent}")
LogUtils.d("replyMessage(): ${message.receivedName} ${message.originalContent} ${message.receivedContent}")
return WeworkOperationImpl.replyMessage(
message.titleList,
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

View File

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

View File

@@ -1,9 +1,9 @@
package org.yameida.worktool.service
import android.os.Message
import android.view.accessibility.AccessibilityNodeInfo
import androidx.core.text.isDigitsOnly
import com.blankj.utilcode.util.LogUtils
import org.yameida.worktool.Demo
import org.yameida.worktool.model.WeworkMessageBean
import org.yameida.worktool.service.WeworkController.mainLoopRunning
import org.yameida.worktool.utils.*
@@ -15,14 +15,35 @@ import java.lang.StringBuilder
*/
object WeworkLoopImpl {
val stopWords = arrayListOf("解析中")
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() {
mainLoopRunning = true
try {
while (mainLoopRunning) {
if (WeworkRoomUtil.getRoomType(getRoot()) != WeworkMessageBean.ROOM_TYPE_UNKNOWN
&& getChatMessageList()) {
}
goHomeTab("消息")
//todo 下上滚动
if (getChatroomList() && getChatMessageList()) {
mainLoopRunning = false
break
@@ -31,7 +52,7 @@ object WeworkLoopImpl {
mainLoopRunning = false
break
}
sleep(1000)
sleep(500)
}
} catch (e: Exception) {
mainLoopRunning = false
@@ -43,13 +64,12 @@ object WeworkLoopImpl {
* 读取通讯录好友请求
*/
fun getFriendRequest(): Boolean {
val list = getRoot().findAccessibilityNodeInfosByText("通讯录")
val list = AccessibilityUtil.findAllByText(getRoot(), "通讯录", timeout = 0)
for (item in list) {
if (item.parent.parent.parent.childCount == 5) {
if (item.parent.childCount > 1) {
LogUtils.d("通讯录有红点")
AccessibilityUtil.performClick(item)
sleep(500)
val addButton = AccessibilityUtil.findOneByText(getRoot(), "添加客户")
val backNode = AccessibilityUtil.findBackNode(addButton)
LogUtils.d(backNode?.className)
@@ -81,6 +101,54 @@ object WeworkLoopImpl {
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 imageView = AccessibilityUtil.findOneByClazz(getRoot(), Views.ImageView)
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() != "微信" }
if (filter.isNotEmpty()) {
val tvNick = filter[0]
@@ -110,7 +178,7 @@ object WeworkLoopImpl {
//回到上一页
var retry = 5
while (retry-- > 0 && !isAtHome()) {
val textView = AccessibilityUtil.findOneByText(getRoot(), "新的客户")
val textView = AccessibilityUtil.findOnceByText(getRoot(), "新的客户")
if (textView == null) {
backPress()
}
@@ -124,6 +192,13 @@ object WeworkLoopImpl {
* 读取聊天列表
*/
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) {
LogUtils.i("读取首页聊天列表")
log("读取首页聊天列表")
@@ -176,58 +251,12 @@ object WeworkLoopImpl {
break
}
}
sleep(1000)
return true
} else {
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>()
LogUtils.d("开始解析一条消息...")
//消息头(在消息主体上方 如时间信息)
val linearLayoutItem = AccessibilityUtil.findOneByClazz(node, Views.LinearLayout, 1)
val linearLayoutItem = AccessibilityUtil.findOnceByClazz(node, Views.LinearLayout, 1)
if (linearLayoutItem != null) {
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() }) {
val text = item.text.toString()
val itemMessage = WeworkMessageBean.ItemMessageBean(0, text)
@@ -253,41 +282,44 @@ object WeworkLoopImpl {
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 (Views.ImageView.equals(relativeLayoutItem.getChild(0).className)) {
LogUtils.v("头像在左边 本条消息发送者为其他联系人")
nameList.addAll(WeworkTextUtil.getNameList(node))
var textType = WeworkMessageBean.TEXT_TYPE_UNKNOWN
val relativeLayoutContent =
AccessibilityUtil.findOneByClazz(relativeLayoutItem, Views.RelativeLayout, 2)
AccessibilityUtil.findOnceByClazz(relativeLayoutItem, Views.RelativeLayout, 2)
if (relativeLayoutContent != null) {
// AccessibilityUtil.printNodeClazzTree(relativeLayoutContent)
textType = WeworkTextUtil.getTextType(relativeLayoutContent)
LogUtils.v("textType: $textType")
val tvList =
AccessibilityUtil.findAllByClazz(relativeLayoutContent, Views.TextView)
AccessibilityUtil.findAllOnceByClazz(relativeLayoutContent, Views.TextView)
for (item in tvList.filter { it.text != null && !it.text.isNullOrBlank() }) {
val text = item.text.toString()
LogUtils.d(text)
val itemMessage = WeworkMessageBean.ItemMessageBean(2, text)
itemMessageList.add(itemMessage)
if (text !in stopWords) {
val itemMessage = WeworkMessageBean.ItemMessageBean(2, text)
itemMessageList.add(itemMessage)
}
}
}
message = WeworkMessageBean.SubMessageBean(0, textType, itemMessageList, nameList)
} else if (Views.ImageView.equals(relativeLayoutItem.getChild(1).className)) {
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() }) {
val text = item.text.toString()
LogUtils.d(text)
val itemMessage = WeworkMessageBean.ItemMessageBean(2, text)
itemMessageList.add(itemMessage)
if (text !in stopWords) {
val itemMessage = WeworkMessageBean.ItemMessageBean(2, text)
itemMessageList.add(itemMessage)
}
}
message = WeworkMessageBean.SubMessageBean(1, 0, itemMessageList, nameList)
} 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() }) {
val text = item.text.toString()
LogUtils.d(text)
@@ -300,7 +332,7 @@ object WeworkLoopImpl {
} else {
// 没有头像的消息(撤销消息、其他可能的系统消息)
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() }) {
val text = item.text.toString()
sb.append(text).append("/t")

View File

@@ -59,13 +59,15 @@ object WeworkOperationImpl {
)
) {
LogUtils.d("开始回复")
sleep(1000)
sendChatMessage(receivedContent, "[自动回复]")
LogUtils.d("$title: 回复成功")
WeworkLoopImpl.getChatMessageList()
return true
} else {
LogUtils.d("$title: 回复失败")
error("$title: 回复失败 $receivedContent")
LogUtils.d("$title: 回复失败 直接发送答案")
error("$title: 回复失败 直接发送答案 $receivedContent")
sendChatMessage(receivedContent, "[自动回复]【$originalContent】@$receivedName\n")
WeworkLoopImpl.getChatMessageList()
}
} else {
LogUtils.d("$title: 回复失败")
@@ -171,10 +173,10 @@ object WeworkOperationImpl {
if (newGroupName != null) {
groupRename(newGroupName)
}
if (selectList != null) {
if (!selectList.isNullOrEmpty()) {
groupAddMember(selectList, showMessageHistory)
}
if (removeList != null) {
if (!removeList.isNullOrEmpty()) {
groupRemoveMember(removeList)
}
if (newGroupAnnouncement != null) {
@@ -201,32 +203,23 @@ object WeworkOperationImpl {
val node = AccessibilityUtil.scrollAndFindByText(getRoot(), "微盘")
if (node != null) {
AccessibilityUtil.performClick(node)
sleep(2000)
val buttonList = AccessibilityUtil.findAllByClazz(getRoot(), Views.Button)
val buttonList = AccessibilityUtil.findAllByClazz(getRoot(), Views.Button, root = true)
if (buttonList.size >= 4) {
AccessibilityUtil.performClick(buttonList[2])
sleep(1000)
AccessibilityUtil.findTextInput(getRoot(), objectName)
sleep(2000)
val editText = AccessibilityUtil.findOneByClazz(getRoot(), Views.EditText)
val backNode = AccessibilityUtil.findBackNode(editText)
val imageViewList = AccessibilityUtil.findAllByClazz(backNode, Views.ImageView)
val imageViewList = AccessibilityUtil.findAllByClazz(getRoot(), Views.ImageView, root = true)
if (imageViewList.size >= 2) {
AccessibilityUtil.performClick(imageViewList[1])
sleep(2000)
val shareFileButton = AccessibilityUtil.findOneByDesc(getRoot(), "以原文件分享")
AccessibilityUtil.performClick(shareFileButton)
sleep(2000)
val shareToWorkButton = AccessibilityUtil.findOneByText(getRoot(true), "发送给同事")
AccessibilityUtil.performClick(shareToWorkButton)
sleep(2000)
relaySelectTarget(titleList, extraText)
sleep(2000)
val stayButton = AccessibilityUtil.findOneByText(getRoot(), "留在企业微信")
AccessibilityUtil.performClick(stayButton)
return true
} else {
LogUtils.e("微盘未搜索到相关文件: $objectName")
LogUtils.e("微盘未搜索到相关图片: $objectName")
}
} else {
LogUtils.e("未找到微盘内搜索")
@@ -252,24 +245,16 @@ object WeworkOperationImpl {
val node = AccessibilityUtil.scrollAndFindByText(getRoot(), "微盘")
if (node != null) {
AccessibilityUtil.performClick(node)
sleep(2000)
val buttonList = AccessibilityUtil.findAllByClazz(getRoot(), Views.Button)
val buttonList = AccessibilityUtil.findAllByClazz(getRoot(), Views.Button, root = true)
if (buttonList.size >= 4) {
AccessibilityUtil.performClick(buttonList[2])
sleep(1000)
AccessibilityUtil.findTextInput(getRoot(), objectName)
sleep(2000)
val editText = AccessibilityUtil.findOneByClazz(getRoot(), Views.EditText)
val backNode = AccessibilityUtil.findBackNode(editText)
val imageViewList = AccessibilityUtil.findAllByClazz(backNode, Views.ImageView)
val imageViewList = AccessibilityUtil.findAllByClazz(getRoot(), Views.ImageView, root = true)
if (imageViewList.size >= 2) {
AccessibilityUtil.performClick(imageViewList[1])
sleep(2000)
val shareFileButton = AccessibilityUtil.findOneByDesc(getRoot(), "转发")
AccessibilityUtil.performClick(shareFileButton)
sleep(2000)
relaySelectTarget(titleList, extraText)
sleep(2000)
return true
} else {
LogUtils.e("微盘未搜索到相关文件: $objectName")
@@ -298,11 +283,9 @@ object WeworkOperationImpl {
val node = AccessibilityUtil.scrollAndFindByText(getRoot(), "用过的小程序")
if (node != null) {
AccessibilityUtil.performClick(node)
sleep(2000)
val textViewList = AccessibilityUtil.findAllByClazz(getRoot(), Views.TextView)
val textViewList = AccessibilityUtil.findAllByClazz(getRoot(), Views.TextView, root = true)
if (textViewList.size > 3) {
AccessibilityUtil.performClick(textViewList[2])
sleep(1000)
AccessibilityUtil.findTextInput(getRoot(), objectName)
sleep(2000)
AccessibilityUtil.findListOneAndClick(getRoot(), 1)
@@ -336,31 +319,22 @@ object WeworkOperationImpl {
return false
}
AccessibilityUtil.performClick(allButton)
sleep(1000)
val myFileButton = AccessibilityUtil.findOneByText(getRoot(), "共享空间")
if (myFileButton == null) {
LogUtils.e("未找到共享空间按钮")
return false
}
AccessibilityUtil.performClick(myFileButton)
sleep(2000)
val buttonList = AccessibilityUtil.findAllByClazz(getRoot(), Views.Button)
val buttonList = AccessibilityUtil.findAllByClazz(getRoot(), Views.Button, root = true)
if (buttonList.size >= 4) {
AccessibilityUtil.performClick(buttonList[3])
sleep(1000)
AccessibilityUtil.findTextInput(getRoot(), objectName)
sleep(2000)
val editText = AccessibilityUtil.findOneByClazz(getRoot(), Views.EditText)
val backNode = AccessibilityUtil.findBackNode(editText)
val imageViewList = AccessibilityUtil.findAllByClazz(backNode, Views.ImageView)
val imageViewList = AccessibilityUtil.findAllByClazz(getRoot(), Views.ImageView, root = true)
if (imageViewList.size >= 2) {
AccessibilityUtil.performClick(imageViewList[1])
sleep(2000)
val shareFileButton = AccessibilityUtil.findOneByDesc(getRoot(), "转发")
AccessibilityUtil.performClick(shareFileButton)
sleep(2000)
relaySelectTarget(titleList, extraText)
sleep(2000)
return true
} else {
LogUtils.e("文档未搜索到相关文件: $objectName")
@@ -371,6 +345,130 @@ object WeworkOperationImpl {
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
@@ -409,20 +507,21 @@ object WeworkOperationImpl {
val list = AccessibilityUtil.findOneByClazz(getRoot(), Views.ListView)
if (list != null) {
val frontNode = AccessibilityUtil.findFrontNode(list)
val textViewList = AccessibilityUtil.findAllByClazz(frontNode, Views.TextView)
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(1000)
AccessibilityUtil.performClick(searchButton)
//todo 搜索需要在循环内
sleep(1000)
for (select in selectList) {
AccessibilityUtil.findTextInput(getRoot(), select)
sleep(2000)
val selectListView = AccessibilityUtil.findOneByClazz(getRoot(), Views.ListView)
val imageView =
AccessibilityUtil.findOneByClazz(selectListView, Views.ImageView)
AccessibilityUtil.findOnceByClazz(selectListView, Views.ImageView)
if (imageView != null) {
AccessibilityUtil.performClick(imageView)
}
@@ -438,7 +537,7 @@ object WeworkOperationImpl {
AccessibilityUtil.findTextInput(getRoot(), extraText)
sleep(1000)
}
val sendButtonList = getRoot().findAccessibilityNodeInfosByText("发送")
val sendButtonList = AccessibilityUtil.findAllByText(getRoot(), "发送", timeout = 0)
for (sendButton in sendButtonList.filter { it.text != null }) {
if (sendButton.text == "发送" || sendButton.text == "发送(${selectList.size})") {
AccessibilityUtil.performClick(sendButton)
@@ -468,10 +567,8 @@ object WeworkOperationImpl {
val textViewGroup = AccessibilityUtil.scrollAndFindByText(getRoot(), "客户群")
if (AccessibilityUtil.performClick(textViewGroup)) {
LogUtils.d("进入客户群应用")
sleep(2000)
val textView = AccessibilityUtil.findOneByText(getRoot(), "创建一个客户群")
AccessibilityUtil.performClick(textView)
sleep(3000)
return true
} else {
LogUtils.d("未找到客户群应用")
@@ -484,12 +581,11 @@ object WeworkOperationImpl {
*/
private fun groupRename(groupName: String): Boolean {
if (WeworkRoomUtil.intoGroupManager()) {
val textView =
AccessibilityUtil.findOneByText(getRoot(), "微信用户创建")
val textView = AccessibilityUtil.findOneByText(getRoot(), "微信用户创建")
//todo 微信用户创建可能找不到
val button = AccessibilityUtil.findFrontNode(textView)
if (button != null) {
AccessibilityUtil.performClick(button)
sleep(1000)
AccessibilityUtil.findTextInput(getRoot(), groupName)
val confirmButton = AccessibilityUtil.findOneByText(getRoot(), "确定")
AccessibilityUtil.performClick(confirmButton)
@@ -520,7 +616,6 @@ object WeworkOperationImpl {
} else {
AccessibilityUtil.performClick(gridView.getChild(gridView.childCount - 2))
}
sleep(1000)
} else {
LogUtils.e("未找到添加成员按钮")
return false
@@ -528,32 +623,29 @@ object WeworkOperationImpl {
val list = AccessibilityUtil.findOneByClazz(getRoot(), Views.ListView)
if (list != null) {
val frontNode = AccessibilityUtil.findFrontNode(list)
val textViewList = AccessibilityUtil.findAllByClazz(frontNode, Views.TextView)
val textViewList = AccessibilityUtil.findAllOnceByClazz(frontNode, Views.TextView)
if (textViewList.size >= 2) {
val multiButton = textViewList.lastOrNull()
AccessibilityUtil.performClick(multiButton)
sleep(1000)
for (select in selectList) {
AccessibilityUtil.performClick(multiButton)
AccessibilityUtil.findTextInput(getRoot(), select)
sleep(2000)
val selectListView =
AccessibilityUtil.findOneByClazz(getRoot(), Views.ListView)
val imageView =
AccessibilityUtil.findOneByClazz(selectListView, Views.ImageView)
if (imageView != null) {
AccessibilityUtil.performClick(imageView)
AccessibilityUtil.findOneByClazz(selectListView, Views.ImageView, root = false)
AccessibilityUtil.performClick(imageView)
val textView = AccessibilityUtil.findOnceByClazz(getRoot(), Views.TextView)
if (textView != null && textView.text.isNullOrBlank()) {
AccessibilityUtil.performClick(textView)
}
sleep(1000)
}
if (showMessageHistory) {
val button = AccessibilityUtil.findOneByText(getRoot(), "附带聊天记录")
if (button != null) AccessibilityUtil.performClick(button)
AccessibilityUtil.findTextAndClick(getRoot(), "聊天记录")
}
val confirmButton =
AccessibilityUtil.findOneByText(getRoot(), "确定(${selectList.size})")
if (confirmButton != null) {
AccessibilityUtil.performClick(confirmButton)
sleep(1000)
} else {
LogUtils.e("未发现确认按钮: ")
return false
@@ -574,6 +666,7 @@ object WeworkOperationImpl {
* 移除群成员/踢人
*/
private fun groupRemoveMember(removeList: List<String>): Boolean {
if (removeList.isNullOrEmpty()) return true
if (WeworkRoomUtil.intoGroupManager()) {
val gridView = AccessibilityUtil.findOneByClazz(getRoot(), Views.GridView)
if (gridView != null && gridView.childCount >= 2) {
@@ -582,7 +675,6 @@ object WeworkOperationImpl {
} else {
AccessibilityUtil.performClick(gridView.getChild(gridView.childCount - 1))
}
sleep(1000)
} else {
LogUtils.e("未找到删除成员按钮")
return false
@@ -590,28 +682,26 @@ object WeworkOperationImpl {
val list = AccessibilityUtil.findOneByClazz(getRoot(), Views.ListView)
if (list != null) {
val frontNode = AccessibilityUtil.findFrontNode(list)
val textViewList = AccessibilityUtil.findAllByClazz(frontNode, Views.TextView)
val textViewList = AccessibilityUtil.findAllOnceByClazz(frontNode, Views.TextView)
if (textViewList.size >= 2) {
val multiButton = textViewList.lastOrNull()
AccessibilityUtil.performClick(multiButton)
sleep(1000)
for (select in removeList) {
AccessibilityUtil.performClick(multiButton)
AccessibilityUtil.findTextInput(getRoot(), select)
sleep(2000)
val selectListView =
AccessibilityUtil.findOneByClazz(getRoot(), Views.ListView)
val imageView =
AccessibilityUtil.findOneByClazz(selectListView, Views.ImageView)
if (imageView != null) {
AccessibilityUtil.performClick(imageView)
AccessibilityUtil.findOneByClazz(selectListView, Views.ImageView, root = false)
AccessibilityUtil.performClick(imageView)
val textView = AccessibilityUtil.findOnceByClazz(getRoot(), Views.TextView)
if (textView != null && textView.text.isNullOrBlank()) {
AccessibilityUtil.performClick(textView)
}
sleep(1000)
}
val confirmButton =
AccessibilityUtil.findOneByText(getRoot(), "移出(${removeList.size})")
if (confirmButton != null) {
AccessibilityUtil.performClick(confirmButton)
sleep(1000)
} else {
LogUtils.e("未发现移出按钮: ")
return false
@@ -631,6 +721,7 @@ object WeworkOperationImpl {
/**
* 修改群公告
* 注:首次为发布 后续为编辑
* 注2外部群为edittext 内部群为webview(只能追加文本)
*/
private fun groupChangeAnnouncement(groupAnnouncement: String? = null): Boolean {
if (groupAnnouncement == null) return true
@@ -638,8 +729,7 @@ object WeworkOperationImpl {
val textView = AccessibilityUtil.findOneByText(getRoot(), "群公告")
if (textView != null) {
AccessibilityUtil.performClick(textView)
sleep(1000)
val editButton = AccessibilityUtil.findOneByText(getRoot(), "编辑")
val editButton = AccessibilityUtil.findOneByText(getRoot(), "编辑", timeout = 2000)
if (editButton != null) {
LogUtils.d("群公告编辑中: $groupAnnouncement")
AccessibilityUtil.performClick(editButton)
@@ -647,15 +737,15 @@ object WeworkOperationImpl {
}
if (AccessibilityUtil.findTextInput(getRoot(), groupAnnouncement)) {
LogUtils.d("群公告发布中: $groupAnnouncement")
sleep(500)
val button = AccessibilityUtil.findOneByText(getRoot(), "发布")
AccessibilityUtil.performClick(button)
sleep(1000)
val publishButtonList = getRoot().findAccessibilityNodeInfosByText("发布")
if (publishButtonList.size >= 2) {
AccessibilityUtil.performClick(publishButtonList[1])
if (AccessibilityUtil.findTextAndClick(getRoot(), "发布")) {
val publishButtonList = AccessibilityUtil.findAllByText(getRoot(), "发布")
if (publishButtonList.size >= 2) {
AccessibilityUtil.performClick(publishButtonList[1])
}
sleep(3000)
} else {
LogUtils.e("无法进行群公告发布: ")
}
sleep(3000)
} else {
LogUtils.e("无法进行群公告发布和编辑: ")
return false
@@ -664,6 +754,8 @@ object WeworkOperationImpl {
LogUtils.e("未找到群公告按钮")
return false
}
} else {
LogUtils.e("进入群管理页失败")
}
return true
}
@@ -672,36 +764,78 @@ object WeworkOperationImpl {
* 发送消息
*/
private fun sendChatMessage(text: String, prefix: String = "") {
val editText = AccessibilityUtil.findOneByClazz(getRoot(), Views.EditText)
if (editText != null) {
AccessibilityUtil.editTextInput(editText, prefix + 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 (AccessibilityUtil.findTextInput(getRoot(), prefix + text)) {
val sendButton = AccessibilityUtil.findAllByClazz(getRoot(), Views.Button)
.firstOrNull { it.text == "发送" }
if (sendButton != null) {
LogUtils.i("发送消息: \n$text")
LogUtils.d("发送消息: \n$text")
log("发送消息: \n$text")
AccessibilityUtil.performClick(sendButton)
sleep(500)
break
} else {
LogUtils.e("未找到发送按钮")
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.view.accessibility.AccessibilityEvent
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.查询类
@@ -45,6 +49,8 @@ import android.view.accessibility.AccessibilityNodeInfo
*/
object AccessibilityUtil {
private const val tag = "AccessibilityUtil"
private const val SHORT_INTERVAL = 100L
private const val SCROLL_INTERVAL = 300L
//编辑EditView(非粘贴 推荐)
fun editTextInput(nodeInfo: AccessibilityNodeInfo?, text: String): Boolean {
@@ -65,22 +71,31 @@ object AccessibilityUtil {
}
//寻找第一个EditView编辑框并输入文本
fun findTextInput(nodeInfo: AccessibilityNodeInfo?, text: String): Boolean {
val nodeInfo: AccessibilityNodeInfo = nodeInfo ?: return false
val editText = findOneByClazz(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)
fun findTextInput(nodeInfo: AccessibilityNodeInfo?, text: String, root: Boolean = true): Boolean {
if (root) {
val editText = findOneByClazz(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)
} 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
}
//寻找第一个列表并点击指定条目(默认点击第一个条目)
fun findListOneAndClick(nodeInfo: AccessibilityNodeInfo, index: Int = 0): Boolean {
val rv = findOneByClazz(nodeInfo, "androidx.recyclerview.widget.RecyclerView")
val lv = findOneByClazz(nodeInfo, "android.widget.ListView")
val rv = findOnceByClazz(nodeInfo, "androidx.recyclerview.widget.RecyclerView")
val lv = findOnceByClazz(nodeInfo, "android.widget.ListView")
if (rv == null && lv == null) return false
if (rv != null && rv.childCount > index) {
performClick(rv.getChild(index))
@@ -99,16 +114,14 @@ object AccessibilityUtil {
var index = 0
while (index++ < maxRetry) {
performScrollUp(nodeInfo, 0)
Thread.sleep(300)
val node = findOneByText(nodeInfo, text)
val node = findOnceByText(nodeInfo, text)
if (node != null) {
return node
}
}
while (index++ < maxRetry * 2) {
performScrollDown(nodeInfo, 0)
Thread.sleep(300)
val node = findOneByText(nodeInfo, text)
val node = findOnceByText(nodeInfo, text)
if (node != null) {
return node
}
@@ -249,6 +262,7 @@ object AccessibilityUtil {
val canScrollNodeList = findCanScrollNode(node)
if (canScrollNodeList.size > index) {
canScrollNodeList[index].performAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD)
sleep(SCROLL_INTERVAL)
return true
}
return false
@@ -260,6 +274,7 @@ object AccessibilityUtil {
val canScrollNodeList = findCanScrollNode(node)
if (canScrollNodeList.size > index) {
canScrollNodeList[index].performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD)
sleep(SCROLL_INTERVAL)
return true
}
return false
@@ -301,56 +316,134 @@ object AccessibilityUtil {
service.performGlobalAction(AccessibilityService.GLOBAL_ACTION_HOME)
}
//按描述寻找节点和子节点内的一个匹配项
/**
* 按描述寻找节点和子节点内的一个匹配项
* @param node 节点
* @param desc 描述
* @param timeout 检查超时时间
*/
fun findOneByDesc(
node: AccessibilityNodeInfo?,
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? {
if (node == null) return null
val description = node.contentDescription?.toString()
if (description == desc || (desc2 != null && description == desc2)) {
if (description == desc) {
return node
}
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
}
return null
}
//按文本(关键词)寻找节点和子节点内的一个匹配项
/**
* 按文本(关键词)寻找节点和子节点内的一个匹配项
* @param node 节点
* @param text 关键词
* @param timeout 检查超时时间
*/
fun findOneByText(
node: AccessibilityNodeInfo?,
text: String,
text2: String? = null
exact: Boolean = false,
timeout: Long = 5000,
root: Boolean = true
): AccessibilityNodeInfo? {
if (node == null) return null
val textViewList = node.findAccessibilityNodeInfosByText(text)
if (textViewList != null && textViewList.size > 0) {
for (textView in textViewList) {
if (textView.text == text) {
return textView
var node = node ?: return null
val startTime = System.currentTimeMillis()
var currentTime = startTime
while (currentTime - startTime <= timeout) {
val textViewList = node.findAccessibilityNodeInfosByText(text)
LogUtils.v("text: $text count: " + textViewList.size)
if (textViewList != null && textViewList.size > 0) {
for (textView in textViewList) {
if (textView.text == text) {
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
}
//按文本(关键词)寻找节点和子节点内的所有匹配项
fun findOnceByText(
node: AccessibilityNodeInfo?,
text: String,
exact: Boolean = false
): AccessibilityNodeInfo? {
return findOneByText(node, text, exact, 0)
}
/**
* 按文本(关键词)寻找节点和子节点内的所有匹配项
* @param node 节点
* @param text 关键词
* @param timeout 检查超时时间
*/
fun findAllByText(
node: AccessibilityNodeInfo?,
text: String,
text2: String? = null
exact: Boolean = false,
timeout: Long = 5000,
root: Boolean = true
): List<AccessibilityNodeInfo> {
if (node == null) return arrayListOf()
val textViewList = node.findAccessibilityNodeInfosByText(text)
if (textViewList != null && textViewList.size > 0)
return textViewList
else if (text2 != null)
return findAllByText(node, text2)
var node = node ?: return arrayListOf()
val startTime = System.currentTimeMillis()
var currentTime = startTime
while (currentTime - startTime <= timeout) {
val tvList = node.findAccessibilityNodeInfosByText(text)
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()
}
@@ -359,21 +452,59 @@ object AccessibilityUtil {
* node 节点
* clazz 类名
* limitDepth 深度 限制深度搜索深度必须匹配提供值且类名相同才返回 不填默认不限制
* timeout 检查超时时间
*/
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?,
clazz: String,
limitDepth: Int? = null,
depth: Int = 0
): AccessibilityNodeInfo? {
if (node == null) return null
// Log.d(tag, "node.className: " + node.className)
if (node.className == clazz) {
if (limitDepth == null || limitDepth == depth)
return node
}
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
}
return null
@@ -386,15 +517,48 @@ object AccessibilityUtil {
* limitDepth 深度 限制深度搜索深度必须匹配提供值且类名相同才返回 不填默认不限制
*/
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?,
clazz: String,
list: ArrayList<AccessibilityNodeInfo> = ArrayList()
): ArrayList<AccessibilityNodeInfo> {
if (node == null) return list
// Log.d(tag, "node.className: " + node.className)
if (node.className == clazz) list.add(node)
for (i in 0 until node.childCount) {
findAllByClazz(node.getChild(i), clazz, list)
findAllOnceByClazz(node.getChild(i), clazz, list)
}
return list
}
@@ -405,7 +569,6 @@ object AccessibilityUtil {
*/
fun findFrontNode(node: AccessibilityNodeInfo?): AccessibilityNodeInfo? {
if (node == null) return null
// Log.d(tag, "node.className: " + node.className)
var parent: AccessibilityNodeInfo? = node.parent
var son: AccessibilityNodeInfo? = node
while (parent != null) {
@@ -431,7 +594,6 @@ object AccessibilityUtil {
*/
fun findBackNode(node: AccessibilityNodeInfo?): AccessibilityNodeInfo? {
if (node == null) return null
// Log.d(tag, "node.className: " + node.className)
var parent: AccessibilityNodeInfo? = node.parent
var son: AccessibilityNodeInfo? = node
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.goHome
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> {
val titleList = arrayListOf<String>()
val list = findOneByClazz(root, Views.ListView)
val list = AccessibilityUtil.findOnceByClazz(root, Views.ListView)
if (list != null) {
val frontNode = findFrontNode(list.parent.parent)
val textViewList = findAllByClazz(frontNode, Views.TextView)
val textViewList = findAllOnceByClazz(frontNode, Views.TextView)
for (textView in textViewList) {
if (!textView.text.isNullOrBlank()) {
titleList.add(textView.text.toString().replace("\\(\\d+\\)$".toRegex(), ""))
@@ -78,7 +78,11 @@ object WeworkRoomUtil {
val titleList = getRoomTitle(getRoot())
val roomType = getRoomType(getRoot())
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("当前正在房间")
return true
}
@@ -86,25 +90,16 @@ object WeworkRoomUtil {
val list = findOneByClazz(getRoot(), Views.ListView)
if (list != null) {
val frontNode = findFrontNode(list)
val textViewList = findAllByClazz(frontNode, Views.TextView)
val textViewList = findAllOnceByClazz(frontNode, Views.TextView)
if (textViewList.size >= 2) {
val searchButton: AccessibilityNodeInfo = textViewList[textViewList.size - 2]
val multiButton: AccessibilityNodeInfo = textViewList[textViewList.size - 1]
AccessibilityUtil.performClick(searchButton)
sleep(1000)
AccessibilityUtil.findTextInput(getRoot(), title.replace("", ""))
sleep(1000)
var selectListView: AccessibilityNodeInfo? = null
while (selectListView == null) {
LogUtils.d("未找到搜索结果列表")
selectListView = findOneByClazz(getRoot(), Views.ListView)
sleep(500)
}
val imageView = findOneByClazz(selectListView, Views.ImageView)
if (imageView != null) {
AccessibilityUtil.performClick(imageView)
}
sleep(2000)
val selectListView = findOneByClazz(getRoot(), Views.ListView)
val imageView = AccessibilityUtil.findOnceByClazz(selectListView, Views.ImageView)
AccessibilityUtil.performClick(imageView)
return true
} else {
LogUtils.e("未找到搜索按钮")
@@ -119,17 +114,17 @@ object WeworkRoomUtil {
* @return true 成功进入群管理页
*/
fun intoGroupManager(): Boolean {
if (AccessibilityUtil.findOneByText(getRoot(), "微信用户创建") != null) {
if (AccessibilityUtil.findOnceByText(getRoot(), "全部群成员") != null
|| AccessibilityUtil.findOnceByText(getRoot(), "微信用户创建") != null) {
return true
}
val list = findOneByClazz(getRoot(), Views.ListView)
if (list != null) {
val frontNode = AccessibilityUtil.findFrontNode(list.parent.parent)
val textViewList = findAllByClazz(frontNode, Views.TextView)
val textViewList = findAllOnceByClazz(frontNode, Views.TextView)
if (textViewList.size >= 2) {
val multiButton = textViewList.lastOrNull()
AccessibilityUtil.performClick(multiButton)
sleep(2000)
return true
} else {
LogUtils.e("未找到群管理按钮")
@@ -149,11 +144,10 @@ object WeworkRoomUtil {
val list = findOneByClazz(getRoot(), Views.ListView)
if (list != null) {
val frontNode = AccessibilityUtil.findFrontNode(list.parent.parent)
val textViewList = findAllByClazz(frontNode, Views.TextView)
val textViewList = findAllOnceByClazz(frontNode, Views.TextView)
if (textViewList.size >= 2) {
val multiButton = textViewList.lastOrNull()
AccessibilityUtil.performClick(multiButton)
sleep(2000)
return true
} else {
LogUtils.e("未找到好友详情按钮")
@@ -172,7 +166,7 @@ object WeworkRoomUtil {
if (intoFriendDetail()) {
val gridView = findOneByClazz(getRoot(), Views.GridView)
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) {
if (textView.text != null) {
titleList.add(textView.text.toString())
@@ -189,12 +183,12 @@ object WeworkRoomUtil {
* 群右上角有两个按钮(快速会议按钮、更多按钮)
*/
private fun isGroupChat(root: AccessibilityNodeInfo): Boolean {
val list = findOneByClazz(root, Views.ListView)
val list = AccessibilityUtil.findOnceByClazz(root, Views.ListView)
if (list != null) {
val frontNode = findFrontNode(list.parent.parent)
val textViewList = findAllByClazz(frontNode, Views.TextView)
val textViewList = findAllOnceByClazz(frontNode, Views.TextView)
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
} else {
LogUtils.d("未找到群管理按钮")
@@ -210,12 +204,12 @@ object WeworkRoomUtil {
* listview前兄弟控件 && text包含外部群
*/
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) {
val frontNode = findFrontNode(listView)
if (frontNode != null) {
val nodeList = frontNode.findAccessibilityNodeInfosByText("外部群")
return nodeList.size > 0
val nodeList = AccessibilityUtil.findAllByText(frontNode, "外部群", timeout = 0)
return nodeList.isNotEmpty()
}
}
return false
@@ -226,8 +220,8 @@ object WeworkRoomUtil {
* 有列表和输入框
*/
private fun isSingleChat(root: AccessibilityNodeInfo): Boolean {
val list = findOneByClazz(root, Views.ListView)
val editText = findOneByClazz(root, Views.EditText)
val list = AccessibilityUtil.findOnceByClazz(root, Views.ListView)
val editText = AccessibilityUtil.findOnceByClazz(root, Views.EditText)
if (list != null && editText != null) {
return true
}

View File

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