update 全面兼容企业微信最新版本(4.0.10)和政务微信;控件搜索优化;已知问题修复;

This commit is contained in:
gallonyin
2022-08-11 18:01:16 +08:00
parent 67f6d0baff
commit 732454522f
15 changed files with 305 additions and 149 deletions

View File

@@ -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"
} }

View File

@@ -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"

View File

@@ -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)
} }

View File

@@ -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群反馈~@我可以聊天~"
} }

View File

@@ -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() {

View File

@@ -30,7 +30,7 @@ fun goHomeTab(title: String): Boolean {
var atHome = false var atHome = false
var find = false var find = false
while (!atHome) { while (!atHome) {
val list = 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)

View File

@@ -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

View File

@@ -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)

View File

@@ -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()

View File

@@ -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())
} }
} }
} }

View File

@@ -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

View File

@@ -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>) {

View File

@@ -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
} }

View File

@@ -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
} }
} }

View File

@@ -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="保存" />