update 显示系统版本;兼容获取我的信息;兼容获取内部群群名;兼容聊天列表查询;兼容群拉人;兼容手势滚动;兼容修改内部群群名;兼容修改内部群群公告

This commit is contained in:
尹甲仑
2022-08-25 17:33:08 +08:00
parent 1b11f5bd26
commit f90cf57298
7 changed files with 300 additions and 103 deletions

View File

@@ -65,7 +65,8 @@ class ListenActivity : AppCompatActivity() {
SPUtils.getInstance().put("autoReply", Constant.autoReply) SPUtils.getInstance().put("autoReply", Constant.autoReply)
}) })
tv_host.text = WebConfig.HOST tv_host.text = WebConfig.HOST
tv_version.text = AppUtils.getAppVersionName() val version = "${AppUtils.getAppVersionName()} Android ${DeviceUtils.getSDKVersionName()} ${DeviceUtils.getManufacturer()} ${DeviceUtils.getModel()}"
tv_version.text = version
val workVersionName = AppUtils.getAppInfo(Constant.PACKAGE_NAMES)?.versionName val workVersionName = AppUtils.getAppInfo(Constant.PACKAGE_NAMES)?.versionName
when (workVersionName) { when (workVersionName) {
null -> { null -> {
@@ -83,7 +84,7 @@ class ListenActivity : AppCompatActivity() {
tv_work_version.text = tip tv_work_version.text = tip
} }
} }
SPUtils.getInstance().put("appVersion", AppUtils.getAppVersionName()) SPUtils.getInstance().put("appVersion", version)
SPUtils.getInstance().put("workVersion", workVersionName) SPUtils.getInstance().put("workVersion", workVersionName)
} }

View File

@@ -49,6 +49,7 @@ object WeworkGetImpl {
fun getMyInfo(): Boolean { fun getMyInfo(): Boolean {
if (!goHomeTab("")) { if (!goHomeTab("")) {
LogUtils.d("未找到我的信息") LogUtils.d("未找到我的信息")
log("未找到我的信息")
goHomeTab("消息") goHomeTab("消息")
val firstTv = AccessibilityUtil.findAllByClazz(getRoot(), Views.TextView) val firstTv = AccessibilityUtil.findAllByClazz(getRoot(), Views.TextView)
.firstOrNull { it.text == null } .firstOrNull { it.text == null }
@@ -57,13 +58,14 @@ object WeworkGetImpl {
val newFirstTv = AccessibilityUtil.findOneByClazz(getRoot(), Views.TextView) val newFirstTv = AccessibilityUtil.findOneByClazz(getRoot(), Views.TextView)
val nickname = newFirstTv?.text?.toString() val nickname = newFirstTv?.text?.toString()
if (nickname != null) { if (nickname != null) {
log("找到我的昵称")
var corp: String? = null var corp: String? = null
val info = StringBuilder() val info = StringBuilder()
if (AccessibilityUtil.performClick(newFirstTv)) { if (AccessibilityUtil.performClick(newFirstTv)) {
sleep(Constant.CHANGE_PAGE_INTERVAL) sleep(Constant.CHANGE_PAGE_INTERVAL)
val rv = AccessibilityUtil.findOneByClazz(getRoot(), Views.RecyclerView) val list = AccessibilityUtil.findOneByClazz(getRoot(), Views.RecyclerView, Views.ListView, Views.ViewGroup, minChildCount = 2)
if (rv != null && rv.childCount > 0) { if (list != null) {
val myInfoLayout = rv.getChild(0) val myInfoLayout = list.getChild(0)
val tvList = AccessibilityUtil.findAllByClazz(myInfoLayout, Views.TextView) val tvList = AccessibilityUtil.findAllByClazz(myInfoLayout, Views.TextView)
.filter { it.text != null } .filter { it.text != null }
if (tvList.isNotEmpty()) { if (tvList.isNotEmpty()) {
@@ -88,6 +90,7 @@ object WeworkGetImpl {
return true return true
} else { } else {
LogUtils.d("未找到我的昵称") LogUtils.d("未找到我的昵称")
log("未找到我的昵称")
return false return false
} }
} }
@@ -141,15 +144,16 @@ 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 = AccessibilityUtil.findOneByText(getRoot(), "微信用户创建", timeout = 2000) val tvManagerFlag = AccessibilityUtil.findOneByText(getRoot(), "全部群成员", "微信用户创建", timeout = 2000)
//不是管理员的群可能没有微信用户创建 todo if (tvManagerFlag != null && tvManagerFlag.text.contains("微信用户创建")) {
// AccessibilityUtil.findOneByText(getRoot(), "全部群成员", timeout = 2000)
val button = AccessibilityUtil.findFrontNode(tvManagerFlag) val button = AccessibilityUtil.findFrontNode(tvManagerFlag)
val tvGroupName = AccessibilityUtil.findOnceByClazz(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 { }
}
if (weworkMessageBean.groupName.isNullOrEmpty()) {
val groupNameTv = AccessibilityUtil.findOnceByText(getRoot(), "群聊名称") val groupNameTv = AccessibilityUtil.findOnceByText(getRoot(), "群聊名称")
if (groupNameTv != null) { if (groupNameTv != null) {
val tvList = AccessibilityUtil.findAllOnceByClazz( val tvList = AccessibilityUtil.findAllOnceByClazz(

View File

@@ -227,11 +227,11 @@ object WeworkLoopImpl {
} }
} }
if (!isAtHome()) return true if (!isAtHome()) return true
if (logIndex++ % 15 == 0) { if (logIndex++ % 30 == 0) {
LogUtils.i("读取首页聊天列表") LogUtils.i("读取首页聊天列表")
log("读取首页聊天列表") if (logIndex % 120 == 0) log("读取首页聊天列表")
} }
val listview = AccessibilityUtil.findOneByClazz(getRoot(), Views.RecyclerView, Views.ListView) val listview = AccessibilityUtil.findOneByClazz(getRoot(), Views.RecyclerView, Views.ListView, Views.ViewGroup)
if (listview != null) { if (listview != null) {
if (listview.childCount >= 2) { if (listview.childCount >= 2) {
if (checkUnreadChatRoom(listview)) { if (checkUnreadChatRoom(listview)) {

View File

@@ -273,11 +273,14 @@ object WeworkOperationImpl {
extraText: String? = null extraText: String? = null
): Boolean { ): Boolean {
goHomeTab("工作台") goHomeTab("工作台")
val node = AccessibilityUtil.scrollAndFindByText(getRoot(), "微盘") val node = AccessibilityUtil.scrollAndFindByText(WeworkController.weworkService, getRoot(), "微盘")
if (node != null) { if (node != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
AccessibilityUtil.performClick(node)
sleep(Constant.POP_WINDOW_INTERVAL) sleep(Constant.POP_WINDOW_INTERVAL)
if (AccessibilityUtil.findOnceByText(getRoot(), "微盘") != null) {
AccessibilityUtil.clickByNode(WeworkController.weworkService, node) AccessibilityUtil.clickByNode(WeworkController.weworkService, node)
}
} else { } else {
AccessibilityUtil.performClick(node) AccessibilityUtil.performClick(node)
} }
@@ -320,11 +323,14 @@ object WeworkOperationImpl {
extraText: String? = null extraText: String? = null
): Boolean { ): Boolean {
goHomeTab("工作台") goHomeTab("工作台")
val node = AccessibilityUtil.scrollAndFindByText(getRoot(), "微盘") val node = AccessibilityUtil.scrollAndFindByText(WeworkController.weworkService, getRoot(), "微盘")
if (node != null) { if (node != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
AccessibilityUtil.performClick(node)
sleep(Constant.POP_WINDOW_INTERVAL) sleep(Constant.POP_WINDOW_INTERVAL)
if (AccessibilityUtil.findOnceByText(getRoot(), "微盘") != null) {
AccessibilityUtil.clickByNode(WeworkController.weworkService, node) AccessibilityUtil.clickByNode(WeworkController.weworkService, node)
}
} else { } else {
AccessibilityUtil.performClick(node) AccessibilityUtil.performClick(node)
} }
@@ -363,7 +369,7 @@ object WeworkOperationImpl {
extraText: String? = null extraText: String? = null
): Boolean { ): Boolean {
goHomeTab("工作台") goHomeTab("工作台")
val node = AccessibilityUtil.scrollAndFindByText(getRoot(), "用过的小程序") val node = AccessibilityUtil.scrollAndFindByText(WeworkController.weworkService, getRoot(), "用过的小程序")
if (node != null) { if (node != null) {
AccessibilityUtil.performClick(node) AccessibilityUtil.performClick(node)
sleep(Constant.CHANGE_PAGE_INTERVAL) sleep(Constant.CHANGE_PAGE_INTERVAL)
@@ -591,7 +597,7 @@ object WeworkOperationImpl {
for (select in selectList) { for (select in selectList) {
AccessibilityUtil.findTextInput(getRoot(), select.replace("", "").replace("-.*$".toRegex(), "")) AccessibilityUtil.findTextInput(getRoot(), select.replace("", "").replace("-.*$".toRegex(), ""))
sleep(Constant.CHANGE_PAGE_INTERVAL * 2) sleep(Constant.CHANGE_PAGE_INTERVAL * 2)
val selectListView = AccessibilityUtil.findOneByClazz(getRoot(), Views.ListView) val selectListView = AccessibilityUtil.findOneByClazz(getRoot(), Views.ListView, Views.RecyclerView, Views.ViewGroup, minChildCount = 2)
val imageView = AccessibilityUtil.findOneByClazz(selectListView, Views.ImageView) val imageView = AccessibilityUtil.findOneByClazz(selectListView, Views.ImageView)
if (imageView != null) { if (imageView != null) {
AccessibilityUtil.performClick(imageView) AccessibilityUtil.performClick(imageView)
@@ -634,11 +640,19 @@ object WeworkOperationImpl {
*/ */
private fun createGroup(): Boolean { private fun createGroup(): Boolean {
goHomeTab("工作台") goHomeTab("工作台")
val node = AccessibilityUtil.scrollAndFindByText(getRoot(), "客户群", "居民群") val node = AccessibilityUtil.scrollAndFindByText(WeworkController.weworkService, getRoot(), "客户群", "居民群")
?: return false if (node != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
AccessibilityUtil.performClick(node)
sleep(Constant.POP_WINDOW_INTERVAL) sleep(Constant.POP_WINDOW_INTERVAL)
if (AccessibilityUtil.findOnceByText(getRoot(), "客户群", "居民群") != null) {
if (AccessibilityUtil.clickByNode(WeworkController.weworkService, node)) { if (AccessibilityUtil.clickByNode(WeworkController.weworkService, node)) {
LogUtils.d("进入客户群应用")
val textView =
AccessibilityUtil.findOneByText(getRoot(), "创建一个客户群", "创建一个居民群")
return AccessibilityUtil.performClick(textView)
}
} else {
LogUtils.d("进入客户群应用") LogUtils.d("进入客户群应用")
val textView = AccessibilityUtil.findOneByText(getRoot(), "创建一个客户群", "创建一个居民群") val textView = AccessibilityUtil.findOneByText(getRoot(), "创建一个客户群", "创建一个居民群")
return AccessibilityUtil.performClick(textView) return AccessibilityUtil.performClick(textView)
@@ -648,7 +662,9 @@ object WeworkOperationImpl {
val textView = AccessibilityUtil.findOneByText(getRoot(), "创建一个客户群", "创建一个居民群") val textView = AccessibilityUtil.findOneByText(getRoot(), "创建一个客户群", "创建一个居民群")
return AccessibilityUtil.performClick(textView) return AccessibilityUtil.performClick(textView)
} }
LogUtils.d("未找到客户群应用") }
LogUtils.e("未找到客户群应用")
log("未找到客户群应用")
return false return false
} }
@@ -657,8 +673,9 @@ object WeworkOperationImpl {
*/ */
private fun groupRename(groupName: String): Boolean { private fun groupRename(groupName: String): Boolean {
if (WeworkRoomUtil.intoGroupManager()) { if (WeworkRoomUtil.intoGroupManager()) {
val textView = AccessibilityUtil.findOneByText(getRoot(), "微信用户创建") val textView = AccessibilityUtil.findOneByText(getRoot(), "全部群成员", "微信用户创建")
//todo 微信用户创建可能找不到 ?: return false
if (textView.text.contains("微信用户创建")) {
val button = AccessibilityUtil.findFrontNode(textView) val button = AccessibilityUtil.findFrontNode(textView)
if (button != null) { if (button != null) {
AccessibilityUtil.performClick(button) AccessibilityUtil.performClick(button)
@@ -671,6 +688,18 @@ object WeworkOperationImpl {
LogUtils.e("未找到填写群名按钮") LogUtils.e("未找到填写群名按钮")
return false return false
} }
} else {
val result = AccessibilityUtil.findTextAndClick(getRoot(), "群聊名称")
if (result) {
AccessibilityUtil.findTextInput(getRoot(), groupName)
val confirmButton = AccessibilityUtil.findOneByText(getRoot(), "确定")
AccessibilityUtil.performClick(confirmButton)
sleep(2000)
return true
} else {
LogUtils.e("未找到群聊名称按钮")
}
}
} }
return false return false
} }
@@ -687,9 +716,14 @@ object WeworkOperationImpl {
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) {
if (gridView.childCount == 2) { val tvEmptySize = AccessibilityUtil.findAllOnceByClazz(gridView, Views.TextView)
.filter { it.text == null }.size
LogUtils.v("tvEmptySize: $tvEmptySize")
if (tvEmptySize == 0) {
return true
} else if (tvEmptySize == 1) {
AccessibilityUtil.performClick(gridView.getChild(gridView.childCount - 1)) AccessibilityUtil.performClick(gridView.getChild(gridView.childCount - 1))
} else { } else if (tvEmptySize == 2) {
AccessibilityUtil.performClick(gridView.getChild(gridView.childCount - 2)) AccessibilityUtil.performClick(gridView.getChild(gridView.childCount - 2))
} }
} else { } else {
@@ -706,10 +740,9 @@ object WeworkOperationImpl {
AccessibilityUtil.performClick(multiButton) AccessibilityUtil.performClick(multiButton)
AccessibilityUtil.findTextInput(getRoot(), select) AccessibilityUtil.findTextInput(getRoot(), select)
sleep(Constant.POP_WINDOW_INTERVAL) sleep(Constant.POP_WINDOW_INTERVAL)
val selectListViewTemp =
AccessibilityUtil.findOneByClazz(getRoot(), Views.ListView)
val selectListView = val selectListView =
AccessibilityUtil.findOnceByClazz(getRoot(), Views.RecyclerView) ?: selectListViewTemp AccessibilityUtil.findOneByClazz(getRoot(), Views.ListView, Views.RecyclerView, Views.ViewGroup, minChildCount = 2)
if (selectListView != null) {
val imageView = val imageView =
AccessibilityUtil.findOneByClazz(selectListView, Views.ImageView, root = false) AccessibilityUtil.findOneByClazz(selectListView, Views.ImageView, root = false)
AccessibilityUtil.performClick(imageView) AccessibilityUtil.performClick(imageView)
@@ -717,6 +750,9 @@ object WeworkOperationImpl {
if (textView != null && textView.text.isNullOrBlank()) { if (textView != null && textView.text.isNullOrBlank()) {
AccessibilityUtil.performClick(textView) AccessibilityUtil.performClick(textView)
} }
} else {
LogUtils.e("未查到列表结果")
}
} }
if (showMessageHistory) { if (showMessageHistory) {
AccessibilityUtil.findTextAndClick(getRoot(), "聊天记录") AccessibilityUtil.findTextAndClick(getRoot(), "聊天记录")
@@ -749,9 +785,12 @@ object WeworkOperationImpl {
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) {
if (gridView.childCount == 2) { val tvEmptySize = AccessibilityUtil.findAllOnceByClazz(gridView, Views.TextView)
.filter { it.text == null }.size
LogUtils.v("tvEmptySize: $tvEmptySize")
if (tvEmptySize <= 1) {
return true return true
} else { } else if (tvEmptySize == 2) {
AccessibilityUtil.performClick(gridView.getChild(gridView.childCount - 1)) AccessibilityUtil.performClick(gridView.getChild(gridView.childCount - 1))
} }
} else { } else {
@@ -767,8 +806,9 @@ object WeworkOperationImpl {
for (select in removeList) { for (select in removeList) {
AccessibilityUtil.performClick(multiButton) AccessibilityUtil.performClick(multiButton)
AccessibilityUtil.findTextInput(getRoot(), select) AccessibilityUtil.findTextInput(getRoot(), select)
sleep(Constant.POP_WINDOW_INTERVAL)
val selectListView = val selectListView =
AccessibilityUtil.findOneByClazz(getRoot(), Views.ListView) AccessibilityUtil.findOneByClazz(getRoot(), Views.ListView, Views.RecyclerView, Views.ViewGroup, minChildCount = 2)
val imageView = val imageView =
AccessibilityUtil.findOneByClazz(selectListView, Views.ImageView, root = false) AccessibilityUtil.findOneByClazz(selectListView, Views.ImageView, root = false)
AccessibilityUtil.performClick(imageView) AccessibilityUtil.performClick(imageView)
@@ -808,11 +848,16 @@ object WeworkOperationImpl {
val textView = AccessibilityUtil.findOneByText(getRoot(), "群公告") val textView = AccessibilityUtil.findOneByText(getRoot(), "群公告")
if (textView != null) { if (textView != null) {
AccessibilityUtil.performClick(textView) AccessibilityUtil.performClick(textView)
val editButton = AccessibilityUtil.findOneByText(getRoot(), "编辑", timeout = 2000) val editButton = AccessibilityUtil.findOneByText(getRoot(), "编辑", timeout = 2000, exact = true)
if (editButton != null) { if (editButton != null) {
LogUtils.d("群公告编辑中: $groupAnnouncement") LogUtils.d("群公告编辑中: $groupAnnouncement")
var retry = 0
while (retry++ < 10) {
AccessibilityUtil.performClick(editButton) AccessibilityUtil.performClick(editButton)
sleep(1000) sleep(Constant.POP_WINDOW_INTERVAL)
if (AccessibilityUtil.findOnceByText(getRoot(), "编辑", exact = true) == null)
break
}
} }
if (AccessibilityUtil.findTextInput(getRoot(), groupAnnouncement)) { if (AccessibilityUtil.findTextInput(getRoot(), groupAnnouncement)) {
LogUtils.d("群公告发布中: $groupAnnouncement") LogUtils.d("群公告发布中: $groupAnnouncement")
@@ -849,8 +894,25 @@ object WeworkOperationImpl {
val atFlag = AccessibilityUtil.findOneByText(getRoot(), "选择提醒的人", timeout = 2000) val atFlag = AccessibilityUtil.findOneByText(getRoot(), "选择提醒的人", timeout = 2000)
if (atFlag != null) { if (atFlag != null) {
val rv = AccessibilityUtil.findOneByClazz(getRoot(), Views.RecyclerView) val rv = AccessibilityUtil.findOneByClazz(getRoot(), Views.RecyclerView)
if (rv != null) {
AccessibilityUtil.findTextInput(getRoot(), at) AccessibilityUtil.findTextInput(getRoot(), at)
val atNode = AccessibilityUtil.findOneByText(rv, at, root = false, timeout = 2000) val atNode =
AccessibilityUtil.findOneByText(rv, at, root = false, timeout = 2000)
if (atNode != null) {
AccessibilityUtil.performClick(atNode)
} else {
LogUtils.e("未找到at人: $at")
atFailed = true
backPress()
}
sleep(Constant.POP_WINDOW_INTERVAL)
} else {
val searchFlag = AccessibilityUtil.findOnceByText(getRoot(), "搜索", exact = true)
val list = AccessibilityUtil.findBackNode(searchFlag, minChildCount = 2)
if (list != null) {
AccessibilityUtil.findTextInput(getRoot(), at)
val atNode =
AccessibilityUtil.findOneByText(list, at, root = false, timeout = 2000)
if (atNode != null) { if (atNode != null) {
AccessibilityUtil.performClick(atNode) AccessibilityUtil.performClick(atNode)
} else { } else {
@@ -861,13 +923,15 @@ object WeworkOperationImpl {
sleep(Constant.POP_WINDOW_INTERVAL) sleep(Constant.POP_WINDOW_INTERVAL)
} }
} }
}
}
val content = if (atFailed) "@$at $prefix$text" else "$prefix$text" val content = if (atFailed) "@$at $prefix$text" else "$prefix$text"
if (AccessibilityUtil.findTextInput(getRoot(), content, append = !atFailed)) { if (AccessibilityUtil.findTextInput(getRoot(), content, append = !atFailed)) {
val sendButton = AccessibilityUtil.findAllByClazz(getRoot(), Views.Button) val sendButton = AccessibilityUtil.findAllByClazz(getRoot(), Views.Button)
.firstOrNull { it.text == "发送" } .firstOrNull { it.text == "发送" }
if (sendButton != null) { if (sendButton != null) {
LogUtils.d("发送消息: \n$text") LogUtils.d("发送消息: \n$content")
log("发送消息: \n$text") log("发送消息: \n$content")
AccessibilityUtil.performClick(sendButton) AccessibilityUtil.performClick(sendButton)
} else { } else {
LogUtils.e("未找到发送按钮") LogUtils.e("未找到发送按钮")

View File

@@ -20,6 +20,8 @@ import org.yameida.worktool.service.getRoot
import java.lang.Exception import java.lang.Exception
import java.lang.Thread.sleep import java.lang.Thread.sleep
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import com.blankj.utilcode.util.ScreenUtils
import org.yameida.worktool.service.WeworkController
/** /**
* 1.查询类 * 1.查询类
@@ -96,33 +98,53 @@ object AccessibilityUtil {
//寻找第一个列表并点击指定条目(默认点击第一个条目) //寻找第一个列表并点击指定条目(默认点击第一个条目)
fun findListOneAndClick(nodeInfo: AccessibilityNodeInfo, index: Int = 0): Boolean { fun findListOneAndClick(nodeInfo: AccessibilityNodeInfo, index: Int = 0): Boolean {
val rv = findOnceByClazz(nodeInfo, "androidx.recyclerview.widget.RecyclerView") val list = findOnceByClazz(nodeInfo, "androidx.recyclerview.widget.RecyclerView", "android.widget.ListView")
val lv = findOnceByClazz(nodeInfo, "android.widget.ListView") if (list != null && list.childCount > index) {
if (rv == null && lv == null) return false return performClick(list.getChild(index))
if (rv != null && rv.childCount > index) {
performClick(rv.getChild(index))
} else if (lv != null && lv.childCount > index) {
performClick(lv.getChild(index))
} }
return true return false
} }
//滚动并按文本寻找第一个控件 //滚动并按文本寻找第一个控件
fun scrollAndFindByText( fun scrollAndFindByText(
service: AccessibilityService,
nodeInfo: AccessibilityNodeInfo, nodeInfo: AccessibilityNodeInfo,
vararg textList: String, vararg textList: String,
maxRetry: Int = 3 maxRetry: Int = 3
): AccessibilityNodeInfo? { ): AccessibilityNodeInfo? {
var index = 0 var index = 0
while (index++ < maxRetry) { while (index++ < maxRetry) {
performScrollDown(nodeInfo, 0)
val node = findOnceByText(nodeInfo, *textList)
if (node != null) {
return node
}
}
index = 0
while (index++ < maxRetry * 2) {
performScrollUp(nodeInfo, 0) performScrollUp(nodeInfo, 0)
val node = findOnceByText(nodeInfo, *textList) val node = findOnceByText(nodeInfo, *textList)
if (node != null) { if (node != null) {
return node return node
} }
} }
LogUtils.d("未找到可滚动列表 使用手势滚动")
val width = ScreenUtils.getScreenWidth()
val height = ScreenUtils.getScreenHeight()
index = 0
while (index++ < maxRetry * 2) { while (index++ < maxRetry * 2) {
performScrollDown(nodeInfo, 0) scrollByXY(service, width / 2, height / 2, 0, -height / 2)
sleep(SCROLL_INTERVAL)
val node = findOnceByText(nodeInfo, *textList)
if (node != null) {
return node
}
}
index = 0
while (index++ < maxRetry * 3) {
scrollByXY(service, width / 2, height / 2, 0, height / 2)
sleep(SCROLL_INTERVAL)
val node = findOnceByText(nodeInfo, *textList) val node = findOnceByText(nodeInfo, *textList)
if (node != null) { if (node != null) {
return node return node
@@ -141,11 +163,11 @@ object AccessibilityUtil {
val gesture = builder.build() val gesture = builder.build()
return service.dispatchGesture(gesture, object : GestureResultCallback() { return service.dispatchGesture(gesture, object : GestureResultCallback() {
override fun onCompleted(gestureDescription: GestureDescription) { override fun onCompleted(gestureDescription: GestureDescription) {
LogUtils.v("click okk onCompleted") LogUtils.v("click ok onCompleted")
} }
override fun onCancelled(gestureDescription: GestureDescription) { override fun onCancelled(gestureDescription: GestureDescription) {
LogUtils.v("click okk onCancelled") LogUtils.v("click ok onCancelled")
} }
}, null) }, null)
} }
@@ -397,7 +419,7 @@ object AccessibilityUtil {
if (node == null) return null if (node == null) return null
val textNodeList = findAllOnceByText(node, *textList, exact = exact) val textNodeList = findAllOnceByText(node, *textList, exact = exact)
LogUtils.v("text: ${textList.joinToString()} count: " + textNodeList.size) LogUtils.v("text: ${textList.joinToString()} count: " + textNodeList.size)
if (exact) return textNodeList[0] if (exact && textNodeList.size > 0) return textNodeList[0]
else if (textNodeList.size > 0) { else if (textNodeList.size > 0) {
for (textNode in textNodeList) { for (textNode in textNodeList) {
for (text in textList) { for (text in textList) {
@@ -486,13 +508,14 @@ object AccessibilityUtil {
limitDepth: Int? = null, limitDepth: Int? = null,
depth: Int = 0, depth: Int = 0,
timeout: Long = 5000, timeout: Long = 5000,
root: Boolean = true root: Boolean = true,
minChildCount: Int = 0
): AccessibilityNodeInfo? { ): AccessibilityNodeInfo? {
var node = node ?: return null var node = node ?: return null
val startTime = System.currentTimeMillis() val startTime = System.currentTimeMillis()
var currentTime = startTime var currentTime = startTime
while (currentTime - startTime <= timeout) { while (currentTime - startTime <= timeout) {
val result = findOnceByClazz(node, *clazzList, limitDepth = limitDepth, depth = depth) val result = findOnceByClazz(node, *clazzList, limitDepth = limitDepth, depth = depth, minChildCount = minChildCount)
LogUtils.v("clazz: ${clazzList.joinToString()} result == null: ${result == null}") LogUtils.v("clazz: ${clazzList.joinToString()} result == null: ${result == null}")
if (result != null) return result if (result != null) return result
sleep(SHORT_INTERVAL) sleep(SHORT_INTERVAL)
@@ -503,7 +526,7 @@ object AccessibilityUtil {
} }
currentTime = System.currentTimeMillis() currentTime = System.currentTimeMillis()
} }
LogUtils.e("findOneByClazz Exception()") LogUtils.e("findOneByClazz Exception(): ${clazzList.joinToString()}")
Exception().printStackTrace() Exception().printStackTrace()
return null return null
} }
@@ -518,15 +541,16 @@ object AccessibilityUtil {
node: AccessibilityNodeInfo?, node: AccessibilityNodeInfo?,
vararg clazzList: String, vararg clazzList: String,
limitDepth: Int? = null, limitDepth: Int? = null,
depth: Int = 0 depth: Int = 0,
minChildCount: Int = 0
): AccessibilityNodeInfo? { ): AccessibilityNodeInfo? {
if (node == null) return null if (node == null) return null
if (node.className in clazzList) { if (node.className in clazzList) {
if (limitDepth == null || limitDepth == depth) if ((limitDepth == null || limitDepth == depth) && node.childCount >= minChildCount)
return node return node
} }
for (i in 0 until node.childCount) { for (i in 0 until node.childCount) {
val result = findOnceByClazz(node.getChild(i), *clazzList, limitDepth = limitDepth, depth = depth + 1) val result = findOnceByClazz(node.getChild(i), *clazzList, limitDepth = limitDepth, depth = depth + 1, minChildCount = minChildCount)
if (result != null) return result if (result != null) return result
} }
return null return null
@@ -560,7 +584,7 @@ object AccessibilityUtil {
} }
currentTime = System.currentTimeMillis() currentTime = System.currentTimeMillis()
} }
LogUtils.e("findAllByClazz Exception()") LogUtils.e("findAllByClazz Exception(): ${clazzList.joinToString()}")
Exception().printStackTrace() Exception().printStackTrace()
return arrayListOf() return arrayListOf()
} }
@@ -705,12 +729,115 @@ object AccessibilityUtil {
val gesture = builder.build() val gesture = builder.build()
return service.dispatchGesture(gesture, object : GestureResultCallback() { return service.dispatchGesture(gesture, object : GestureResultCallback() {
override fun onCompleted(gestureDescription: GestureDescription) { override fun onCompleted(gestureDescription: GestureDescription) {
LogUtils.v("click okk onCompleted") LogUtils.v("click ok onCompleted")
} }
override fun onCancelled(gestureDescription: GestureDescription) { override fun onCancelled(gestureDescription: GestureDescription) {
LogUtils.v("click okk onCancelled") LogUtils.v("click ok onCancelled")
} }
}, null) }, null)
} }
/**
* 向下滚动
* Gesture手势实现滚动(Android7+)
* 解决 scrollable=false 无法滚动问题
*/
@RequiresApi(api = Build.VERSION_CODES.N)
fun scrollDownByNode(
service: AccessibilityService,
nodeInfo: AccessibilityNodeInfo
): Boolean {
val rect = Rect()
nodeInfo.getBoundsInScreen(rect)
val x: Int = (rect.left + rect.right) / 2
val y: Int = (rect.top + rect.bottom) / 2
val point = Point(x, y)
val builder = GestureDescription.Builder()
val path = Path()
path.moveTo(point.x.toFloat(), point.y.toFloat())
builder.addStroke(StrokeDescription(path, 0L, 300L))
val gesture = builder.build()
return service.dispatchGesture(gesture, object : GestureResultCallback() {
override fun onCompleted(gestureDescription: GestureDescription) {
LogUtils.v("click ok onCompleted")
}
override fun onCancelled(gestureDescription: GestureDescription) {
LogUtils.v("click ok onCancelled")
}
}, null)
}
/**
* Gesture手势实现滚动(Android7+)
* 解决滚动距离不可控制问题
* @param distanceX 向右滚动为负值 向左滚动为正值
* @param distanceY 向下滚动为负值 向上滚动为正值
*/
fun scrollByNode(
service: AccessibilityService,
nodeInfo: AccessibilityNodeInfo,
distanceX: Int = 0,
distanceY: Int = 0
): Boolean {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
val rect = Rect()
nodeInfo.getBoundsInScreen(rect)
val point = Point((rect.left + rect.right) / 2, (rect.top + rect.bottom) / 2)
val builder = GestureDescription.Builder()
val path = Path()
path.moveTo(point.x.toFloat(), point.y.toFloat())
path.lineTo(point.x.toFloat() + distanceX, point.y.toFloat() + distanceY)
builder.addStroke(StrokeDescription(path, 0L, 300L))
val gesture = builder.build()
return service.dispatchGesture(gesture, object : GestureResultCallback() {
override fun onCompleted(gestureDescription: GestureDescription) {
LogUtils.v("scroll ok onCompleted")
}
override fun onCancelled(gestureDescription: GestureDescription) {
LogUtils.v("scroll ok onCancelled")
}
}, null)
} else {
LogUtils.e("系统版本<7.0 不支持手势操作")
return false
}
}
/**
* Gesture手势实现滚动(Android7+)
* 解决滚动距离不可控制问题
* @param distanceX 向右滚动为负值 向左滚动为正值
* @param distanceY 向下滚动为负值 向上滚动为正值
*/
fun scrollByXY(
service: AccessibilityService,
x: Int = 0,
y: Int = 0,
distanceX: Int = 0,
distanceY: Int = 0
): Boolean {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
val builder = GestureDescription.Builder()
val path = Path()
path.moveTo(x.toFloat(), y.toFloat())
path.lineTo(x.toFloat() + distanceX, y.toFloat() + distanceY)
builder.addStroke(StrokeDescription(path, 0L, 300L))
val gesture = builder.build()
return service.dispatchGesture(gesture, object : GestureResultCallback() {
override fun onCompleted(gestureDescription: GestureDescription) {
LogUtils.v("scroll ok onCompleted")
}
override fun onCancelled(gestureDescription: GestureDescription) {
LogUtils.v("scroll ok onCancelled")
}
}, null)
} else {
LogUtils.e("系统版本<7.0 不支持手势操作")
return false
}
}
} }

View File

@@ -92,7 +92,7 @@ object WeworkRoomUtil {
return true return true
} }
goHome() goHome()
val list = findOneByClazz(getRoot(), Views.RecyclerView, Views.ListView) val list = findOneByClazz(getRoot(), Views.RecyclerView, Views.ListView, Views.ViewGroup)
if (list != null) { if (list != null) {
val frontNode = findFrontNode(list) val frontNode = findFrontNode(list)
val textViewList = findAllOnceByClazz(frontNode, Views.TextView) val textViewList = findAllOnceByClazz(frontNode, Views.TextView)
@@ -106,6 +106,7 @@ object WeworkRoomUtil {
val imageView = AccessibilityUtil.findOnceByClazz(selectListView, Views.ImageView) val imageView = AccessibilityUtil.findOnceByClazz(selectListView, Views.ImageView)
if (imageView != null) { if (imageView != null) {
AccessibilityUtil.performClick(imageView) AccessibilityUtil.performClick(imageView)
LogUtils.d("进入房间: $title")
sleep(Constant.CHANGE_PAGE_INTERVAL) sleep(Constant.CHANGE_PAGE_INTERVAL)
return true return true
} else { } else {
@@ -125,8 +126,7 @@ object WeworkRoomUtil {
* @return true 成功进入群管理页 * @return true 成功进入群管理页
*/ */
fun intoGroupManager(): Boolean { fun intoGroupManager(): Boolean {
if (AccessibilityUtil.findOnceByText(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)

View File

@@ -2,8 +2,10 @@ package org.yameida.worktool.utils
import android.view.accessibility.AccessibilityNodeInfo import android.view.accessibility.AccessibilityNodeInfo
import com.blankj.utilcode.util.LogUtils import com.blankj.utilcode.util.LogUtils
import org.yameida.worktool.Constant
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.findAllByClazz import org.yameida.worktool.utils.AccessibilityUtil.findAllByClazz
import org.yameida.worktool.utils.AccessibilityUtil.findAllOnceByClazz import org.yameida.worktool.utils.AccessibilityUtil.findAllOnceByClazz
import java.util.* import java.util.*
@@ -319,16 +321,15 @@ object WeworkTextUtil {
private fun longClickMessageItem(item: AccessibilityNodeInfo, roomType: Int, key: String): Boolean { private fun longClickMessageItem(item: AccessibilityNodeInfo, roomType: Int, key: String): Boolean {
val backNode = getMessageListNode(item, roomType) val backNode = getMessageListNode(item, roomType)
AccessibilityUtil.performLongClickWithSon(backNode) AccessibilityUtil.performLongClickWithSon(backNode)
val optionRvList = findAllByClazz(getRoot(), Views.RecyclerView) sleep(Constant.POP_WINDOW_INTERVAL)
val optionRvList = findAllByClazz(getRoot(), Views.RecyclerView, Views.ViewGroup)
for (optionRv in optionRvList) { for (optionRv in optionRvList) {
val optionTvList = findAllOnceByClazz(optionRv, Views.TextView) val keyTv = AccessibilityUtil.findOnceByText(optionRv, key, exact = true)
for (optionTv in optionTvList) { if (keyTv != null) {
if (optionTv.text == key) { AccessibilityUtil.performClick(keyTv)
AccessibilityUtil.performClick(optionTv)
return true return true
} }
} }
}
return false return false
} }