update 悬浮窗兼容模拟器;图片复制

This commit is contained in:
gallonyin
2023-02-03 15:43:49 +08:00
parent 63fcd7311f
commit 6b3eb10f4a
10 changed files with 343 additions and 34 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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("推送图片失败")
}
})
}
}

View File

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

View File

@@ -35,7 +35,8 @@ public class PermissionPageManagement {
* @param activity
*/
public static void goToSetting(Activity activity){
switch (Build.MANUFACTURER){
try {
switch (Build.MANUFACTURER) {
case MANUFACTURER_HUAWEI:
Huawei(activity);
break;
@@ -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) {

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