update 全面兼容企业微信最新版本(4.0.10)和政务微信;控件搜索优化;已知问题修复;
This commit is contained in:
@@ -9,8 +9,8 @@ android {
|
|||||||
applicationId "org.yameida.worktool"
|
applicationId "org.yameida.worktool"
|
||||||
minSdkVersion 23
|
minSdkVersion 23
|
||||||
targetSdkVersion 30
|
targetSdkVersion 30
|
||||||
versionCode 1
|
versionCode 2
|
||||||
versionName "1.3"
|
versionName "2.0"
|
||||||
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
package="org.yameida.worktool">
|
package="org.yameida.worktool">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
@@ -17,6 +18,8 @@
|
|||||||
<uses-permission android:name="android.permission.VIBRATE" />
|
<uses-permission android:name="android.permission.VIBRATE" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_DOWNLOAD_MANAGER" />
|
<uses-permission android:name="android.permission.ACCESS_DOWNLOAD_MANAGER" />
|
||||||
<uses-permission android:name="android.permission.EXPAND_STATUS_BAR" />
|
<uses-permission android:name="android.permission.EXPAND_STATUS_BAR" />
|
||||||
|
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
|
||||||
|
tools:ignore="QueryAllPackagesPermission" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name="org.yameida.worktool.MyApplication"
|
android:name="org.yameida.worktool.MyApplication"
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import org.yameida.worktool.config.WebConfig
|
|||||||
|
|
||||||
object Constant {
|
object Constant {
|
||||||
|
|
||||||
|
val AVAILABLE_VERSION = arrayListOf("4.0.2", "4.0.6", "4.0.8", "4.0.10")
|
||||||
const val PACKAGE_NAMES = "com.tencent.wework"
|
const val PACKAGE_NAMES = "com.tencent.wework"
|
||||||
val BASE_URL = WebConfig.HOST.replace("wss", "https").replace("ws", "http")
|
val BASE_URL = WebConfig.HOST.replace("wss", "https").replace("ws", "http")
|
||||||
val URL_CHECK_UPDATE = "$BASE_URL/appUpdate/checkUpdate"
|
val URL_CHECK_UPDATE = "$BASE_URL/appUpdate/checkUpdate"
|
||||||
@@ -15,5 +16,6 @@ object Constant {
|
|||||||
var key = "9876543210abcdef".toByteArray()
|
var key = "9876543210abcdef".toByteArray()
|
||||||
var iv = "0123456789abcdef".toByteArray()
|
var iv = "0123456789abcdef".toByteArray()
|
||||||
val transformation = "AES/CBC/PKCS7Padding"
|
val transformation = "AES/CBC/PKCS7Padding"
|
||||||
var encryptType = SPUtils.getInstance().getInt("encryptType", 0)
|
var encryptType = SPUtils.getInstance().getInt("encryptType", 1)
|
||||||
|
var autoReply = SPUtils.getInstance().getInt("autoReply", 1)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -107,13 +107,14 @@ object Demo {
|
|||||||
"titleList":[
|
"titleList":[
|
||||||
"$name"
|
"$name"
|
||||||
],
|
],
|
||||||
"receivedContent":"你好~我是机器人,你可以和我聊天,你也可以通过API文档来让我发送消息或完成建群等任务。接口文档:https://www.apifox.cn/apidoc/project-1035094/api-23520034"
|
"receivedContent":"你好~我是机器人,你可以@我和我聊天,你也可以通过API文档来让我发送消息或完成建群等任务。接口文档:https://www.apifox.cn/apidoc/project-1035094/api-23520034"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": 206,
|
"type": 206,
|
||||||
"groupName": "$groupName",
|
"groupName": "$groupName",
|
||||||
"selectList": [
|
"selectList": [
|
||||||
"$name"
|
"$name",
|
||||||
|
"尹甲仑"
|
||||||
],
|
],
|
||||||
"groupAnnouncement": "(自动填写群公告) WorkTool欢迎大家~WorkTool管家是机器人,有问题可以在QQ群反馈~@我可以聊天~"
|
"groupAnnouncement": "(自动填写群公告) WorkTool欢迎大家~WorkTool管家是机器人,有问题可以在QQ群反馈~@我可以聊天~"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,15 +52,39 @@ class ListenActivity : AppCompatActivity() {
|
|||||||
})
|
})
|
||||||
MobclickAgent.onProfileSignIn(channel)
|
MobclickAgent.onProfileSignIn(channel)
|
||||||
}
|
}
|
||||||
Constant.encryptType = SPUtils.getInstance().getInt("encryptType", 0)
|
|
||||||
sw_encrypt.isChecked = Constant.encryptType == 1
|
sw_encrypt.isChecked = Constant.encryptType == 1
|
||||||
sw_encrypt.setOnCheckedChangeListener(CompoundButton.OnCheckedChangeListener { buttonView, isChecked ->
|
sw_encrypt.setOnCheckedChangeListener(CompoundButton.OnCheckedChangeListener { buttonView, isChecked ->
|
||||||
LogUtils.i("sw_encrypt onCheckedChanged: $isChecked")
|
LogUtils.i("sw_encrypt onCheckedChanged: $isChecked")
|
||||||
Constant.encryptType = if (isChecked) 1 else 0
|
Constant.encryptType = if (isChecked) 1 else 0
|
||||||
SPUtils.getInstance().put("encryptType", Constant.encryptType)
|
SPUtils.getInstance().put("encryptType", Constant.encryptType)
|
||||||
})
|
})
|
||||||
|
sw_auto_reply.isChecked = Constant.autoReply == 1
|
||||||
|
sw_auto_reply.setOnCheckedChangeListener(CompoundButton.OnCheckedChangeListener { buttonView, isChecked ->
|
||||||
|
LogUtils.i("sw_auto_reply onCheckedChanged: $isChecked")
|
||||||
|
Constant.autoReply = if (isChecked) 1 else 0
|
||||||
|
SPUtils.getInstance().put("autoReply", Constant.autoReply)
|
||||||
|
})
|
||||||
tv_host.text = WebConfig.HOST
|
tv_host.text = WebConfig.HOST
|
||||||
tv_version.text = AppUtils.getAppVersionName()
|
tv_version.text = AppUtils.getAppVersionName()
|
||||||
|
val workVersionName = AppUtils.getAppInfo(Constant.PACKAGE_NAMES)?.versionName
|
||||||
|
when (workVersionName) {
|
||||||
|
null -> {
|
||||||
|
LogUtils.e("系统检测到您尚未安装企业微信,请先安装企业微信")
|
||||||
|
tv_work_version.text = "检测到您尚未安装企业微信,请先安装登录!"
|
||||||
|
}
|
||||||
|
in Constant.AVAILABLE_VERSION -> {
|
||||||
|
LogUtils.i("当前企业微信版本已适配: $workVersionName")
|
||||||
|
val tip = "$workVersionName 已适配,可放心使用~"
|
||||||
|
tv_work_version.text = tip
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
LogUtils.e("当前企业微信版本未兼容: $workVersionName")
|
||||||
|
val tip = "$workVersionName 可能存在部分兼容性问题!"
|
||||||
|
tv_work_version.text = tip
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SPUtils.getInstance().put("appVersion", AppUtils.getAppVersionName())
|
||||||
|
SPUtils.getInstance().put("workVersion", workVersionName)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initAccessibility() {
|
private fun initAccessibility() {
|
||||||
|
|||||||
@@ -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 = AccessibilityUtil.findAllByText(getRoot(), "消息", timeout = 0)
|
val list = AccessibilityUtil.findAllOnceByText(getRoot(), "消息", exact = true)
|
||||||
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 = AccessibilityUtil.findAllByText(getRoot(), title, timeout = 0)
|
val tempList = AccessibilityUtil.findAllOnceByText(getRoot(), title, exact = true)
|
||||||
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)
|
||||||
@@ -64,7 +64,7 @@ fun goHomeTab(title: String): Boolean {
|
|||||||
* 当前是否在首页
|
* 当前是否在首页
|
||||||
*/
|
*/
|
||||||
fun isAtHome(): Boolean {
|
fun isAtHome(): Boolean {
|
||||||
val list = AccessibilityUtil.findAllByText(getRoot(), "消息", timeout = 0)
|
val list = AccessibilityUtil.findAllOnceByText(getRoot(), "消息", exact = true)
|
||||||
return list.count { it.parent.parent.parent.childCount == 5 } > 0
|
return list.count { it.parent.parent.parent.childCount == 5 } > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,8 +85,7 @@ fun getRoot(ignoreCheck: Boolean): AccessibilityNodeInfo {
|
|||||||
val root = WeworkController.weworkService.rootInActiveWindow
|
val root = WeworkController.weworkService.rootInActiveWindow
|
||||||
if (tempRoot != root) {
|
if (tempRoot != root) {
|
||||||
LogUtils.e("tempRoot != root")
|
LogUtils.e("tempRoot != root")
|
||||||
}
|
} else if (root != null) {
|
||||||
if (root != null) {
|
|
||||||
if (root.packageName == Constant.PACKAGE_NAMES) {
|
if (root.packageName == Constant.PACKAGE_NAMES) {
|
||||||
return root
|
return root
|
||||||
} else {
|
} else {
|
||||||
@@ -124,9 +123,7 @@ fun backPress() {
|
|||||||
AccessibilityUtil.performClick(button)
|
AccessibilityUtil.performClick(button)
|
||||||
} else {
|
} else {
|
||||||
LogUtils.d("未找到BT按钮")
|
LogUtils.d("未找到BT按钮")
|
||||||
val confirm = AccessibilityUtil.findOnceByText(getRoot(), "确定")
|
val confirm = AccessibilityUtil.findOnceByText(getRoot(), "确定", "我知道了", "暂不进入", "不用了", "取消")
|
||||||
?: AccessibilityUtil.findOnceByText(getRoot(), "我知道了")
|
|
||||||
?: AccessibilityUtil.findOnceByText(getRoot(), "暂不进入")
|
|
||||||
if (confirm != null) {
|
if (confirm != null) {
|
||||||
LogUtils.d("尝试点击确定/我知道了/暂不进入")
|
LogUtils.d("尝试点击确定/我知道了/暂不进入")
|
||||||
AccessibilityUtil.performClick(confirm)
|
AccessibilityUtil.performClick(confirm)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.yameida.worktool.service
|
package org.yameida.worktool.service
|
||||||
|
|
||||||
|
import com.blankj.utilcode.util.GsonUtils
|
||||||
import com.blankj.utilcode.util.LogUtils
|
import com.blankj.utilcode.util.LogUtils
|
||||||
import org.yameida.worktool.Constant
|
import org.yameida.worktool.Constant
|
||||||
import org.yameida.worktool.model.WeworkMessageBean
|
import org.yameida.worktool.model.WeworkMessageBean
|
||||||
@@ -104,7 +105,7 @@ object WeworkGetImpl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LogUtils.d("我的信息", myInfo)
|
LogUtils.d("我的信息", GsonUtils.toJson(myInfo))
|
||||||
val weworkMessageBean = WeworkMessageBean()
|
val weworkMessageBean = WeworkMessageBean()
|
||||||
weworkMessageBean.type = WeworkMessageBean.GET_MY_INFO
|
weworkMessageBean.type = WeworkMessageBean.GET_MY_INFO
|
||||||
weworkMessageBean.myInfo = myInfo
|
weworkMessageBean.myInfo = myInfo
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ object WeworkLoopImpl {
|
|||||||
mainLoopRunning = true
|
mainLoopRunning = true
|
||||||
try {
|
try {
|
||||||
while (mainLoopRunning) {
|
while (mainLoopRunning) {
|
||||||
if (WeworkRoomUtil.getRoomType(getRoot(), false) != WeworkMessageBean.ROOM_TYPE_UNKNOWN
|
if (WeworkRoomUtil.getRoomType(false) != WeworkMessageBean.ROOM_TYPE_UNKNOWN
|
||||||
&& getChatMessageList()) {
|
&& getChatMessageList()) {
|
||||||
}
|
}
|
||||||
if (!mainLoopRunning) break
|
if (!mainLoopRunning) break
|
||||||
@@ -49,7 +49,7 @@ object WeworkLoopImpl {
|
|||||||
* 读取通讯录好友请求
|
* 读取通讯录好友请求
|
||||||
*/
|
*/
|
||||||
fun getFriendRequest(): Boolean {
|
fun getFriendRequest(): Boolean {
|
||||||
val list = AccessibilityUtil.findAllByText(getRoot(), "通讯录", timeout = 0)
|
val list = AccessibilityUtil.findAllOnceByText(getRoot(), "通讯录", exact = true)
|
||||||
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) {
|
||||||
@@ -94,9 +94,10 @@ object WeworkLoopImpl {
|
|||||||
* 2.获取消息列表
|
* 2.获取消息列表
|
||||||
*/
|
*/
|
||||||
fun getChatMessageList(timeout: Long = 3000): Boolean {
|
fun getChatMessageList(timeout: Long = 3000): Boolean {
|
||||||
|
if (Constant.autoReply == 0) return true
|
||||||
AccessibilityUtil.performScrollDown(getRoot(), 0)
|
AccessibilityUtil.performScrollDown(getRoot(), 0)
|
||||||
val roomType = WeworkRoomUtil.getRoomType(getRoot())
|
val roomType = WeworkRoomUtil.getRoomType()
|
||||||
var titleList = WeworkRoomUtil.getRoomTitle(getRoot())
|
var titleList = WeworkRoomUtil.getRoomTitle()
|
||||||
if (titleList.contains("对方正在输入…")) {
|
if (titleList.contains("对方正在输入…")) {
|
||||||
titleList = WeworkRoomUtil.getFriendName()
|
titleList = WeworkRoomUtil.getFriendName()
|
||||||
}
|
}
|
||||||
@@ -183,7 +184,7 @@ object WeworkLoopImpl {
|
|||||||
//回到上一页
|
//回到上一页
|
||||||
var retry = 5
|
var retry = 5
|
||||||
while (retry-- > 0 && !isAtHome()) {
|
while (retry-- > 0 && !isAtHome()) {
|
||||||
val textView = AccessibilityUtil.findOnceByText(getRoot(), "新的客户")
|
val textView = AccessibilityUtil.findOnceByText(getRoot(), "新的客户", "新的居民")
|
||||||
if (textView == null) {
|
if (textView == null) {
|
||||||
backPress()
|
backPress()
|
||||||
}
|
}
|
||||||
@@ -197,6 +198,7 @@ object WeworkLoopImpl {
|
|||||||
* 读取聊天列表
|
* 读取聊天列表
|
||||||
*/
|
*/
|
||||||
private fun getChatroomList(): Boolean {
|
private fun getChatroomList(): Boolean {
|
||||||
|
if (Constant.autoReply == 0) return true
|
||||||
if (!isAtHome()) return true
|
if (!isAtHome()) return true
|
||||||
if (logIndex % 3 == 0) {
|
if (logIndex % 3 == 0) {
|
||||||
AccessibilityUtil.performScrollUp(getRoot(), 0)
|
AccessibilityUtil.performScrollUp(getRoot(), 0)
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ object WeworkOperationImpl {
|
|||||||
*/
|
*/
|
||||||
fun replyMessage(
|
fun replyMessage(
|
||||||
titleList: List<String>,
|
titleList: List<String>,
|
||||||
receivedName: String,
|
receivedName: String?,
|
||||||
originalContent: String,
|
originalContent: String,
|
||||||
textType: Int,
|
textType: Int,
|
||||||
receivedContent: String
|
receivedContent: String
|
||||||
@@ -70,7 +70,11 @@ object WeworkOperationImpl {
|
|||||||
} else {
|
} else {
|
||||||
LogUtils.d("$title: 回复失败 直接发送答案")
|
LogUtils.d("$title: 回复失败 直接发送答案")
|
||||||
error("$title: 回复失败 直接发送答案 $receivedContent")
|
error("$title: 回复失败 直接发送答案 $receivedContent")
|
||||||
|
if (receivedName == null) {
|
||||||
|
sendChatMessage(receivedContent, "[自动回复]【$originalContent】\n")
|
||||||
|
} else {
|
||||||
sendChatMessage(receivedContent, "[自动回复]【$originalContent】@$receivedName\n")
|
sendChatMessage(receivedContent, "[自动回复]【$originalContent】@$receivedName\n")
|
||||||
|
}
|
||||||
WeworkLoopImpl.getChatMessageList()
|
WeworkLoopImpl.getChatMessageList()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -432,8 +436,7 @@ object WeworkOperationImpl {
|
|||||||
sleep(Constant.POP_WINDOW_INTERVAL)
|
sleep(Constant.POP_WINDOW_INTERVAL)
|
||||||
val listViewList = AccessibilityUtil.findAllByClazz(getRoot(), Views.ListView)
|
val listViewList = AccessibilityUtil.findAllByClazz(getRoot(), Views.ListView)
|
||||||
if (!listViewList.isNullOrEmpty()) {
|
if (!listViewList.isNullOrEmpty()) {
|
||||||
// if (AccessibilityUtil.findTextAndClick(listViewList.last(), "添加客户")) {
|
if (AccessibilityUtil.findTextAndClick(listViewList.last(), "添加客户", "添加居民", "加微信")) {
|
||||||
if (AccessibilityUtil.findTextAndClick(listViewList.last(), "添加")) {
|
|
||||||
AccessibilityUtil.findTextAndClick(getRoot(), "搜索手机号添加")
|
AccessibilityUtil.findTextAndClick(getRoot(), "搜索手机号添加")
|
||||||
AccessibilityUtil.findTextInput(getRoot(), friend.phone.trim())
|
AccessibilityUtil.findTextInput(getRoot(), friend.phone.trim())
|
||||||
if (AccessibilityUtil.findTextAndClick(getRoot(), "网络查找手机")) {
|
if (AccessibilityUtil.findTextAndClick(getRoot(), "网络查找手机")) {
|
||||||
@@ -500,7 +503,7 @@ object WeworkOperationImpl {
|
|||||||
}
|
}
|
||||||
if (AccessibilityUtil.findTextAndClick(getRoot(), "添加为联系人")) {
|
if (AccessibilityUtil.findTextAndClick(getRoot(), "添加为联系人")) {
|
||||||
LogUtils.d("添加好友成功: " + friend.phone)
|
LogUtils.d("添加好友成功: " + friend.phone)
|
||||||
if (AccessibilityUtil.findTextAndClick(getRoot(), "发送添加邀请")) {
|
if (AccessibilityUtil.findTextAndClick(getRoot(), "发送添加邀请", "发送申请")) {
|
||||||
LogUtils.d("发送添加邀请成功: " + friend.phone)
|
LogUtils.d("发送添加邀请成功: " + friend.phone)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -565,38 +568,33 @@ object WeworkOperationImpl {
|
|||||||
private fun relaySelectTarget(selectList: List<String>, extraText: String? = null): Boolean {
|
private fun relaySelectTarget(selectList: List<String>, extraText: String? = null): Boolean {
|
||||||
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, 2)
|
||||||
val textViewList = AccessibilityUtil.findAllOnceByClazz(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)
|
|
||||||
AccessibilityUtil.performClick(searchButton)
|
AccessibilityUtil.performClick(searchButton)
|
||||||
//todo 搜索需要在循环内
|
|
||||||
sleep(1000)
|
|
||||||
for (select in selectList) {
|
for (select in selectList) {
|
||||||
AccessibilityUtil.findTextInput(getRoot(), select)
|
AccessibilityUtil.findTextInput(getRoot(), select.replace("…", "").replace("-.*$".toRegex(), ""))
|
||||||
sleep(2000)
|
sleep(Constant.CHANGE_PAGE_INTERVAL * 2)
|
||||||
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)
|
||||||
}
|
}
|
||||||
sleep(1000)
|
sleep(Constant.CHANGE_PAGE_INTERVAL)
|
||||||
}
|
}
|
||||||
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)
|
sleep(Constant.POP_WINDOW_INTERVAL)
|
||||||
if (!extraText.isNullOrBlank()) {
|
if (!extraText.isNullOrBlank()) {
|
||||||
LogUtils.d("extraText: $extraText")
|
LogUtils.d("extraText: $extraText")
|
||||||
AccessibilityUtil.findTextInput(getRoot(), extraText)
|
AccessibilityUtil.findTextInput(getRoot(), extraText)
|
||||||
sleep(1000)
|
|
||||||
}
|
}
|
||||||
val sendButtonList = AccessibilityUtil.findAllByText(getRoot(), "发送", timeout = 0)
|
val sendButtonList = AccessibilityUtil.findAllByText(getRoot(), "发送")
|
||||||
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)
|
||||||
@@ -623,10 +621,10 @@ object WeworkOperationImpl {
|
|||||||
*/
|
*/
|
||||||
private fun createGroup(): Boolean {
|
private fun createGroup(): Boolean {
|
||||||
goHomeTab("工作台")
|
goHomeTab("工作台")
|
||||||
val textViewGroup = AccessibilityUtil.scrollAndFindByText(getRoot(), "客户群")
|
val textViewGroup = AccessibilityUtil.scrollAndFindByText(getRoot(), "客户群", "居民群")
|
||||||
if (AccessibilityUtil.performClick(textViewGroup)) {
|
if (AccessibilityUtil.performClick(textViewGroup)) {
|
||||||
LogUtils.d("进入客户群应用")
|
LogUtils.d("进入客户群应用")
|
||||||
val textView = AccessibilityUtil.findOneByText(getRoot(), "创建一个客户群")
|
val textView = AccessibilityUtil.findOneByText(getRoot(), "创建一个客户群", "创建一个居民群")
|
||||||
AccessibilityUtil.performClick(textView)
|
AccessibilityUtil.performClick(textView)
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
@@ -681,15 +679,18 @@ 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, 2)
|
||||||
val textViewList = AccessibilityUtil.findAllOnceByClazz(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()
|
||||||
for (select in selectList) {
|
for (select in selectList) {
|
||||||
AccessibilityUtil.performClick(multiButton)
|
AccessibilityUtil.performClick(multiButton)
|
||||||
AccessibilityUtil.findTextInput(getRoot(), select)
|
AccessibilityUtil.findTextInput(getRoot(), select)
|
||||||
val selectListView =
|
sleep(Constant.POP_WINDOW_INTERVAL)
|
||||||
|
val selectListViewTemp =
|
||||||
AccessibilityUtil.findOneByClazz(getRoot(), Views.ListView)
|
AccessibilityUtil.findOneByClazz(getRoot(), Views.ListView)
|
||||||
|
val selectListView =
|
||||||
|
AccessibilityUtil.findOnceByClazz(getRoot(), Views.RecyclerView) ?: selectListViewTemp
|
||||||
val imageView =
|
val imageView =
|
||||||
AccessibilityUtil.findOneByClazz(selectListView, Views.ImageView, root = false)
|
AccessibilityUtil.findOneByClazz(selectListView, Views.ImageView, root = false)
|
||||||
AccessibilityUtil.performClick(imageView)
|
AccessibilityUtil.performClick(imageView)
|
||||||
@@ -740,7 +741,7 @@ 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, 2)
|
||||||
val textViewList = AccessibilityUtil.findAllOnceByClazz(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()
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import java.lang.Exception
|
|||||||
* event.source则不需要验证包名获取窗口并可获得事件详情
|
* event.source则不需要验证包名获取窗口并可获得事件详情
|
||||||
*/
|
*/
|
||||||
class WeworkService : AccessibilityService() {
|
class WeworkService : AccessibilityService() {
|
||||||
|
private val TAG = "WeworkService"
|
||||||
lateinit var webSocketManager: WebSocketManager
|
lateinit var webSocketManager: WebSocketManager
|
||||||
|
|
||||||
override fun onServiceConnected() {
|
override fun onServiceConnected() {
|
||||||
@@ -85,10 +86,15 @@ class WeworkService : AccessibilityService() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class EchoWebSocketListener() : WebSocketListener() {
|
class EchoWebSocketListener() : WebSocketListener() {
|
||||||
|
private val TAG = "WeworkService.EchoWebSocketListener"
|
||||||
private lateinit var socket: WebSocket
|
private lateinit var socket: WebSocket
|
||||||
override fun onOpen(webSocket: WebSocket, response: Response) {
|
override fun onOpen(webSocket: WebSocket, response: Response) {
|
||||||
socket = webSocket
|
socket = webSocket
|
||||||
Log.e("ChatMaskActivityListener", "链接建立")
|
Log.e(TAG, "链接建立")
|
||||||
|
val robotId = SPUtils.getInstance().getString(WebConfig.LISTEN_CHANNEL_ID, "")
|
||||||
|
val appVersion = SPUtils.getInstance().getString("appVersion", "")
|
||||||
|
val workVersion= SPUtils.getInstance().getString("workVersion", "")
|
||||||
|
log("链接建立: $robotId appVersion: $appVersion workVersion: $workVersion")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onMessage(webSocket: WebSocket, text: String) {
|
override fun onMessage(webSocket: WebSocket, text: String) {
|
||||||
@@ -104,19 +110,19 @@ class WeworkService : AccessibilityService() {
|
|||||||
override fun onClosed(webSocket: WebSocket, code: Int, reason: String) {
|
override fun onClosed(webSocket: WebSocket, code: Int, reason: String) {
|
||||||
super.onClosed(webSocket, code, reason)
|
super.onClosed(webSocket, code, reason)
|
||||||
//服务器关闭后
|
//服务器关闭后
|
||||||
Log.e("ChatMaskActivityListener", "链接关闭 $reason")
|
Log.e(TAG, "链接关闭 $reason")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onClosing(webSocket: WebSocket, code: Int, reason: String) {
|
override fun onClosing(webSocket: WebSocket, code: Int, reason: String) {
|
||||||
super.onClosing(webSocket, code, reason)
|
super.onClosing(webSocket, code, reason)
|
||||||
socket.close(code, reason)
|
socket.close(code, reason)
|
||||||
Log.e("ChatMaskActivityListener", "服务端关闭连接 $code: $reason")
|
Log.e(TAG, "服务端关闭连接 $code: $reason")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
|
override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
|
||||||
//服务器中断
|
//服务器中断
|
||||||
|
|
||||||
Log.e("ChatMaskActivityListener", "链接错误: " + t.toString() + response.toString())
|
Log.e(TAG, "链接错误: " + t.toString() + response.toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -70,8 +70,8 @@ object AccessibilityUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//寻找第一个文本匹配(关键词)并点击
|
//寻找第一个文本匹配(关键词)并点击
|
||||||
fun findTextAndClick(nodeInfo: AccessibilityNodeInfo?, text: String): Boolean {
|
fun findTextAndClick(nodeInfo: AccessibilityNodeInfo?, vararg textList: String): Boolean {
|
||||||
val textView = findOneByText(nodeInfo, text) ?: return false
|
val textView = findOneByText(nodeInfo, *textList) ?: return false
|
||||||
return performClick(textView)
|
return performClick(textView)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,20 +113,20 @@ object AccessibilityUtil {
|
|||||||
//滚动并按文本寻找第一个控件
|
//滚动并按文本寻找第一个控件
|
||||||
fun scrollAndFindByText(
|
fun scrollAndFindByText(
|
||||||
nodeInfo: AccessibilityNodeInfo,
|
nodeInfo: AccessibilityNodeInfo,
|
||||||
text: String,
|
vararg textList: String,
|
||||||
maxRetry: Int = 3
|
maxRetry: Int = 3
|
||||||
): AccessibilityNodeInfo? {
|
): AccessibilityNodeInfo? {
|
||||||
var index = 0
|
var index = 0
|
||||||
while (index++ < maxRetry) {
|
while (index++ < maxRetry) {
|
||||||
performScrollUp(nodeInfo, 0)
|
performScrollUp(nodeInfo, 0)
|
||||||
val node = findOnceByText(nodeInfo, text)
|
val node = findOnceByText(nodeInfo, *textList)
|
||||||
if (node != null) {
|
if (node != null) {
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while (index++ < maxRetry * 2) {
|
while (index++ < maxRetry * 2) {
|
||||||
performScrollDown(nodeInfo, 0)
|
performScrollDown(nodeInfo, 0)
|
||||||
val node = findOnceByText(nodeInfo, text)
|
val node = findOnceByText(nodeInfo, *textList)
|
||||||
if (node != null) {
|
if (node != null) {
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
@@ -369,12 +369,12 @@ object AccessibilityUtil {
|
|||||||
/**
|
/**
|
||||||
* 按文本(关键词)寻找节点和子节点内的一个匹配项
|
* 按文本(关键词)寻找节点和子节点内的一个匹配项
|
||||||
* @param node 节点
|
* @param node 节点
|
||||||
* @param text 关键词
|
* @param textList 关键词
|
||||||
* @param timeout 检查超时时间
|
* @param timeout 检查超时时间
|
||||||
*/
|
*/
|
||||||
fun findOneByText(
|
fun findOneByText(
|
||||||
node: AccessibilityNodeInfo?,
|
node: AccessibilityNodeInfo?,
|
||||||
text: String,
|
vararg textList: String,
|
||||||
exact: Boolean = false,
|
exact: Boolean = false,
|
||||||
timeout: Long = 5000,
|
timeout: Long = 5000,
|
||||||
root: Boolean = true
|
root: Boolean = true
|
||||||
@@ -383,18 +383,9 @@ object AccessibilityUtil {
|
|||||||
val startTime = System.currentTimeMillis()
|
val startTime = System.currentTimeMillis()
|
||||||
var currentTime = startTime
|
var currentTime = startTime
|
||||||
while (currentTime - startTime <= timeout) {
|
while (currentTime - startTime <= timeout) {
|
||||||
val textViewList = node.findAccessibilityNodeInfosByText(text)
|
val result = findOnceByText(node, *textList, exact = exact)
|
||||||
LogUtils.v("text: $text count: " + textViewList.size)
|
LogUtils.v("text: $textList result == null: ${result == null}")
|
||||||
if (textViewList != null && textViewList.size > 0) {
|
if (result != null) return result
|
||||||
for (textView in textViewList) {
|
|
||||||
if (textView.text == text) {
|
|
||||||
return textView
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!exact) {
|
|
||||||
return textViewList[0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sleep(SHORT_INTERVAL)
|
sleep(SHORT_INTERVAL)
|
||||||
if (root) {
|
if (root) {
|
||||||
node = getRoot(true)
|
node = getRoot(true)
|
||||||
@@ -403,43 +394,53 @@ object AccessibilityUtil {
|
|||||||
}
|
}
|
||||||
currentTime = System.currentTimeMillis()
|
currentTime = System.currentTimeMillis()
|
||||||
}
|
}
|
||||||
Log.e(tag, "findOneByText: not found: $text")
|
Log.e(tag, "findOneByText: not found: $textList")
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun findOnceByText(
|
fun findOnceByText(
|
||||||
node: AccessibilityNodeInfo?,
|
node: AccessibilityNodeInfo?,
|
||||||
text: String,
|
vararg textList: String,
|
||||||
exact: Boolean = false
|
exact: Boolean = false
|
||||||
): AccessibilityNodeInfo? {
|
): AccessibilityNodeInfo? {
|
||||||
return findOneByText(node, text, exact, 0)
|
if (node == null) return null
|
||||||
|
val textNodeList = findAllOnceByText(node, *textList, exact = exact)
|
||||||
|
LogUtils.v("text: $textList count: " + textNodeList.size)
|
||||||
|
if (exact) return textNodeList[0]
|
||||||
|
else if (textNodeList.size > 0) {
|
||||||
|
for (textNode in textNodeList) {
|
||||||
|
for (text in textList) {
|
||||||
|
if (textNode.text == text) {
|
||||||
|
return textNode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return textNodeList[0]
|
||||||
|
}
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 按文本(关键词)寻找节点和子节点内的所有匹配项
|
* 按文本(关键词)寻找节点和子节点内的所有匹配项
|
||||||
* @param node 节点
|
* @param node 节点
|
||||||
* @param text 关键词
|
* @param textList 关键词
|
||||||
* @param timeout 检查超时时间
|
* @param timeout 检查超时时间
|
||||||
*/
|
*/
|
||||||
fun findAllByText(
|
fun findAllByText(
|
||||||
node: AccessibilityNodeInfo?,
|
node: AccessibilityNodeInfo?,
|
||||||
text: String,
|
vararg textList: String,
|
||||||
exact: Boolean = false,
|
exact: Boolean = false,
|
||||||
timeout: Long = 5000,
|
timeout: Long = 5000,
|
||||||
root: Boolean = true
|
root: Boolean = true,
|
||||||
|
minSize: Int = 1
|
||||||
): List<AccessibilityNodeInfo> {
|
): List<AccessibilityNodeInfo> {
|
||||||
var node = node ?: return arrayListOf()
|
var node = node ?: return arrayListOf()
|
||||||
val startTime = System.currentTimeMillis()
|
val startTime = System.currentTimeMillis()
|
||||||
var currentTime = startTime
|
var currentTime = startTime
|
||||||
while (currentTime - startTime <= timeout) {
|
while (currentTime - startTime <= timeout) {
|
||||||
val tvList = node.findAccessibilityNodeInfosByText(text)
|
val result = findAllOnceByText(node, *textList, exact = exact)
|
||||||
if (tvList != null && tvList.size > 0) {
|
LogUtils.v("text: $textList count: " + result.size)
|
||||||
if (!exact) {
|
if (result.size >= minSize) return result
|
||||||
return tvList
|
|
||||||
} else if (tvList.count { it.text == text } > 0) {
|
|
||||||
return tvList.filter { it.text == text }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sleep(SHORT_INTERVAL)
|
sleep(SHORT_INTERVAL)
|
||||||
if (root) {
|
if (root) {
|
||||||
node = getRoot(true)
|
node = getRoot(true)
|
||||||
@@ -448,10 +449,39 @@ object AccessibilityUtil {
|
|||||||
}
|
}
|
||||||
currentTime = System.currentTimeMillis()
|
currentTime = System.currentTimeMillis()
|
||||||
}
|
}
|
||||||
Log.e(tag, "findAllByText: not found: $text")
|
Log.e(tag, "findAllByText: not found: $textList")
|
||||||
return arrayListOf()
|
return arrayListOf()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按文本(关键词)寻找节点和子节点内的所有匹配项
|
||||||
|
* node 节点
|
||||||
|
* clazz 类名
|
||||||
|
* limitDepth 深度 限制深度搜索深度必须匹配提供值且类名相同才返回 不填默认不限制
|
||||||
|
*/
|
||||||
|
fun findAllOnceByText(
|
||||||
|
node: AccessibilityNodeInfo?,
|
||||||
|
vararg textList: String,
|
||||||
|
exact: Boolean = false,
|
||||||
|
list: ArrayList<AccessibilityNodeInfo> = ArrayList()
|
||||||
|
): ArrayList<AccessibilityNodeInfo> {
|
||||||
|
if (node == null) return list
|
||||||
|
val nodeText = node.text
|
||||||
|
if (nodeText != null) {
|
||||||
|
for (text in textList) {
|
||||||
|
if (exact && nodeText == text) {
|
||||||
|
list.add(node)
|
||||||
|
} else if (!exact && nodeText.contains(text)) {
|
||||||
|
list.add(node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (i in 0 until node.childCount) {
|
||||||
|
findAllOnceByText(node.getChild(i), *textList, exact = exact, list = list)
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 按类名寻找节点和子节点内的一个匹配项
|
* 按类名寻找节点和子节点内的一个匹配项
|
||||||
* node 节点
|
* node 节点
|
||||||
@@ -468,10 +498,6 @@ object AccessibilityUtil {
|
|||||||
root: Boolean = true
|
root: Boolean = true
|
||||||
): AccessibilityNodeInfo? {
|
): AccessibilityNodeInfo? {
|
||||||
var node = node ?: return null
|
var node = node ?: return null
|
||||||
if (node.className == clazz) {
|
|
||||||
if (limitDepth == null || limitDepth == depth)
|
|
||||||
return node
|
|
||||||
}
|
|
||||||
val startTime = System.currentTimeMillis()
|
val startTime = System.currentTimeMillis()
|
||||||
var currentTime = startTime
|
var currentTime = startTime
|
||||||
while (currentTime - startTime <= timeout) {
|
while (currentTime - startTime <= timeout) {
|
||||||
@@ -524,12 +550,11 @@ object AccessibilityUtil {
|
|||||||
fun findAllByClazz(
|
fun findAllByClazz(
|
||||||
node: AccessibilityNodeInfo?,
|
node: AccessibilityNodeInfo?,
|
||||||
clazz: String,
|
clazz: String,
|
||||||
list: ArrayList<AccessibilityNodeInfo> = ArrayList(),
|
|
||||||
timeout: Long = 5000,
|
timeout: Long = 5000,
|
||||||
root: Boolean = true,
|
root: Boolean = true,
|
||||||
minSize: Int = 1
|
minSize: Int = 1
|
||||||
): ArrayList<AccessibilityNodeInfo> {
|
): ArrayList<AccessibilityNodeInfo> {
|
||||||
var node = node ?: return list
|
var node = node ?: return arrayListOf()
|
||||||
val startTime = System.currentTimeMillis()
|
val startTime = System.currentTimeMillis()
|
||||||
var currentTime = startTime
|
var currentTime = startTime
|
||||||
while (currentTime - startTime <= timeout) {
|
while (currentTime - startTime <= timeout) {
|
||||||
@@ -546,7 +571,7 @@ object AccessibilityUtil {
|
|||||||
}
|
}
|
||||||
LogUtils.e("findAllByClazz Exception()")
|
LogUtils.e("findAllByClazz Exception()")
|
||||||
Exception().printStackTrace()
|
Exception().printStackTrace()
|
||||||
return list
|
return arrayListOf()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -568,11 +593,23 @@ object AccessibilityUtil {
|
|||||||
return list
|
return list
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找节点的前兄弟节点 直到该节点满足子节点数
|
||||||
|
* node 节点
|
||||||
|
*/
|
||||||
|
fun findFrontNode(node: AccessibilityNodeInfo?, minChildCount: Int = 0): AccessibilityNodeInfo? {
|
||||||
|
var findFrontNode = findFrontNode(node) ?: return null
|
||||||
|
while (findFrontNode.childCount < minChildCount) {
|
||||||
|
findFrontNode = findFrontNode(findFrontNode) ?: return null
|
||||||
|
}
|
||||||
|
return findFrontNode
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查找节点的前兄弟节点
|
* 查找节点的前兄弟节点
|
||||||
* node 节点
|
* node 节点
|
||||||
*/
|
*/
|
||||||
fun findFrontNode(node: AccessibilityNodeInfo?): AccessibilityNodeInfo? {
|
private fun findFrontNode(node: AccessibilityNodeInfo?): AccessibilityNodeInfo? {
|
||||||
if (node == null) return null
|
if (node == null) return null
|
||||||
var parent: AccessibilityNodeInfo? = node.parent
|
var parent: AccessibilityNodeInfo? = node.parent
|
||||||
var son: AccessibilityNodeInfo? = node
|
var son: AccessibilityNodeInfo? = node
|
||||||
@@ -593,11 +630,23 @@ object AccessibilityUtil {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找节点的后兄弟节点 直到该节点满足子节点数
|
||||||
|
* node 节点
|
||||||
|
*/
|
||||||
|
fun findBackNode(node: AccessibilityNodeInfo?, minChildCount: Int = 0): AccessibilityNodeInfo? {
|
||||||
|
var findBackNode = findBackNode(node) ?: return null
|
||||||
|
while (findBackNode.childCount < minChildCount) {
|
||||||
|
findBackNode = findFrontNode(findBackNode) ?: return null
|
||||||
|
}
|
||||||
|
return findBackNode
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查找节点的后兄弟节点
|
* 查找节点的后兄弟节点
|
||||||
* node 节点
|
* node 节点
|
||||||
*/
|
*/
|
||||||
fun findBackNode(node: AccessibilityNodeInfo?): AccessibilityNodeInfo? {
|
private fun findBackNode(node: AccessibilityNodeInfo?): AccessibilityNodeInfo? {
|
||||||
if (node == null) return null
|
if (node == null) return null
|
||||||
var parent: AccessibilityNodeInfo? = node.parent
|
var parent: AccessibilityNodeInfo? = node.parent
|
||||||
var son: AccessibilityNodeInfo? = node
|
var son: AccessibilityNodeInfo? = node
|
||||||
|
|||||||
@@ -16,12 +16,6 @@ import update.UpdateAppUtils
|
|||||||
object UpdateUtil {
|
object UpdateUtil {
|
||||||
|
|
||||||
fun checkUpdate() {
|
fun checkUpdate() {
|
||||||
// val remoteVersionCode = 10
|
|
||||||
// val remoteVersionName = "1.0.1"
|
|
||||||
// val forceUpdate = false
|
|
||||||
// val updateLog = "修复Bug\n优化用户体验"
|
|
||||||
// val downloadUrl = "https://down.qq.com/qqweb/QQ_1/android_apk/Android_8.5.0.5025_537066738.apk"
|
|
||||||
// val fileMD5 = "560017dc94e8f9b65f4ca997c7feb326"
|
|
||||||
OkGo.get<String>(Constant.URL_CHECK_UPDATE)
|
OkGo.get<String>(Constant.URL_CHECK_UPDATE)
|
||||||
.execute(object : StringCallback() {
|
.execute(object : StringCallback() {
|
||||||
override fun onSuccess(response: Response<String>) {
|
override fun onSuccess(response: Response<String>) {
|
||||||
|
|||||||
@@ -21,22 +21,22 @@ object WeworkRoomUtil {
|
|||||||
* 房间类型 ROOM_TYPE
|
* 房间类型 ROOM_TYPE
|
||||||
* @see WeworkMessageBean.ROOM_TYPE
|
* @see WeworkMessageBean.ROOM_TYPE
|
||||||
*/
|
*/
|
||||||
fun getRoomType(root: AccessibilityNodeInfo, print: Boolean = true): Int {
|
fun getRoomType(print: Boolean = true): Int {
|
||||||
|
val roomTitle = getRoomTitle()
|
||||||
when {
|
when {
|
||||||
isExternalSingleChat(root) -> {
|
isExternalSingleChat(roomTitle) -> {
|
||||||
LogUtils.d("ROOM_TYPE: ROOM_TYPE_EXTERNAL_CONTACT")
|
LogUtils.d("ROOM_TYPE: ROOM_TYPE_EXTERNAL_CONTACT")
|
||||||
return WeworkMessageBean.ROOM_TYPE_EXTERNAL_CONTACT
|
return WeworkMessageBean.ROOM_TYPE_EXTERNAL_CONTACT
|
||||||
}
|
}
|
||||||
isGroupChat(root) -> {
|
isExternalGroup() -> {
|
||||||
return if (isExternalGroup(root)) {
|
|
||||||
LogUtils.d("ROOM_TYPE: ROOM_TYPE_EXTERNAL_GROUP")
|
LogUtils.d("ROOM_TYPE: ROOM_TYPE_EXTERNAL_GROUP")
|
||||||
WeworkMessageBean.ROOM_TYPE_EXTERNAL_GROUP
|
return WeworkMessageBean.ROOM_TYPE_EXTERNAL_GROUP
|
||||||
} else {
|
}
|
||||||
|
isGroupChat(roomTitle) -> {
|
||||||
LogUtils.d("ROOM_TYPE: ROOM_TYPE_INTERNAL_GROUP")
|
LogUtils.d("ROOM_TYPE: ROOM_TYPE_INTERNAL_GROUP")
|
||||||
WeworkMessageBean.ROOM_TYPE_INTERNAL_GROUP
|
return WeworkMessageBean.ROOM_TYPE_INTERNAL_GROUP
|
||||||
}
|
}
|
||||||
}
|
isSingleChat() -> {
|
||||||
isSingleChat(root) -> {
|
|
||||||
LogUtils.d("ROOM_TYPE: ROOM_TYPE_INTERNAL_CONTACT")
|
LogUtils.d("ROOM_TYPE: ROOM_TYPE_INTERNAL_CONTACT")
|
||||||
return WeworkMessageBean.ROOM_TYPE_INTERNAL_CONTACT
|
return WeworkMessageBean.ROOM_TYPE_INTERNAL_CONTACT
|
||||||
}
|
}
|
||||||
@@ -55,19 +55,23 @@ object WeworkRoomUtil {
|
|||||||
* @see WeworkMessageBean.ROOM_TYPE_INTERNAL_GROUP
|
* @see WeworkMessageBean.ROOM_TYPE_INTERNAL_GROUP
|
||||||
* @see WeworkMessageBean.ROOM_TYPE_INTERNAL_CONTACT
|
* @see WeworkMessageBean.ROOM_TYPE_INTERNAL_CONTACT
|
||||||
*/
|
*/
|
||||||
fun getRoomTitle(root: AccessibilityNodeInfo): ArrayList<String> {
|
fun getRoomTitle(print: Boolean = true): ArrayList<String> {
|
||||||
val titleList = arrayListOf<String>()
|
val titleList = arrayListOf<String>()
|
||||||
val list = AccessibilityUtil.findOnceByClazz(root, Views.ListView)
|
val list = AccessibilityUtil.findOnceByClazz(getRoot(), Views.ListView)
|
||||||
if (list != null) {
|
if (list != null) {
|
||||||
val frontNode = findFrontNode(list.parent.parent)
|
val frontNode = findFrontNode(list.parent.parent)
|
||||||
val textViewList = findAllOnceByClazz(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(), ""))
|
val text = textView.text.toString()
|
||||||
|
titleList.add(text.replace("\\(\\d+\\)$".toRegex(), ""))
|
||||||
|
if (text.contains("\\(\\d+\\)$".toRegex())) {
|
||||||
|
titleList.add(text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LogUtils.v("getRoomTitle: ", titleList)
|
}
|
||||||
|
if (print) LogUtils.v("getRoomTitle: ", titleList)
|
||||||
return titleList
|
return titleList
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,8 +80,8 @@ object WeworkRoomUtil {
|
|||||||
*/
|
*/
|
||||||
fun intoRoom(title: String): Boolean {
|
fun intoRoom(title: String): Boolean {
|
||||||
LogUtils.d("intoRoom(): $title")
|
LogUtils.d("intoRoom(): $title")
|
||||||
val titleList = getRoomTitle(getRoot())
|
val titleList = getRoomTitle(false)
|
||||||
val roomType = getRoomType(getRoot())
|
val roomType = getRoomType()
|
||||||
if (roomType != WeworkMessageBean.ROOM_TYPE_UNKNOWN
|
if (roomType != WeworkMessageBean.ROOM_TYPE_UNKNOWN
|
||||||
&& titleList.count {
|
&& titleList.count {
|
||||||
it.replace("…", "").replace("\\(.*?\\)".toRegex(), "") == title.replace("…", "")
|
it.replace("…", "").replace("\\(.*?\\)".toRegex(), "") == title.replace("…", "")
|
||||||
@@ -96,7 +100,7 @@ object WeworkRoomUtil {
|
|||||||
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)
|
||||||
AccessibilityUtil.findTextInput(getRoot(), title.replace("…", ""))
|
AccessibilityUtil.findTextInput(getRoot(), title.replace("…", "").replace("-.*$".toRegex(), ""))
|
||||||
sleep(Constant.CHANGE_PAGE_INTERVAL)
|
sleep(Constant.CHANGE_PAGE_INTERVAL)
|
||||||
val selectListView = findOneByClazz(getRoot(), Views.ListView)
|
val selectListView = findOneByClazz(getRoot(), Views.ListView)
|
||||||
val imageView = AccessibilityUtil.findOnceByClazz(selectListView, Views.ImageView)
|
val imageView = AccessibilityUtil.findOnceByClazz(selectListView, Views.ImageView)
|
||||||
@@ -195,35 +199,22 @@ object WeworkRoomUtil {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否是群聊
|
* 是否是群聊
|
||||||
* 群右上角有两个按钮(快速会议按钮、更多按钮)
|
* 群名最后有(\d)显示群人数
|
||||||
*/
|
*/
|
||||||
private fun isGroupChat(root: AccessibilityNodeInfo): Boolean {
|
private fun isGroupChat(roomTitle: ArrayList<String>): Boolean {
|
||||||
val list = AccessibilityUtil.findOnceByClazz(root, Views.ListView)
|
return roomTitle.size > 1 && roomTitle[1].contains("\\(\\d+\\)$".toRegex())
|
||||||
if (list != null) {
|
|
||||||
val frontNode = findFrontNode(list.parent.parent)
|
|
||||||
val textViewList = findAllOnceByClazz(frontNode, Views.TextView)
|
|
||||||
if (textViewList.size >= 2) {
|
|
||||||
val buttonList = findAllOnceByClazz(textViewList.last().parent.parent, Views.TextView)
|
|
||||||
return buttonList.size == 2
|
|
||||||
} else {
|
|
||||||
LogUtils.v("未找到群管理按钮")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
LogUtils.d("未找到消息列表")
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否是外部群
|
* 是否是外部群
|
||||||
* listview前兄弟控件 && text包含外部群
|
* listview前兄弟控件 && text包含外部群
|
||||||
*/
|
*/
|
||||||
private fun isExternalGroup(root: AccessibilityNodeInfo): Boolean {
|
private fun isExternalGroup(): Boolean {
|
||||||
val listView = AccessibilityUtil.findOnceByClazz(root, Views.ListView, null, 0)
|
val listView = AccessibilityUtil.findOnceByClazz(getRoot(), 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 = AccessibilityUtil.findAllByText(frontNode, "外部群", timeout = 0)
|
val nodeList = AccessibilityUtil.findAllOnceByText(frontNode, "外部群")
|
||||||
return nodeList.isNotEmpty()
|
return nodeList.isNotEmpty()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -234,9 +225,9 @@ object WeworkRoomUtil {
|
|||||||
* 是否是单聊
|
* 是否是单聊
|
||||||
* 有列表和输入框
|
* 有列表和输入框
|
||||||
*/
|
*/
|
||||||
private fun isSingleChat(root: AccessibilityNodeInfo): Boolean {
|
private fun isSingleChat(): Boolean {
|
||||||
val list = AccessibilityUtil.findOnceByClazz(root, Views.ListView)
|
val list = AccessibilityUtil.findOnceByClazz(getRoot(), Views.ListView)
|
||||||
val editText = AccessibilityUtil.findOnceByClazz(root, Views.EditText)
|
val editText = AccessibilityUtil.findOnceByClazz(getRoot(), Views.EditText)
|
||||||
if (list != null && editText != null) {
|
if (list != null && editText != null) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -247,8 +238,7 @@ object WeworkRoomUtil {
|
|||||||
* 是否是外部单聊
|
* 是否是外部单聊
|
||||||
* 姓名下面有@xx
|
* 姓名下面有@xx
|
||||||
*/
|
*/
|
||||||
private fun isExternalSingleChat(root: AccessibilityNodeInfo): Boolean {
|
private fun isExternalSingleChat(roomTitle: ArrayList<String>): Boolean {
|
||||||
val roomTitle = getRoomTitle(root)
|
|
||||||
return roomTitle.size > 1 && roomTitle.count { it.matches("^[@@].*?".toRegex()) } > 0
|
return roomTitle.size > 1 && roomTitle.count { it.matches("^[@@].*?".toRegex()) } > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -282,7 +282,7 @@ object WeworkTextUtil {
|
|||||||
fun longClickMessageItem(
|
fun longClickMessageItem(
|
||||||
node: AccessibilityNodeInfo?,
|
node: AccessibilityNodeInfo?,
|
||||||
replyTextType: Int,
|
replyTextType: Int,
|
||||||
replyNick: String,
|
replyNick: String?,
|
||||||
replyContent: String,
|
replyContent: String,
|
||||||
key: String
|
key: String
|
||||||
): Boolean {
|
): Boolean {
|
||||||
@@ -290,14 +290,24 @@ object WeworkTextUtil {
|
|||||||
for (i in 0 until node.childCount) {
|
for (i in 0 until node.childCount) {
|
||||||
val item = node.getChild(node.childCount - 1 - i) ?: continue
|
val item = node.getChild(node.childCount - 1 - i) ?: continue
|
||||||
val nameList = getNameList(item)
|
val nameList = getNameList(item)
|
||||||
for (name in nameList) {
|
if (nameList.isEmpty()) {
|
||||||
if (name == replyNick) {
|
val backNode = getMessageListNode(item, WeworkMessageBean.ROOM_TYPE_INTERNAL_CONTACT)
|
||||||
val backNode = getMessageListNode(item)
|
|
||||||
if (backNode != null) {
|
if (backNode != null) {
|
||||||
val textNode = AccessibilityUtil.findOnceByText(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, WeworkMessageBean.ROOM_TYPE_INTERNAL_CONTACT, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (name in nameList) {
|
||||||
|
if (name == replyNick) {
|
||||||
|
val backNode = getMessageListNode(item, WeworkMessageBean.ROOM_TYPE_INTERNAL_GROUP)
|
||||||
|
if (backNode != null) {
|
||||||
|
val textNode = AccessibilityUtil.findOnceByText(backNode, replyContent)
|
||||||
|
if (textNode != null) {
|
||||||
|
LogUtils.d("nameList: $nameList\nreplyContent: $replyContent")
|
||||||
|
return longClickMessageItem(item, WeworkMessageBean.ROOM_TYPE_INTERNAL_GROUP, key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -306,8 +316,8 @@ object WeworkTextUtil {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun longClickMessageItem(item: AccessibilityNodeInfo, key: String): Boolean {
|
private fun longClickMessageItem(item: AccessibilityNodeInfo, roomType: Int, key: String): Boolean {
|
||||||
val backNode = getMessageListNode(item)
|
val backNode = getMessageListNode(item, roomType)
|
||||||
AccessibilityUtil.performLongClickWithSon(backNode)
|
AccessibilityUtil.performLongClickWithSon(backNode)
|
||||||
val optionRvList = findAllByClazz(getRoot(), Views.RecyclerView)
|
val optionRvList = findAllByClazz(getRoot(), Views.RecyclerView)
|
||||||
for (optionRv in optionRvList) {
|
for (optionRv in optionRvList) {
|
||||||
@@ -327,11 +337,18 @@ object WeworkTextUtil {
|
|||||||
* 适用于左侧发言者
|
* 适用于左侧发言者
|
||||||
* @param item 消息item节点
|
* @param item 消息item节点
|
||||||
*/
|
*/
|
||||||
private fun getMessageListNode(item: AccessibilityNodeInfo): AccessibilityNodeInfo? {
|
private fun getMessageListNode(item: AccessibilityNodeInfo, roomType: Int): AccessibilityNodeInfo? {
|
||||||
|
if (roomType in arrayOf(WeworkMessageBean.ROOM_TYPE_INTERNAL_CONTACT, WeworkMessageBean.ROOM_TYPE_EXTERNAL_CONTACT)) {
|
||||||
|
val node = AccessibilityUtil.findOnceByClazz(item, Views.ImageView)
|
||||||
|
if (node != null) {
|
||||||
|
return AccessibilityUtil.findBackNode(node)
|
||||||
|
}
|
||||||
|
} else if (roomType in arrayOf(WeworkMessageBean.ROOM_TYPE_INTERNAL_GROUP, WeworkMessageBean.ROOM_TYPE_EXTERNAL_GROUP)) {
|
||||||
val node = AccessibilityUtil.findOnceByClazz(item, Views.ViewGroup)
|
val node = AccessibilityUtil.findOnceByClazz(item, Views.ViewGroup)
|
||||||
if (node != null) {
|
if (node != null) {
|
||||||
return AccessibilityUtil.findBackNode(node)
|
return AccessibilityUtil.findBackNode(node)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -150,6 +150,40 @@
|
|||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingStart="@dimen/setting_start_padding"
|
||||||
|
android:paddingTop="@dimen/setting_vertical_padding"
|
||||||
|
android:paddingEnd="@dimen/setting_end_padding"
|
||||||
|
android:paddingBottom="@dimen/setting_vertical_padding">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_marginStart="@dimen/setting_start_padding"
|
||||||
|
android:layout_marginEnd="@dimen/setting_end_padding"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="企业微信版本"
|
||||||
|
android:textColor="@color/color_333333"
|
||||||
|
android:textSize="@dimen/setting_start_font_size" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tv_work_version"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="4.0.2"
|
||||||
|
android:textColor="@color/color_999999"
|
||||||
|
android:textSize="@dimen/setting_end_font_size" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@@ -232,6 +266,41 @@
|
|||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingStart="@dimen/setting_start_padding"
|
||||||
|
android:paddingTop="@dimen/setting_vertical_padding"
|
||||||
|
android:paddingEnd="@dimen/setting_end_start_padding"
|
||||||
|
android:paddingBottom="@dimen/setting_vertical_padding">
|
||||||
|
|
||||||
|
<Switch
|
||||||
|
android:id="@+id/sw_auto_reply"
|
||||||
|
android:layout_width="@dimen/setting_end_font_width"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_marginStart="@dimen/setting_end_start_padding" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_marginStart="@dimen/setting_start_padding"
|
||||||
|
android:layout_toStartOf="@id/sw_auto_reply"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="开启自动回复"
|
||||||
|
android:textColor="@color/color_333333"
|
||||||
|
android:textSize="@dimen/setting_start_font_size" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@@ -273,13 +342,13 @@
|
|||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
|
android:id="@+id/bt_save"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:paddingStart="50dp"
|
|
||||||
android:paddingEnd="50dp"
|
|
||||||
android:layout_gravity="center_horizontal"
|
android:layout_gravity="center_horizontal"
|
||||||
android:background="@drawable/comment_red_btn"
|
android:background="@drawable/comment_red_btn"
|
||||||
android:id="@+id/bt_save"
|
android:paddingStart="50dp"
|
||||||
|
android:paddingEnd="50dp"
|
||||||
android:text="保存" />
|
android:text="保存" />
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user