From 6b3eb10f4aab26e7102645e445b8845c673a5731 Mon Sep 17 00:00:00 2001 From: gallonyin Date: Fri, 3 Feb 2023 15:43:49 +0800 Subject: [PATCH] =?UTF-8?q?update=20=E6=82=AC=E6=B5=AE=E7=AA=97=E5=85=BC?= =?UTF-8?q?=E5=AE=B9=E6=A8=A1=E6=8B=9F=E5=99=A8;=E5=9B=BE=E7=89=87?= =?UTF-8?q?=E5=A4=8D=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/yameida/worktool/Constant.kt | 1 + .../worktool/activity/ListenActivity.kt | 13 ++ .../worktool/observer/MultiFileObserver.java | 152 ++++++++++++++++++ .../yameida/worktool/service/GlobalMethod.kt | 11 +- .../worktool/service/WeworkLoopImpl.kt | 59 ++++++- .../worktool/utils/FlowPermissionHelper.kt | 5 + .../org/yameida/worktool/utils/HttpUtil.kt | 27 ++++ .../worktool/utils/ImageDepthSizeUtil.java | 26 +++ .../utils/PermissionPageManagement.java | 57 ++++--- .../org/yameida/worktool/utils/RuntimeUtil.kt | 26 +++ 10 files changed, 343 insertions(+), 34 deletions(-) create mode 100644 app/src/main/java/org/yameida/worktool/observer/MultiFileObserver.java create mode 100644 app/src/main/java/org/yameida/worktool/utils/ImageDepthSizeUtil.java create mode 100644 app/src/main/java/org/yameida/worktool/utils/RuntimeUtil.kt diff --git a/app/src/main/java/org/yameida/worktool/Constant.kt b/app/src/main/java/org/yameida/worktool/Constant.kt index 3ea8ef7..fd7bd6b 100644 --- a/app/src/main/java/org/yameida/worktool/Constant.kt +++ b/app/src/main/java/org/yameida/worktool/Constant.kt @@ -7,6 +7,7 @@ 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", "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" 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/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/WeworkLoopImpl.kt b/app/src/main/java/org/yameida/worktool/service/WeworkLoopImpl.kt index a37018e..e94e9be 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 主循环 @@ -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)) } } } @@ -474,6 +478,7 @@ object WeworkLoopImpl { */ private fun parseChatMessageItem( node: AccessibilityNodeInfo, + titleList: ArrayList, roomType: Int ): WeworkMessageBean.SubMessageBean { val message: WeworkMessageBean.SubMessageBean @@ -517,6 +522,52 @@ object WeworkLoopImpl { } } message = WeworkMessageBean.SubMessageBean(0, textType, itemMessageList, nameList) + //图片类型特殊处理 + if (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/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