Merge branch 'master' of https://github.com/gallonyin/worktool into master
This commit is contained in:
14
README.md
14
README.md
@@ -27,7 +27,7 @@ https://www.apifox.cn/apidoc/project-1035094/doc-840833
|
||||
2. 手机登录企业微信(账号需要提前实名认证,不然很多功能无法正常使用)
|
||||
3. 建议提前给该企业微信账号开通"工作台"-"客户群"权限(如无需外部群创建和管理可不开启)
|
||||
4. 自助申请一个[机器人链接号(点击这里)](https://wt.asrtts.cn/regist.html),您也可以加入QQ群向管理员咨询如何操作。
|
||||
5. 在这台手机上安装[WorkTool APP安装包(点击下载)](https://cdn.asrtts.cn/uploads/worktool/apk/worktool-2.5.4.apk)
|
||||
5. 在这台手机上安装[WorkTool APP安装包(点击下载)](https://cdn.asrtts.cn/uploads/worktool/apk/worktool-2.5.6.apk)
|
||||
6. 打开WorkTool APP,按照APP提示保存链接号,开启主功能,并打开到企业微信界面,不要关闭屏幕即可。
|
||||
|
||||
如果您想使用自己开发的QA回调接口接收机器人收到的所有消息并定制回答,请参考[第三方QA回调接口规范(点击这里)](https://www.apifox.cn/apidoc/project-1035094/doc-861677)开发接口,并在[机器人第三方QA配置(点击这里)](https://www.apifox.cn/apidoc/project-1035094/api-22587884)提交您的机器人id和回调接口地址
|
||||
@@ -59,13 +59,17 @@ Apache License, Version 2.0
|
||||
|
||||
# 版本更新
|
||||
|
||||
tag 2.5.4 2023-1-28 文件发送优化;消息列表识别优化;切换企业;其他已知缺陷修复
|
||||
tag 2.5.6 2023-02-06 兼容主流模拟器;其他已知缺陷修复
|
||||
|
||||
tag 2.5.3 2023-1-11 群模板兼容新版;消息类型识别优化;其他已知缺陷修复
|
||||
tag 2.5.5 2023-02-02 文件发送优化;新消息增强校验;其他已知缺陷修复
|
||||
|
||||
tag 2.5.2 2023-1-05 返回首页缺陷修复
|
||||
tag 2.5.4 2023-01-28 文件发送优化;消息列表识别优化;切换企业;其他已知缺陷修复
|
||||
|
||||
tag 2.5.1 2023-1-04 优化返回首页和回复速度;支持群二维码回调;其他已知缺陷修复
|
||||
tag 2.5.3 2023-01-11 群模板兼容新版;消息类型识别优化;其他已知缺陷修复
|
||||
|
||||
tag 2.5.2 2023-01-05 返回首页缺陷修复
|
||||
|
||||
tag 2.5.1 2023-01-04 优化返回首页和回复速度;支持群二维码回调;其他已知缺陷修复
|
||||
|
||||
tag 2.4.2 2022-12-14 优化at;优化通过好友请求;其他已知缺陷修复
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@ android {
|
||||
applicationId "org.yameida.worktool"
|
||||
minSdkVersion 24
|
||||
targetSdkVersion 30
|
||||
versionCode 2540
|
||||
versionName "2.5.4"
|
||||
versionCode 2561
|
||||
versionName "2.5.6"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
@@ -4,9 +4,10 @@ import com.blankj.utilcode.util.SPUtils
|
||||
|
||||
object Constant {
|
||||
|
||||
val AVAILABLE_VERSION = arrayListOf("4.0.2", "4.0.6", "4.0.8", "4.0.10", "4.0.12", "4.0.16", "4.0.18", "4.0.19", "4.0.20")
|
||||
val AVAILABLE_VERSION = arrayListOf("4.0.2", "4.0.6", "4.0.8", "4.0.10", "4.0.12", "4.0.16", "4.0.18", "4.0.19", "4.0.20", "4.1.0")
|
||||
const val PACKAGE_NAMES = "com.tencent.wework"
|
||||
const val WEWORK_NOTIFY = "wework_notify"
|
||||
const val LONG_INTERVAL = 5000L
|
||||
const val CHANGE_PAGE_INTERVAL = 1000L
|
||||
const val POP_WINDOW_INTERVAL = 500L
|
||||
private const val DEFAULT_HOST = "wss://worktool.asrtts.cn"
|
||||
@@ -21,6 +22,8 @@ object Constant {
|
||||
var autoReply = SPUtils.getInstance().getInt("autoReply", 1)
|
||||
var groupStrict = false
|
||||
var friendRemarkStrict = false
|
||||
var pushImage = false
|
||||
var autoPublishComment = false
|
||||
var robotId: String
|
||||
get() = SPUtils.getInstance().getString("robotId", SPUtils.getInstance().getString("LISTEN_CHANNEL_ID", ""))
|
||||
set(value) {
|
||||
|
||||
@@ -10,9 +10,11 @@ import com.umeng.analytics.MobclickAgent
|
||||
import kotlinx.android.synthetic.main.activity_listen.*
|
||||
import org.yameida.worktool.*
|
||||
import android.content.*
|
||||
import android.os.FileObserver
|
||||
import android.text.InputType
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.qmuiteam.qmui.widget.dialog.QMUIDialog
|
||||
import org.yameida.worktool.observer.MultiFileObserver
|
||||
import org.yameida.worktool.utils.*
|
||||
import org.yameida.worktool.utils.envcheck.CheckHook
|
||||
import org.yameida.worktool.utils.envcheck.CheckRoot
|
||||
@@ -20,6 +22,8 @@ import org.yameida.worktool.utils.envcheck.CheckRoot
|
||||
|
||||
class ListenActivity : AppCompatActivity() {
|
||||
|
||||
private var mFileObserver: FileObserver? = null
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* @param type 0=游客登录
|
||||
@@ -42,6 +46,7 @@ class ListenActivity : AppCompatActivity() {
|
||||
initView()
|
||||
initAccessibility()
|
||||
initOverlays()
|
||||
initObserver()
|
||||
initData()
|
||||
PermissionUtils.permission("android.permission.READ_EXTERNAL_STORAGE").request()
|
||||
registerReceiver(openWsReceiver, IntentFilter(Constant.WEWORK_NOTIFY))
|
||||
@@ -155,6 +160,14 @@ class ListenActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun initObserver() {
|
||||
if (mFileObserver == null) {
|
||||
mFileObserver =
|
||||
MultiFileObserver("/storage/emulated/0/Android/data/com.tencent.wework/files/imagecache/imagemsg2");
|
||||
mFileObserver?.startWatching()
|
||||
}
|
||||
}
|
||||
|
||||
private fun initData() {
|
||||
HttpUtil.checkUpdate()
|
||||
HttpUtil.getMyConfig(toast = false)
|
||||
|
||||
@@ -174,6 +174,8 @@ public class WeworkMessageBean {
|
||||
public String extraText;
|
||||
//接收消息类型
|
||||
public Integer textType;
|
||||
//最大尝试次数
|
||||
public Integer maxRetryCount;
|
||||
|
||||
//群名
|
||||
public String groupName;
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
package org.yameida.worktool.observer;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Stack;
|
||||
|
||||
import android.os.FileObserver;
|
||||
import android.util.Log;
|
||||
|
||||
public class MultiFileObserver extends FileObserver {
|
||||
|
||||
public HashMap<String, Long> map = new HashMap<>();
|
||||
public static String lastPicPath = "";
|
||||
public static Long lastPicCreateTime = 0L;
|
||||
|
||||
/** Only modification events */
|
||||
public static int CHANGES_ONLY = CREATE | MODIFY | DELETE | CLOSE_WRITE
|
||||
| DELETE_SELF | MOVE_SELF | MOVED_FROM | MOVED_TO;
|
||||
|
||||
private List<SingleFileObserver> mObservers;
|
||||
private String mPath;
|
||||
private int mMask;
|
||||
|
||||
public MultiFileObserver(String path) {
|
||||
this(path, ALL_EVENTS);
|
||||
}
|
||||
|
||||
public MultiFileObserver(String path, int mask) {
|
||||
super(path, mask);
|
||||
mPath = path;
|
||||
mMask = mask;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startWatching() {
|
||||
if (mObservers != null)
|
||||
return;
|
||||
|
||||
mObservers = new ArrayList<SingleFileObserver>();
|
||||
Stack<String> stack = new Stack<String>();
|
||||
stack.push(mPath);
|
||||
|
||||
while (!stack.isEmpty()) {
|
||||
String parent = stack.pop();
|
||||
mObservers.add(new SingleFileObserver(parent, mMask));
|
||||
File path = new File(parent);
|
||||
File[] files = path.listFiles();
|
||||
if (null == files)
|
||||
continue;
|
||||
for (File f : files) {
|
||||
if (f.isDirectory() && !f.getName().equals(".")
|
||||
&& !f.getName().equals("..")) {
|
||||
stack.push(f.getPath());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < mObservers.size(); i++) {
|
||||
SingleFileObserver sfo = mObservers.get(i);
|
||||
sfo.startWatching();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopWatching() {
|
||||
if (mObservers == null)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < mObservers.size(); i++) {
|
||||
SingleFileObserver sfo = mObservers.get(i);
|
||||
sfo.stopWatching();
|
||||
}
|
||||
|
||||
mObservers.clear();
|
||||
mObservers = null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onEvent(int event, String path) {
|
||||
switch (event) {
|
||||
case FileObserver.ACCESS:
|
||||
case FileObserver.CLOSE_NOWRITE:
|
||||
if (path.endsWith(".0") && !map.containsKey(path)) {
|
||||
Log.i("RecursiveFileObserver", "发现新图片: " + path);
|
||||
map.put(path, System.currentTimeMillis());
|
||||
lastPicPath = path;
|
||||
}
|
||||
break;
|
||||
case FileObserver.CREATE:
|
||||
Log.i("RecursiveFileObserver", "CREATE: " + path);
|
||||
lastPicCreateTime = System.currentTimeMillis();
|
||||
break;
|
||||
case FileObserver.ATTRIB:
|
||||
// Log.i("RecursiveFileObserver", "ATTRIB: " + path);
|
||||
break;
|
||||
case FileObserver.CLOSE_WRITE:
|
||||
// Log.i("RecursiveFileObserver", "CLOSE_WRITE: " + path);
|
||||
break;
|
||||
case FileObserver.DELETE:
|
||||
// Log.i("RecursiveFileObserver", "DELETE: " + path);
|
||||
break;
|
||||
case FileObserver.DELETE_SELF:
|
||||
// Log.i("RecursiveFileObserver", "DELETE_SELF: " + path);
|
||||
break;
|
||||
case FileObserver.MODIFY:
|
||||
// Log.i("RecursiveFileObserver", "MODIFY: " + path);
|
||||
break;
|
||||
case FileObserver.MOVE_SELF:
|
||||
// Log.i("RecursiveFileObserver", "MOVE_SELF: " + path);
|
||||
break;
|
||||
case FileObserver.MOVED_FROM:
|
||||
// Log.i("RecursiveFileObserver", "MOVED_FROM: " + path);
|
||||
break;
|
||||
case FileObserver.MOVED_TO:
|
||||
// Log.i("RecursiveFileObserver", "MOVED_TO: " + path);
|
||||
break;
|
||||
case FileObserver.OPEN:
|
||||
// Log.i("RecursiveFileObserver", "OPEN: " + path);
|
||||
break;
|
||||
default:
|
||||
// Log.i("RecursiveFileObserver", "DEFAULT(" + event + " : " + path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Monitor single directory and dispatch all events to its parent, with full
|
||||
* path.
|
||||
*/
|
||||
class SingleFileObserver extends FileObserver {
|
||||
String mPath;
|
||||
|
||||
public SingleFileObserver(String path) {
|
||||
this(path, ALL_EVENTS);
|
||||
mPath = path;
|
||||
}
|
||||
|
||||
public SingleFileObserver(String path, int mask) {
|
||||
super(path, mask);
|
||||
mPath = path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(int event, String path) {
|
||||
String newPath = mPath + "/" + path;
|
||||
MultiFileObserver .this.onEvent(event, newPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -135,6 +135,7 @@ fun getRoot(ignoreCheck: Boolean): AccessibilityNodeInfo {
|
||||
* 后退
|
||||
*/
|
||||
fun backPress() {
|
||||
val clazz = WeworkController.weworkService.currentClass
|
||||
val textView = AccessibilityUtil.findOnceByClazz(getRoot(), Views.TextView)
|
||||
if (textView != null && textView.text.isNullOrBlank() && AccessibilityUtil.performClick(textView, retry = false)) {
|
||||
LogUtils.v("找到回退按钮")
|
||||
@@ -164,7 +165,8 @@ fun backPress() {
|
||||
} else {
|
||||
LogUtils.d("未找到对话框 点击bar中心")
|
||||
AccessibilityUtil.performXYClick(WeworkController.weworkService, ScreenUtils.getScreenWidth() / 2F, BarUtils.getStatusBarHeight() * 2F)
|
||||
sleep(Constant.POP_WINDOW_INTERVAL)
|
||||
sleep(Constant.CHANGE_PAGE_INTERVAL * 2)
|
||||
if (WeworkController.weworkService.currentClass == clazz) {
|
||||
val firstEmptyTextView = AccessibilityUtil.findAllByClazz(getRoot(), Views.TextView).firstOrNull { it.text.isNullOrEmpty() }
|
||||
if (firstEmptyTextView != null && firstEmptyTextView.isClickable) {
|
||||
AccessibilityUtil.performClick(firstEmptyTextView)
|
||||
@@ -174,6 +176,7 @@ fun backPress() {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sleep(Constant.POP_WINDOW_INTERVAL)
|
||||
}
|
||||
|
||||
|
||||
@@ -51,6 +51,8 @@ object MyLooper {
|
||||
fun init() {
|
||||
LogUtils.i("init myLooper...")
|
||||
SPUtils.getInstance("noTipMessage").clear()
|
||||
SPUtils.getInstance("lastSyncMessage").clear()
|
||||
SPUtils.getInstance("noSyncMessage").clear()
|
||||
SPUtils.getInstance("limit").clear()
|
||||
}
|
||||
|
||||
|
||||
@@ -334,7 +334,8 @@ object WeworkController {
|
||||
message.fileUrl,
|
||||
message.fileBase64,
|
||||
message.fileType,
|
||||
message.extraText
|
||||
message.extraText,
|
||||
message.maxRetryCount
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -2,15 +2,19 @@ package org.yameida.worktool.service
|
||||
|
||||
import android.view.accessibility.AccessibilityNodeInfo
|
||||
import androidx.core.text.isDigitsOnly
|
||||
import com.blankj.utilcode.util.LogUtils
|
||||
import com.blankj.utilcode.util.SPUtils
|
||||
import com.blankj.utilcode.util.*
|
||||
import org.yameida.worktool.Constant
|
||||
import org.yameida.worktool.Demo
|
||||
import org.yameida.worktool.model.WeworkMessageBean
|
||||
import org.yameida.worktool.observer.MultiFileObserver
|
||||
import org.yameida.worktool.service.WeworkController.mainLoopRunning
|
||||
import org.yameida.worktool.utils.*
|
||||
import java.io.File
|
||||
import java.lang.Exception
|
||||
import java.lang.StringBuilder
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
/**
|
||||
* 获取数据类型 201 202 主循环
|
||||
@@ -26,7 +30,7 @@ object WeworkLoopImpl {
|
||||
mainLoopRunning = true
|
||||
try {
|
||||
while (mainLoopRunning) {
|
||||
if (!isAtHome() && WeworkRoomUtil.getRoomType(false) != WeworkMessageBean.ROOM_TYPE_UNKNOWN) {
|
||||
if (!isAtHome()) {
|
||||
LogUtils.d("当前在房间: ")
|
||||
getChatMessageList()
|
||||
if (mainLoopRunning) {
|
||||
@@ -112,7 +116,7 @@ object WeworkLoopImpl {
|
||||
if (titleList.contains("对方正在输入…")) {
|
||||
titleList = WeworkRoomUtil.getFriendName()
|
||||
}
|
||||
if (titleList.size > 0) {
|
||||
if (roomType != WeworkMessageBean.ROOM_TYPE_UNKNOWN && titleList.size > 0) {
|
||||
val title = titleList.joinToString()
|
||||
LogUtils.v("聊天: $title")
|
||||
log("聊天: $title")
|
||||
@@ -128,7 +132,7 @@ object WeworkLoopImpl {
|
||||
for (i in 0 until list.childCount) {
|
||||
val item = list.getChild(i)
|
||||
if (item != null && item.childCount > 0) {
|
||||
messageList.add(parseChatMessageItem(item, roomType))
|
||||
messageList.add(parseChatMessageItem(item, titleList, roomType))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -140,7 +144,7 @@ object WeworkLoopImpl {
|
||||
for (i in 0 until list2.childCount) {
|
||||
val item = list2.getChild(i)
|
||||
if (item != null && item.childCount > 0) {
|
||||
messageList2.add(parseChatMessageItem(item, roomType))
|
||||
messageList2.add(parseChatMessageItem(item, titleList, roomType))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -159,6 +163,7 @@ object WeworkLoopImpl {
|
||||
null
|
||||
)
|
||||
)
|
||||
SPUtils.getInstance("lastSyncMessage").put(title, messageList.last().itemMessageList.lastOrNull()?.text)
|
||||
//推测是否回复并在房间等待指令
|
||||
if (needInfer) {
|
||||
val lastMessage = messageList.lastOrNull()
|
||||
@@ -203,6 +208,20 @@ object WeworkLoopImpl {
|
||||
LogUtils.e("未找到聊天消息列表")
|
||||
error("未找到聊天消息列表")
|
||||
}
|
||||
} else if (Constant.autoPublishComment && WeworkController.weworkService.currentClass == "com.tencent.wework.moments.controller.MomentsIndexListActivity") {
|
||||
LogUtils.d("自动发表朋友圈")
|
||||
val tvGoPublish = AccessibilityUtil.findOneByText(getRoot(), "去发表", exact = true)
|
||||
if (tvGoPublish != null) {
|
||||
AccessibilityUtil.performClick(tvGoPublish)
|
||||
AccessibilityExtraUtil.loadingPage("MomentsEnterpriseNotificationListActivity")
|
||||
val tvPublishList = AccessibilityUtil.findAllByText(getRoot(), "发表", exact = true)
|
||||
LogUtils.d("发现${tvPublishList.size}条待发送")
|
||||
for (tvPublish in tvPublishList) {
|
||||
AccessibilityUtil.performClick(tvPublish)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LogUtils.v("退出非聊天房间 ${WeworkController.weworkService.currentClass}")
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -280,8 +299,9 @@ object WeworkLoopImpl {
|
||||
val listview = AccessibilityUtil.findOneByClazz(getRoot(), Views.RecyclerView, Views.ListView, Views.ViewGroup)
|
||||
if (listview != null && listview.childCount >= 2) {
|
||||
if (hasNewMessage != null) {
|
||||
//发现新消息
|
||||
if (checkUnreadChatRoom(listview)) {
|
||||
//如果有红点 点击进入聊天页
|
||||
//如果房间有红点 点击进入聊天页
|
||||
return true
|
||||
} else {
|
||||
AccessibilityUtil.clickByNode(WeworkController.weworkService, hasNewMessage)
|
||||
@@ -290,14 +310,18 @@ object WeworkLoopImpl {
|
||||
sleep(Constant.POP_WINDOW_INTERVAL / 5)
|
||||
//双击消息再试一次
|
||||
if (checkUnreadChatRoom(listview)) {
|
||||
//如果有红点 点击进入聊天页
|
||||
//如果房间有红点 点击进入聊天页
|
||||
return true
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//未发现新消息
|
||||
if (checkNoTipMessage(listview) == 1) {
|
||||
//如果发现拉入群聊/修改群名/移出群聊 点击进入聊天页
|
||||
return true
|
||||
} else if (checkNoSyncMessage(listview) == 1) {
|
||||
//消息不一致
|
||||
return true
|
||||
} else {
|
||||
LogUtils.v("未发现新消息或无提示消息")
|
||||
}
|
||||
@@ -336,6 +360,9 @@ object WeworkLoopImpl {
|
||||
if (checkNoTipMessage(listview) != 0) {
|
||||
return true
|
||||
}
|
||||
if (checkNoSyncMessage(listview) != 0) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
})
|
||||
@@ -388,14 +415,14 @@ object WeworkLoopImpl {
|
||||
val listBriefList = arrayListOf<List<CharSequence>>()
|
||||
for (i in 0 until list.childCount) {
|
||||
val item = list.getChild(i)
|
||||
val tvList = AccessibilityUtil.findAllOnceByClazz(item, Views.TextView).mapNotNull { it.text }
|
||||
val tvList = AccessibilityUtil.findAllOnceByClazz(item, Views.TextView).mapNotNull { it.text?.toString() }
|
||||
listBriefList.add(tvList)
|
||||
//tvList title/time/content
|
||||
if (tvList.size == 3) {
|
||||
//只查看最近一周内的消息
|
||||
if (tvList[1].isBlank() || tvList[1].contains("(刚刚)|(分钟前)|(上午)|(下午)|(昨天)|(星期)|(日程)|(会议)".toRegex())) {
|
||||
if (tvList[2].contains("(移出了群聊)|(邀请你加入了)|(修改群名为)|(此群为外部群)|(加入了外部群)".toRegex())) {
|
||||
val interval = System.currentTimeMillis() / 1000 - SPUtils.getInstance("noTipMessage").getLong(tvList[0].toString(), 0)
|
||||
val interval = System.currentTimeMillis() / 1000 - SPUtils.getInstance("noTipMessage").getLong(tvList[0], 0)
|
||||
if (interval > 3600) {
|
||||
LogUtils.i("发现无提示消息: $tvList")
|
||||
log("发现无提示消息: $tvList")
|
||||
@@ -404,7 +431,7 @@ object WeworkLoopImpl {
|
||||
} else {
|
||||
AccessibilityUtil.clickByNode(WeworkController.weworkService, item)
|
||||
}
|
||||
SPUtils.getInstance("noTipMessage").put(tvList[0].toString(), System.currentTimeMillis() / 1000)
|
||||
SPUtils.getInstance("noTipMessage").put(tvList[0], System.currentTimeMillis() / 1000)
|
||||
return 1
|
||||
} else {
|
||||
LogUtils.v("发现无提示消息: $tvList 消息在 $interval 秒前已被查看")
|
||||
@@ -418,11 +445,54 @@ object WeworkLoopImpl {
|
||||
return 0
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查首页-聊天列表是否有不一致消息
|
||||
* @return -1当前列表不存在一周内消息 0未发现不一致消息 1发现不一致消息
|
||||
*/
|
||||
private fun checkNoSyncMessage(list: AccessibilityNodeInfo): Int {
|
||||
list.refresh()
|
||||
val listBriefList = arrayListOf<List<CharSequence>>()
|
||||
for (i in 0 until list.childCount) {
|
||||
val item = list.getChild(i)
|
||||
val tvList = AccessibilityUtil.findAllOnceByClazz(item, Views.TextView).mapNotNull { it.text?.toString() }
|
||||
listBriefList.add(tvList)
|
||||
//tvList title/time/content
|
||||
if (tvList.size == 3) {
|
||||
//只查看最近一周内的消息
|
||||
val title = tvList[0]
|
||||
if (tvList[1].isBlank() || tvList[1].contains("(刚刚)|(分钟前)|(上午)|(下午)|(昨天)|(星期)|(日程)|(会议)".toRegex())) {
|
||||
val lastSyncMessage = SPUtils.getInstance("lastSyncMessage").getString(title, null)
|
||||
?: continue
|
||||
if (tvList[2].contains(lastSyncMessage.replace("\n", " "))) {
|
||||
continue
|
||||
}
|
||||
if (SPUtils.getInstance("noSyncMessage").getString(title) != lastSyncMessage) {
|
||||
LogUtils.e("发现不一致消息: $tvList")
|
||||
error("发现不一致消息: $tvList $lastSyncMessage")
|
||||
SPUtils.getInstance("noSyncMessage").put(title, lastSyncMessage)
|
||||
if (AccessibilityUtil.performClick(item)) {
|
||||
//进入聊天页 下一步 getChatMessageList
|
||||
} else {
|
||||
AccessibilityUtil.clickByNode(WeworkController.weworkService, item)
|
||||
}
|
||||
return 1
|
||||
} else {
|
||||
LogUtils.v("消息多次不一致: $tvList")
|
||||
}
|
||||
} else {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析消息列表里的一条消息
|
||||
*/
|
||||
private fun parseChatMessageItem(
|
||||
node: AccessibilityNodeInfo,
|
||||
titleList: ArrayList<String>,
|
||||
roomType: Int
|
||||
): WeworkMessageBean.SubMessageBean {
|
||||
val message: WeworkMessageBean.SubMessageBean
|
||||
@@ -466,6 +536,52 @@ object WeworkLoopImpl {
|
||||
}
|
||||
}
|
||||
message = WeworkMessageBean.SubMessageBean(0, textType, itemMessageList, nameList)
|
||||
//图片类型特殊处理
|
||||
if (Constant.pushImage && textType == 2) {
|
||||
val lastPicCreateTime = MultiFileObserver.lastPicCreateTime
|
||||
val lastPicPath = MultiFileObserver.lastPicPath
|
||||
LogUtils.d("发现图片类型应该点击")
|
||||
AccessibilityUtil.performClickWithSon(relativeLayoutContent)
|
||||
AccessibilityExtraUtil.loadingPage("com.tencent.wework.msg.controller.ShowImageController", Constant.CHANGE_PAGE_INTERVAL)
|
||||
LogUtils.d("发现图片类型 查看图片检查有无新图片产生")
|
||||
if (MultiFileObserver.lastPicCreateTime > lastPicCreateTime) {
|
||||
LogUtils.d("正在下载图片...")
|
||||
var downloading = true
|
||||
val startTime = System.currentTimeMillis()
|
||||
var currentTime = startTime
|
||||
while (currentTime - startTime < Constant.LONG_INTERVAL) {
|
||||
if (!lastPicPath.equals(MultiFileObserver.lastPicPath)) {
|
||||
LogUtils.d("下载图片完成")
|
||||
downloading = false
|
||||
try {
|
||||
val df = SimpleDateFormat("MMdd_HHmmss")
|
||||
val targetPath = "${
|
||||
Utils.getApp().getExternalFilesDir("copy")
|
||||
}/${df.format(Date())}/${File(MultiFileObserver.lastPicPath).name}.png"
|
||||
if (FileUtils.copy(MultiFileObserver.lastPicPath, targetPath)) {
|
||||
LogUtils.d("复制图片完成: $targetPath")
|
||||
} else {
|
||||
LogUtils.e("复制图片失败 请检查权限: $targetPath")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
LogUtils.e("接收图片出错", e)
|
||||
}
|
||||
break
|
||||
}
|
||||
sleep(Constant.POP_WINDOW_INTERVAL / 5)
|
||||
currentTime = System.currentTimeMillis()
|
||||
}
|
||||
if (downloading) {
|
||||
LogUtils.e("下载图片失败")
|
||||
}
|
||||
} else {
|
||||
LogUtils.d("该图片已下载 忽略")
|
||||
}
|
||||
while (WeworkController.weworkService.currentClass == "com.tencent.wework.msg.controller.ShowImageController") {
|
||||
AccessibilityUtil.performXYClick(WeworkController.weworkService, ScreenUtils.getScreenWidth() / 2F, BarUtils.getStatusBarHeight() * 2F)
|
||||
sleep(Constant.POP_WINDOW_INTERVAL)
|
||||
}
|
||||
}
|
||||
} else if (Views.ImageView.equals(relativeLayoutItem.getChild(1).className)) {
|
||||
LogUtils.v("头像在右边 本条消息发送者为自己")
|
||||
var textType = WeworkMessageBean.TEXT_TYPE_UNKNOWN
|
||||
|
||||
@@ -549,8 +549,10 @@ object WeworkOperationImpl {
|
||||
fileUrl: String?,
|
||||
fileBase64: String?,
|
||||
fileType: String,
|
||||
extraText: String? = null
|
||||
extraText: String? = null,
|
||||
maxRetryCount: Int? = null
|
||||
): Boolean {
|
||||
val retryCount = maxRetryCount ?: 1
|
||||
val startTime = System.currentTimeMillis()
|
||||
if (!PermissionUtils.isGrantedDrawOverlays()) {
|
||||
LogUtils.e("未打开悬浮窗权限")
|
||||
@@ -601,6 +603,9 @@ object WeworkOperationImpl {
|
||||
return true
|
||||
} else {
|
||||
LogUtils.e("文件转发失败: $objectName")
|
||||
if (retryCount > 0) {
|
||||
return pushFile(message, titleList, objectName, fileUrl, fileBase64, fileType, extraText, retryCount - 1)
|
||||
}
|
||||
uploadCommandResult(
|
||||
message,
|
||||
ExecCallbackBean.ERROR_RELAY,
|
||||
@@ -611,6 +616,9 @@ object WeworkOperationImpl {
|
||||
}
|
||||
} else {
|
||||
LogUtils.e("文件存储本地失败 $filePath")
|
||||
if (retryCount > 0) {
|
||||
return pushFile(message, titleList, objectName, fileUrl, fileBase64, fileType, extraText, retryCount - 1)
|
||||
}
|
||||
uploadCommandResult(
|
||||
message,
|
||||
ExecCallbackBean.ERROR_FILE_STORAGE,
|
||||
@@ -621,6 +629,9 @@ object WeworkOperationImpl {
|
||||
}
|
||||
} else {
|
||||
LogUtils.e("文件下载失败")
|
||||
if (retryCount > 0) {
|
||||
return pushFile(message, titleList, objectName, fileUrl, fileBase64, fileType, extraText, retryCount - 1)
|
||||
}
|
||||
uploadCommandResult(
|
||||
message,
|
||||
ExecCallbackBean.ERROR_FILE_DOWNLOAD,
|
||||
@@ -668,6 +679,9 @@ object WeworkOperationImpl {
|
||||
return true
|
||||
} else {
|
||||
LogUtils.e("文件转发失败: $objectName")
|
||||
if (retryCount > 0) {
|
||||
return pushFile(message, titleList, objectName, fileUrl, fileBase64, fileType, extraText, retryCount - 1)
|
||||
}
|
||||
uploadCommandResult(
|
||||
message,
|
||||
ExecCallbackBean.ERROR_RELAY,
|
||||
@@ -678,6 +692,9 @@ object WeworkOperationImpl {
|
||||
}
|
||||
} else {
|
||||
LogUtils.e("文件存储本地失败 $filePath")
|
||||
if (retryCount > 0) {
|
||||
return pushFile(message, titleList, objectName, fileUrl, fileBase64, fileType, extraText, retryCount - 1)
|
||||
}
|
||||
uploadCommandResult(
|
||||
message,
|
||||
ExecCallbackBean.ERROR_FILE_STORAGE,
|
||||
@@ -687,6 +704,10 @@ object WeworkOperationImpl {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
LogUtils.e("未找到文件资源参数")
|
||||
if (retryCount > 0) {
|
||||
return pushFile(message, titleList, objectName, fileUrl, fileBase64, fileType, extraText, retryCount - 1)
|
||||
}
|
||||
uploadCommandResult(
|
||||
message,
|
||||
ExecCallbackBean.ERROR_ILLEGAL_DATA,
|
||||
@@ -1059,7 +1080,9 @@ object WeworkOperationImpl {
|
||||
if (tvCorp != null) {
|
||||
LogUtils.d("找到目标企业: $objectName")
|
||||
AccessibilityUtil.performClick(tvCorp)
|
||||
uploadCommandResult(message, ExecCallbackBean.SUCCESS, "切换企业成功: $objectName", startTime)
|
||||
goHome()
|
||||
WeworkGetImpl.getMyInfo(message)
|
||||
return true
|
||||
} else {
|
||||
LogUtils.e("未找到目标企业: $objectName")
|
||||
|
||||
@@ -5,6 +5,7 @@ import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.provider.Settings
|
||||
import org.yameida.worktool.utils.envcheck.CheckRoot
|
||||
import java.lang.reflect.Method
|
||||
|
||||
object FlowPermissionHelper {
|
||||
@@ -26,6 +27,10 @@ object FlowPermissionHelper {
|
||||
}
|
||||
|
||||
fun canBackgroundStart(context: Context): Boolean {
|
||||
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1 || CheckRoot.isDeviceRooted()) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (isXiaoMi()) {
|
||||
return isXiaomiBgStartPermissionAllowed(context)
|
||||
}
|
||||
|
||||
@@ -13,9 +13,13 @@ import org.yameida.worktool.R
|
||||
import org.yameida.worktool.model.network.CheckUpdateResult
|
||||
import org.yameida.worktool.model.network.GetMyConfigResult
|
||||
import update.UpdateAppUtils
|
||||
import java.io.File
|
||||
|
||||
object HttpUtil {
|
||||
|
||||
/**
|
||||
* 检查更新
|
||||
*/
|
||||
fun checkUpdate() {
|
||||
OkGo.get<String>(Constant.getCheckUpdateUrl())
|
||||
.execute(object : StringCallback() {
|
||||
@@ -63,6 +67,9 @@ object HttpUtil {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取机器人配置
|
||||
*/
|
||||
fun getMyConfig(toast: Boolean = true) {
|
||||
if (Constant.robotId.isBlank()) {
|
||||
if (toast) {
|
||||
@@ -101,4 +108,24 @@ object HttpUtil {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 推送图片
|
||||
*/
|
||||
fun pushImage(url: String, groupName: String, receivedName: String?, imagePath: String) {
|
||||
OkGo.post<String>(url)
|
||||
.params("groupName", groupName)
|
||||
.params("receivedName", receivedName ?: "")
|
||||
.params("image", File(imagePath))
|
||||
.execute(object : StringCallback() {
|
||||
override fun onSuccess(response: Response<String>) {
|
||||
LogUtils.d("推送图片成功: $groupName $receivedName $imagePath")
|
||||
}
|
||||
|
||||
override fun onError(response: Response<String>) {
|
||||
ToastUtils.showLong("推送图片失败")
|
||||
LogUtils.e("推送图片失败")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package org.yameida.worktool.utils;
|
||||
|
||||
import com.blankj.utilcode.util.LogUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class ImageDepthSizeUtil {
|
||||
|
||||
public static boolean checkRawImage(String path) {
|
||||
try {
|
||||
File file = new File(path);
|
||||
FileInputStream fileInputStream = new FileInputStream(file);
|
||||
byte[] b = new byte[1000000];
|
||||
fileInputStream.read(b);
|
||||
int bitsPerPixel = (b[25] & 0xff) == 2 || (b[25] & 0xff) == 6 ? (b[24] & 0xff) * 3 : b[24] & 0xff;
|
||||
LogUtils.v("path: " + path, "bitsPerPixel: " + bitsPerPixel);
|
||||
return bitsPerPixel == 24;
|
||||
} catch (IOException e) {
|
||||
LogUtils.e("ImageDepthSize Check Error", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -35,6 +35,7 @@ public class PermissionPageManagement {
|
||||
* @param activity
|
||||
*/
|
||||
public static void goToSetting(Activity activity){
|
||||
try {
|
||||
switch (Build.MANUFACTURER) {
|
||||
case MANUFACTURER_HUAWEI:
|
||||
Huawei(activity);
|
||||
@@ -62,6 +63,10 @@ public class PermissionPageManagement {
|
||||
Log.e("goToSetting", "目前暂不支持此系统");
|
||||
break;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e("goToSetting", "Error 目前暂不支持此系统");
|
||||
ApplicationInfo(activity);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Huawei(Activity activity) {
|
||||
|
||||
26
app/src/main/java/org/yameida/worktool/utils/RuntimeUtil.kt
Normal file
26
app/src/main/java/org/yameida/worktool/utils/RuntimeUtil.kt
Normal file
@@ -0,0 +1,26 @@
|
||||
package org.yameida.worktool.utils
|
||||
|
||||
import java.io.BufferedReader
|
||||
import java.io.InputStream
|
||||
import java.io.InputStreamReader
|
||||
|
||||
object RuntimeUtil {
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun exec(cmd: String): String? {
|
||||
var ret: String? = null
|
||||
val p = Runtime.getRuntime().exec(arrayOf("sh", "-c", cmd))
|
||||
val inputStream: InputStream = p.inputStream
|
||||
val reader = BufferedReader(InputStreamReader(inputStream))
|
||||
var line: String? = reader.readLine()
|
||||
while (line != null) {
|
||||
ret = line
|
||||
line = reader.readLine()
|
||||
}
|
||||
p.waitFor()
|
||||
inputStream.close()
|
||||
reader.close()
|
||||
p.destroy()
|
||||
return ret
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user