diff --git a/README.md b/README.md index 92e4776..e16bd75 100644 --- a/README.md +++ b/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;优化通过好友请求;其他已知缺陷修复 diff --git a/app/build.gradle b/app/build.gradle index 4fa9cd5..b99699a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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" } diff --git a/app/src/main/java/org/yameida/worktool/Constant.kt b/app/src/main/java/org/yameida/worktool/Constant.kt index 4db8872..b61b509 100644 --- a/app/src/main/java/org/yameida/worktool/Constant.kt +++ b/app/src/main/java/org/yameida/worktool/Constant.kt @@ -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) { diff --git a/app/src/main/java/org/yameida/worktool/activity/ListenActivity.kt b/app/src/main/java/org/yameida/worktool/activity/ListenActivity.kt index 74bb6f2..81c187e 100644 --- a/app/src/main/java/org/yameida/worktool/activity/ListenActivity.kt +++ b/app/src/main/java/org/yameida/worktool/activity/ListenActivity.kt @@ -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) diff --git a/app/src/main/java/org/yameida/worktool/model/WeworkMessageBean.java b/app/src/main/java/org/yameida/worktool/model/WeworkMessageBean.java index 72735e6..c2fddb0 100644 --- a/app/src/main/java/org/yameida/worktool/model/WeworkMessageBean.java +++ b/app/src/main/java/org/yameida/worktool/model/WeworkMessageBean.java @@ -174,6 +174,8 @@ public class WeworkMessageBean { public String extraText; //接收消息类型 public Integer textType; + //最大尝试次数 + public Integer maxRetryCount; //群名 public String groupName; diff --git a/app/src/main/java/org/yameida/worktool/observer/MultiFileObserver.java b/app/src/main/java/org/yameida/worktool/observer/MultiFileObserver.java new file mode 100644 index 0000000..103cae1 --- /dev/null +++ b/app/src/main/java/org/yameida/worktool/observer/MultiFileObserver.java @@ -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 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 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(); + Stack stack = new Stack(); + 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); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/yameida/worktool/service/GlobalMethod.kt b/app/src/main/java/org/yameida/worktool/service/GlobalMethod.kt index f7c7385..4252e56 100644 --- a/app/src/main/java/org/yameida/worktool/service/GlobalMethod.kt +++ b/app/src/main/java/org/yameida/worktool/service/GlobalMethod.kt @@ -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,10 +165,12 @@ fun backPress() { } else { LogUtils.d("未找到对话框 点击bar中心") AccessibilityUtil.performXYClick(WeworkController.weworkService, ScreenUtils.getScreenWidth() / 2F, BarUtils.getStatusBarHeight() * 2F) - sleep(Constant.POP_WINDOW_INTERVAL) - val firstEmptyTextView = AccessibilityUtil.findAllByClazz(getRoot(), Views.TextView).firstOrNull { it.text.isNullOrEmpty() } - if (firstEmptyTextView != null && firstEmptyTextView.isClickable) { - AccessibilityUtil.performClick(firstEmptyTextView) + 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) + } } } } diff --git a/app/src/main/java/org/yameida/worktool/service/MyLooper.kt b/app/src/main/java/org/yameida/worktool/service/MyLooper.kt index de3ccc6..eef8334 100644 --- a/app/src/main/java/org/yameida/worktool/service/MyLooper.kt +++ b/app/src/main/java/org/yameida/worktool/service/MyLooper.kt @@ -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() } diff --git a/app/src/main/java/org/yameida/worktool/service/WeworkController.kt b/app/src/main/java/org/yameida/worktool/service/WeworkController.kt index 2916257..160c258 100644 --- a/app/src/main/java/org/yameida/worktool/service/WeworkController.kt +++ b/app/src/main/java/org/yameida/worktool/service/WeworkController.kt @@ -334,7 +334,8 @@ object WeworkController { message.fileUrl, message.fileBase64, message.fileType, - message.extraText + message.extraText, + message.maxRetryCount ) } diff --git a/app/src/main/java/org/yameida/worktool/service/WeworkLoopImpl.kt b/app/src/main/java/org/yameida/worktool/service/WeworkLoopImpl.kt index 0e0e620..519e458 100644 --- a/app/src/main/java/org/yameida/worktool/service/WeworkLoopImpl.kt +++ b/app/src/main/java/org/yameida/worktool/service/WeworkLoopImpl.kt @@ -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>() 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>() + 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, 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 diff --git a/app/src/main/java/org/yameida/worktool/service/WeworkOperationImpl.kt b/app/src/main/java/org/yameida/worktool/service/WeworkOperationImpl.kt index f1f25dd..3461055 100644 --- a/app/src/main/java/org/yameida/worktool/service/WeworkOperationImpl.kt +++ b/app/src/main/java/org/yameida/worktool/service/WeworkOperationImpl.kt @@ -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") diff --git a/app/src/main/java/org/yameida/worktool/utils/FlowPermissionHelper.kt b/app/src/main/java/org/yameida/worktool/utils/FlowPermissionHelper.kt index 9a070ce..2942116 100644 --- a/app/src/main/java/org/yameida/worktool/utils/FlowPermissionHelper.kt +++ b/app/src/main/java/org/yameida/worktool/utils/FlowPermissionHelper.kt @@ -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) } diff --git a/app/src/main/java/org/yameida/worktool/utils/HttpUtil.kt b/app/src/main/java/org/yameida/worktool/utils/HttpUtil.kt index a54bf58..3d24fb5 100644 --- a/app/src/main/java/org/yameida/worktool/utils/HttpUtil.kt +++ b/app/src/main/java/org/yameida/worktool/utils/HttpUtil.kt @@ -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(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(url) + .params("groupName", groupName) + .params("receivedName", receivedName ?: "") + .params("image", File(imagePath)) + .execute(object : StringCallback() { + override fun onSuccess(response: Response) { + LogUtils.d("推送图片成功: $groupName $receivedName $imagePath") + } + + override fun onError(response: Response) { + ToastUtils.showLong("推送图片失败") + LogUtils.e("推送图片失败") + } + }) + } } diff --git a/app/src/main/java/org/yameida/worktool/utils/ImageDepthSizeUtil.java b/app/src/main/java/org/yameida/worktool/utils/ImageDepthSizeUtil.java new file mode 100644 index 0000000..6db85ee --- /dev/null +++ b/app/src/main/java/org/yameida/worktool/utils/ImageDepthSizeUtil.java @@ -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; + } + +} diff --git a/app/src/main/java/org/yameida/worktool/utils/PermissionPageManagement.java b/app/src/main/java/org/yameida/worktool/utils/PermissionPageManagement.java index b02819e..2be5285 100644 --- a/app/src/main/java/org/yameida/worktool/utils/PermissionPageManagement.java +++ b/app/src/main/java/org/yameida/worktool/utils/PermissionPageManagement.java @@ -35,32 +35,37 @@ public class PermissionPageManagement { * @param activity */ public static void goToSetting(Activity activity){ - switch (Build.MANUFACTURER){ - case MANUFACTURER_HUAWEI: - Huawei(activity); - break; - case MANUFACTURER_MEIZU: - Meizu(activity); - break; - case MANUFACTURER_XIAOMI: - Xiaomi(activity); - break; - case MANUFACTURER_SONY: - Sony(activity); - break; - case MANUFACTURER_OPPO: - OPPO(activity); - break; - case MANUFACTURER_VIVO: - VIVO(activity); - break; - case MANUFACTURER_LG: - LG(activity); - break; - default: - ApplicationInfo(activity); - Log.e("goToSetting", "目前暂不支持此系统"); - break; + try { + switch (Build.MANUFACTURER) { + case MANUFACTURER_HUAWEI: + Huawei(activity); + break; + case MANUFACTURER_MEIZU: + Meizu(activity); + break; + case MANUFACTURER_XIAOMI: + Xiaomi(activity); + break; + case MANUFACTURER_SONY: + Sony(activity); + break; + case MANUFACTURER_OPPO: + OPPO(activity); + break; + case MANUFACTURER_VIVO: + VIVO(activity); + break; + case MANUFACTURER_LG: + LG(activity); + break; + default: + ApplicationInfo(activity); + Log.e("goToSetting", "目前暂不支持此系统"); + break; + } + } catch (Exception e) { + Log.e("goToSetting", "Error 目前暂不支持此系统"); + ApplicationInfo(activity); } } diff --git a/app/src/main/java/org/yameida/worktool/utils/RuntimeUtil.kt b/app/src/main/java/org/yameida/worktool/utils/RuntimeUtil.kt new file mode 100644 index 0000000..d43e91d --- /dev/null +++ b/app/src/main/java/org/yameida/worktool/utils/RuntimeUtil.kt @@ -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 + } +} \ No newline at end of file