diff --git a/app/build.gradle b/app/build.gradle index e509df5..8da8278 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -45,6 +45,8 @@ dependencies { //工具集 implementation 'com.blankj:utilcodex:1.31.0' + //toast + implementation 'com.github.getActivity:ToastUtils:10.5' //Gson implementation 'com.google.code.gson:gson:2.8.5' //网络 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e45dca6..7205f54 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -60,6 +60,16 @@ android:resource="@xml/accessibility_service_config" /> + + + + diff --git a/app/src/main/java/org/yameida/worktool/Demo.kt b/app/src/main/java/org/yameida/worktool/Demo.kt index 7eb74b0..a183716 100644 --- a/app/src/main/java/org/yameida/worktool/Demo.kt +++ b/app/src/main/java/org/yameida/worktool/Demo.kt @@ -22,77 +22,6 @@ object Demo { //打印当前视图树 // AccessibilityUtil.printNodeClazzTree(getRoot()) - //获取群二维码 -// WeworkOperationImpl.getGroupQrcode("测试群01") - - //手机号添加好友 -// WeworkOperationImpl.addFriendByPhone(WeworkMessageBean.Friend().apply { -// this.phone = "13010001000" -// this.markName = "hhh" -// this.markCorp = "corp" -// this.markExtra = "ex" -// this.tagList = arrayListOf("tag1", "tag2") -// }) - - //自动通过好友 -// WeworkLoopImpl.getFriendRequest() - - //自动通过好友(后台可配置开关) -// WeworkLoopImpl.mainLoop() - - //创建群信息 -// WeworkController.initGroup(WeworkMessageBean().apply { -// groupName = "新建外部群 " + UUID.randomUUID().toString().substring(0, 5) -// selectList = arrayListOf("冯燕", "尹甲仑") -// groupAnnouncement = "本群为雨花台区法院诉前调解官方微信群" -// }) - - //修改群信息 -// WeworkController.intoGroupAndConfig(WeworkMessageBean().apply { -// groupName = "新建外部群 " + UUID.randomUUID().toString().substring(0, 5) -// selectList = arrayListOf("冯燕", "尹甲仑") -// groupAnnouncement = "本群为雨花台区法院诉前调解官方微信群" -// }) - - //获取群信息 -// WeworkController.getGroupInfo(WeworkMessageBean().apply { -// selectList = arrayListOf("企微RPA机器人自测1") -// }) - - //在房间内发送消息 -// WeworkController.sendMessage(WeworkMessageBean().apply { -// titleList = arrayListOf("下级群1", "上级群1") -// receivedContent = "aaa" -// }) - - //获取我的信息 -// WeworkController.getMyInfo() - - //推送任意小程序 -// WeworkController.pushMicroprogram(WeworkMessageBean().apply { -// titleList = arrayListOf("尹甲仑") -// objectName = "小法名律" -// extraText = "123" -// }) - - //推送微盘图片 -// WeworkController.pushMicroDiskImage(WeworkMessageBean().apply { -// titleList = arrayListOf("尹甲仑") -// objectName = "雨水.jpg" -// }) - - //推送微盘文件 -// WeworkController.pushMicroDiskFile(WeworkMessageBean().apply { -// titleList = arrayListOf("尹甲仑") -// objectName = "雨水.jpg" -// }) - - //推送腾讯文档 -// WeworkController.pushOffice(WeworkMessageBean().apply { -// titleList = arrayListOf("尹甲仑") -// objectName = "机器人中台" -// extraText = "附加留言" -// }) } fun test2(name: String) { diff --git a/app/src/main/java/org/yameida/worktool/MyApplication.kt b/app/src/main/java/org/yameida/worktool/MyApplication.kt index 1ffcb9b..387b898 100644 --- a/app/src/main/java/org/yameida/worktool/MyApplication.kt +++ b/app/src/main/java/org/yameida/worktool/MyApplication.kt @@ -1,8 +1,12 @@ package org.yameida.worktool import android.app.Application +import android.content.Intent +import com.blankj.utilcode.util.LogUtils import com.blankj.utilcode.util.SPUtils import com.blankj.utilcode.util.Utils +import com.efs.sdk.base.core.util.PackageUtil +import com.hjq.toast.ToastUtils import com.tendcloud.tenddata.TalkingDataSDK import com.umeng.commonsdk.UMConfigure import org.yameida.worktool.config.GlobalException @@ -10,15 +14,34 @@ import update.UpdateAppUtils class MyApplication : Application() { + companion object { + + /** + * 回到WorkTool首页 需要先授权显示悬浮窗 + */ + fun launchIntent() { + LogUtils.e("进入WorkTool APP~") + val app = Utils.getApp() + app.packageManager.getLaunchIntentForPackage(PackageUtil.getPackageName(app))?.apply { + this.flags = Intent.FLAG_ACTIVITY_NEW_TASK + app.startActivity(this) + } + } + } + override fun onCreate() { super.onCreate() //初始化工具类 Utils.init(this) + //初始化 Toast 框架 + ToastUtils.init(this) //初始化友盟统计 - UMConfigure.preInit(this, "6284a3a3d024421570f97c3c", "main_channel") + val key = "6284a3a3d024421570f97c3c" + val channel = "main_channel" + UMConfigure.preInit(this, key, channel) //判断是否同意隐私协议,uminit为1时为已经同意,直接初始化umsdk if (SPUtils.getInstance().getString("uminit", "1") == "1") { - UMConfigure.init(this, "6284a3a3d024421570f97c3c", "main_channel", UMConfigure.DEVICE_TYPE_PHONE, "") + UMConfigure.init(this, key, channel, UMConfigure.DEVICE_TYPE_PHONE, "") } TalkingDataSDK.init(this, "80E9C84E39904DAFB28562910FF7C86C", "worktool_master", SPUtils.getInstance().getString(Constant.LISTEN_CHANNEL_ID)); //初始化自动更新 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 9b94fcf..e4d5d52 100644 --- a/app/src/main/java/org/yameida/worktool/activity/ListenActivity.kt +++ b/app/src/main/java/org/yameida/worktool/activity/ListenActivity.kt @@ -1,10 +1,7 @@ package org.yameida.worktool.activity -import android.content.DialogInterface -import android.content.Intent import android.os.Bundle import android.provider.Settings -import android.text.TextUtils.SimpleStringSplitter import android.view.WindowManager import android.widget.CompoundButton import android.widget.Switch @@ -17,8 +14,12 @@ import org.yameida.worktool.* import org.yameida.worktool.service.WeworkService import org.yameida.worktool.utils.UpdateUtil import android.app.Dialog +import android.content.* import android.widget.Button import android.widget.EditText +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import org.yameida.worktool.utils.PermissionHelper +import org.yameida.worktool.utils.PermissionPageManagement class ListenActivity : AppCompatActivity() { @@ -32,16 +33,28 @@ class ListenActivity : AppCompatActivity() { initView() initAccessibility() + initOverlays() UpdateUtil.checkUpdate() PermissionUtils.permission("android.permission.READ_EXTERNAL_STORAGE").request() + registerReceiver(openWsReceiver, IntentFilter(Constant.WEWORK_NOTIFY)) } - override fun onStart() { - super.onStart() + override fun onDestroy() { + super.onDestroy() + unregisterReceiver(openWsReceiver) + } + + override fun onResume() { + super.onResume() + sw_overlay.isChecked = PermissionUtils.isGrantedDrawOverlays() freshOpenServiceSwitch( WeworkService::class.java, sw_accessibility ) + if (needToWork) { + needToWork = false + goToWork() + } } private fun initView() { @@ -102,11 +115,11 @@ class ListenActivity : AppCompatActivity() { if (SPUtils.getInstance().getString(Constant.LISTEN_CHANNEL_ID).isNullOrBlank()) { sw_accessibility.isChecked = false ToastUtils.showLong("请先填写并保存链接号~") - } else if (!isAccessibilitySettingOn()) { + } else if (!PermissionHelper.isAccessibilitySettingOn()) { openAccessibility() } } else { - if (isAccessibilitySettingOn()) { + if (PermissionHelper.isAccessibilitySettingOn()) { sw_accessibility.isChecked = true val intent = Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS) startActivity(intent) @@ -115,39 +128,27 @@ class ListenActivity : AppCompatActivity() { }) } - private fun isAccessibilitySettingOn(): Boolean { - val context = Utils.getApp() - var enable = 0 - val serviceName = context.packageName + "/" + WeworkService::class.java.canonicalName - LogUtils.i("isAccessibilitySettingOn: $serviceName") - try { - enable = Settings.Secure.getInt( - context.contentResolver, - Settings.Secure.ACCESSIBILITY_ENABLED, - 0 - ) - } catch (e: Exception) { - e.printStackTrace() - } - if (enable == 1) { - val stringSplitter = SimpleStringSplitter(':') - val settingVal = Settings.Secure.getString( - context.contentResolver, - Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES - ) - if (settingVal != null) { - stringSplitter.setString(settingVal) - while (stringSplitter.hasNext()) { - val accessibilityService = stringSplitter.next() - if (accessibilityService == serviceName) { - LogUtils.i("isAccessibilitySettingOn: true") - return true - } + private fun initOverlays() { + sw_overlay.setOnCheckedChangeListener(CompoundButton.OnCheckedChangeListener { buttonView, isChecked -> + LogUtils.i("sw_overlay onCheckedChanged: $isChecked") + if (isChecked) { + if (!PermissionUtils.isGrantedDrawOverlays()) { + PermissionUtils.requestDrawOverlays(object : PermissionUtils.SimpleCallback { + override fun onGranted() { + ToastUtils.showLong("请同时打开后台弹出界面权限~") + PermissionPageManagement.goToSetting(this@ListenActivity) + } + + override fun onDenied() { sw_accessibility.isChecked = false } + }) + } + } else { + if (PermissionUtils.isGrantedDrawOverlays()) { + sw_overlay.isChecked = true + PermissionPageManagement.goToSetting(this) } } - } - LogUtils.i("isAccessibilitySettingOn: false") - return false + }) } /** @@ -185,7 +186,7 @@ class ListenActivity : AppCompatActivity() { } private fun freshOpenServiceSwitch(clazz: Class<*>, s: Switch) { - if (isAccessibilitySettingOn()) { + if (PermissionHelper.isAccessibilitySettingOn()) { s.isChecked = true when (s.id) { R.id.sw_accessibility -> { @@ -205,6 +206,7 @@ class ListenActivity : AppCompatActivity() { val commentDialog = Dialog(this) commentDialog.setContentView(R.layout.dialog_input) val et: EditText = commentDialog.findViewById(R.id.body) as EditText + et.setText(tv_host.text) val okBtn: Button = commentDialog.findViewById(R.id.ok) as Button okBtn.setOnClickListener { val text = et.text.toString() @@ -228,4 +230,32 @@ class ListenActivity : AppCompatActivity() { commentDialog.show() } + private var needToWork = false + + private fun goToWork() { + val positiveButton = + MaterialAlertDialogBuilder(this, R.style.Theme_MaterialComponents_DayNight_Dialog) + .setTitle("设置成功") + .setMessage("请勿人工操作手机 \n5秒后自动跳转") + .setNegativeButton("", null) + .setPositiveButton("", null) + val show = positiveButton.show() + bt_save.postDelayed({ show.dismiss() }, 5000) + bt_save.postDelayed({ + packageManager.getLaunchIntentForPackage(Constant.PACKAGE_NAMES)?.apply { + this.flags = Intent.FLAG_ACTIVITY_NEW_TASK + startActivity(this) + } + com.hjq.toast.ToastUtils.show("机器人运行中 请勿人工操作手机~") + }, 5000) + } + + private val openWsReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + if (intent.getStringExtra("type") == "openWs") { + needToWork = intent.getBooleanExtra("switch", false) + } + } + } + } \ No newline at end of file diff --git a/app/src/main/java/org/yameida/worktool/model/ExecCallbackBean.kt b/app/src/main/java/org/yameida/worktool/model/ExecCallbackBean.kt new file mode 100644 index 0000000..0227633 --- /dev/null +++ b/app/src/main/java/org/yameida/worktool/model/ExecCallbackBean.kt @@ -0,0 +1,18 @@ +package org.yameida.worktool.model + +import java.util.* + +data class ExecCallbackBean( + //原生消息指令 + var rawMsg: String? = null, + + //1成功 0失败 + var rawSuccess: Int = 0, + + //失败原因 + var errorReason: String = "", + + //执行时间 + var runTime: Date = Date() + +) : WeworkMessageBean() \ No newline at end of file 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 1382685..cf1c27a 100644 --- a/app/src/main/java/org/yameida/worktool/model/WeworkMessageBean.java +++ b/app/src/main/java/org/yameida/worktool/model/WeworkMessageBean.java @@ -30,6 +30,7 @@ public class WeworkMessageBean { * 通过当前所有好友请求 PASS_ALL_FRIEND_REQUEST * 按手机号添加好友 ADD_FRIEND_BY_PHONE * 展示群信息 SHOW_GROUP_INFO + * 推送文件(网络图片视频和文件等) PUSH_FILE *

* 非操作类型 300 * 机器人普通日志记录 ROBOT_LOG @@ -61,6 +62,7 @@ public class WeworkMessageBean { public static final int INIT_ANTI_HARASSMENT_RULE = 215; public static final int UPDATE_ANTI_HARASSMENT_RULE = 216; public static final int DELETED_ANTI_HARASSMENT_RULE = 217; + public static final int PUSH_FILE = 218; public static final int ROBOT_LOG = 301; public static final int ROBOT_ERROR_LOG = 302; @@ -126,6 +128,9 @@ public class WeworkMessageBean { public static final int TEXT_TYPE_COLLECTION = 14; public static final int TEXT_TYPE_REPLY = 15; + //消息id(解析指令时同步) + public String messageId; + //标题 通常是群名或联系人 public List titleList; //上传聊天列表 @@ -141,6 +146,8 @@ public class WeworkMessageBean { public String receivedContent; //想要at的昵称 public String at; + //想要at的昵称列表 + public List atList; //原始内容text public String originalContent; //多选(转发等) @@ -160,6 +167,10 @@ public class WeworkMessageBean { public Integer groupNumber; //群公告 public String groupAnnouncement; + //群备注 + public String groupRemark; + //群模板 + public String groupTemplate; //新群名 public String newGroupName; //新群公告 @@ -178,6 +189,10 @@ public class WeworkMessageBean { //添加好友 public Friend friend; + //网络文件 + public String fileUrl; + public String fileType; + public WeworkMessageBean() {} public WeworkMessageBean(String receivedName, String receivedContent, int type, Integer roomType, List titleList, List messageList, String log) { @@ -190,6 +205,7 @@ public class WeworkMessageBean { this.receivedName = receivedName; } + //消息类型 public int type = 0; //消息列表的每条消息 @@ -254,6 +270,19 @@ public class WeworkMessageBean { public Boolean newFriend; //留言 public String leavingMsg; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Friend friend = (Friend) o; + return Objects.equals(phone, friend.phone) && Objects.equals(name, friend.name) && Objects.equals(markName, friend.markName) && Objects.equals(markCorp, friend.markCorp) && Objects.equals(markExtra, friend.markExtra) && Objects.equals(tagList, friend.tagList) && Objects.equals(newFriend, friend.newFriend) && Objects.equals(leavingMsg, friend.leavingMsg); + } + + @Override + public int hashCode() { + return Objects.hash(phone, name, markName, markCorp, markExtra, tagList, newFriend, leavingMsg); + } } @Override @@ -261,12 +290,12 @@ public class WeworkMessageBean { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; WeworkMessageBean that = (WeworkMessageBean) o; - return textType == that.textType && showMessageHistory == that.showMessageHistory && type == that.type && Objects.equals(titleList, that.titleList) && Objects.equals(messageList, that.messageList) && Objects.equals(log, that.log) && Objects.equals(roomType, that.roomType) && Objects.equals(receivedName, that.receivedName) && Objects.equals(receivedContent, that.receivedContent) && Objects.equals(originalContent, that.originalContent) && Objects.equals(nameList, that.nameList) && Objects.equals(extraText, that.extraText) && Objects.equals(groupName, that.groupName) && Objects.equals(groupOwner, that.groupOwner) && Objects.equals(selectList, that.selectList) && Objects.equals(groupNumber, that.groupNumber) && Objects.equals(groupAnnouncement, that.groupAnnouncement) && Objects.equals(newGroupName, that.newGroupName) && Objects.equals(newGroupAnnouncement, that.newGroupAnnouncement) && Objects.equals(removeList, that.removeList) && Objects.equals(myInfo, that.myInfo) && Objects.equals(objectName, that.objectName); + return textType == that.textType && showMessageHistory == that.showMessageHistory && type == that.type && Objects.equals(titleList, that.titleList) && Objects.equals(messageList, that.messageList) && Objects.equals(log, that.log) && Objects.equals(roomType, that.roomType) && Objects.equals(receivedName, that.receivedName) && Objects.equals(receivedContent, that.receivedContent) && Objects.equals(at, that.at) && Objects.equals(atList, that.atList) && Objects.equals(originalContent, that.originalContent) && Objects.equals(nameList, that.nameList) && Objects.equals(extraText, that.extraText) && Objects.equals(groupName, that.groupName) && Objects.equals(groupOwner, that.groupOwner) && Objects.equals(selectList, that.selectList) && Objects.equals(groupNumber, that.groupNumber) && Objects.equals(groupAnnouncement, that.groupAnnouncement) && Objects.equals(groupRemark, that.groupRemark) && Objects.equals(groupTemplate, that.groupTemplate) && Objects.equals(newGroupName, that.newGroupName) && Objects.equals(newGroupAnnouncement, that.newGroupAnnouncement) && Objects.equals(removeList, that.removeList) && Objects.equals(myInfo, that.myInfo) && Objects.equals(objectName, that.objectName) && Objects.equals(qrcode, that.qrcode) && Objects.equals(friend, that.friend) && Objects.equals(fileUrl, that.fileUrl) && Objects.equals(fileType, that.fileType); } @Override public int hashCode() { - return Objects.hash(titleList, messageList, log, roomType, receivedName, receivedContent, originalContent, nameList, extraText, textType, groupName, groupOwner, selectList, groupNumber, groupAnnouncement, newGroupName, newGroupAnnouncement, removeList, showMessageHistory, myInfo, objectName, type, friend); + return Objects.hash(titleList, messageList, log, roomType, receivedName, receivedContent, at, atList, originalContent, nameList, extraText, textType, groupName, groupOwner, selectList, groupNumber, groupAnnouncement, groupRemark, groupTemplate, newGroupName, newGroupAnnouncement, removeList, showMessageHistory, myInfo, objectName, qrcode, friend, fileUrl, fileType, type); } @Override @@ -278,6 +307,8 @@ public class WeworkMessageBean { ", roomType=" + roomType + ", receivedName='" + receivedName + '\'' + ", receivedContent='" + receivedContent + '\'' + + ", at='" + at + '\'' + + ", atList=" + atList + ", originalContent='" + originalContent + '\'' + ", nameList=" + nameList + ", extraText='" + extraText + '\'' + @@ -287,12 +318,18 @@ public class WeworkMessageBean { ", selectList=" + selectList + ", groupNumber=" + groupNumber + ", groupAnnouncement='" + groupAnnouncement + '\'' + + ", groupRemark='" + groupRemark + '\'' + + ", groupTemplate='" + groupTemplate + '\'' + ", newGroupName='" + newGroupName + '\'' + ", newGroupAnnouncement='" + newGroupAnnouncement + '\'' + ", removeList=" + removeList + ", showMessageHistory=" + showMessageHistory + ", myInfo=" + myInfo + ", objectName='" + objectName + '\'' + + ", qrcode='" + qrcode + '\'' + + ", friend=" + friend + + ", fileUrl='" + fileUrl + '\'' + + ", fileType='" + fileType + '\'' + ", type=" + type + '}'; } diff --git a/app/src/main/java/org/yameida/worktool/model/WeworkMessageListBean.kt b/app/src/main/java/org/yameida/worktool/model/WeworkMessageListBean.kt index c42ddab..f9d27a8 100644 --- a/app/src/main/java/org/yameida/worktool/model/WeworkMessageListBean.kt +++ b/app/src/main/java/org/yameida/worktool/model/WeworkMessageListBean.kt @@ -7,12 +7,13 @@ import org.yameida.worktool.Constant import java.util.* import kotlin.collections.ArrayList -class WeworkMessageListBean { +class WeworkMessageListBean { companion object { const val SOCKET_TYPE_HEARTBEAT = 0 const val SOCKET_TYPE_MESSAGE_CONFIRM = 1 const val SOCKET_TYPE_MESSAGE_LIST = 2 + const val SOCKET_TYPE_RAW_CONFIRM = 3 } /** @@ -20,6 +21,7 @@ class WeworkMessageListBean { * TYPE_HEARTBEAT 心跳检测 * TYPE_MESSAGE_CONFIRM 消息确认 * TYPE_MESSAGE_LIST 消息列表 + * SOCKET_TYPE_RAW_CONFIRM 执行指令结果确认 */ var socketType = SOCKET_TYPE_HEARTBEAT @@ -27,7 +29,7 @@ class WeworkMessageListBean { var messageId = TimeUtils.date2String(Date()).replace(" ", "#") + "#" + UUID.randomUUID() //消息列表 - var list: ArrayList = arrayListOf() + var list: ArrayList = arrayListOf() //加密消息列表 var encryptedList: String = "" @@ -35,7 +37,7 @@ class WeworkMessageListBean { //消息加密 0不加密 1AES var encryptType = Constant.encryptType - constructor(weworkMessageBean: WeworkMessageBean, type: Int) { + constructor(weworkMessageBean: T, type: Int, messageId: String? = null) { if (encryptType == 0) { list.add(weworkMessageBean) } else if (encryptType == 1) { @@ -47,6 +49,7 @@ class WeworkMessageListBean { ) } this.socketType = type + if (messageId != null) this.messageId = messageId } constructor(messageId: String, type: Int) { @@ -54,17 +57,4 @@ class WeworkMessageListBean { this.socketType = type } - constructor(weworkMessageBeanList: List, type: Int) { - if (encryptType == 0) { - list.addAll(weworkMessageBeanList) - } else if (encryptType == 1) { - encryptedList = EncryptUtils.encryptAES2HexString( - GsonUtils.toJson(weworkMessageBeanList).toByteArray(), - Constant.key, - Constant.transformation, - Constant.iv - ) - } - this.socketType = type - } } \ 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 758468b..fb80dfa 100644 --- a/app/src/main/java/org/yameida/worktool/service/GlobalMethod.kt +++ b/app/src/main/java/org/yameida/worktool/service/GlobalMethod.kt @@ -5,7 +5,10 @@ import android.view.accessibility.AccessibilityNodeInfo import com.blankj.utilcode.util.GsonUtils import com.blankj.utilcode.util.LogUtils import com.blankj.utilcode.util.ScreenUtils +import com.hjq.toast.ToastUtils import org.yameida.worktool.Constant +import org.yameida.worktool.MyApplication +import org.yameida.worktool.model.ExecCallbackBean import org.yameida.worktool.model.WeworkMessageBean import org.yameida.worktool.model.WeworkMessageListBean import org.yameida.worktool.utils.AccessibilityUtil @@ -54,6 +57,13 @@ fun goHomeTab(title: String): Boolean { } if (!atHome) { backPress() + //如果在登录页面就提示关闭worktool主功能 + if (AccessibilityUtil.findOnceByText(getRoot(), "手机号登录", exact = true) != null) { + LogUtils.e("登录前请先关闭WorkTool主功能!") + ToastUtils.show("登录前请先关闭WorkTool主功能!") + MyApplication.launchIntent() + sleep(5000) + } } } LogUtils.v("进入首页-${title}页") @@ -135,7 +145,20 @@ fun backPress() { } /** - * 上传运行日志 简单封装 info log + * 上传执行指令结果 + */ +fun uploadCommandResult(message: WeworkMessageBean, success: Boolean, reason: String) { + WeworkController.weworkService.webSocketManager.send( + WeworkMessageListBean( + ExecCallbackBean(GsonUtils.toJson(message), if (success) 1 else 0, reason), + WeworkMessageListBean.SOCKET_TYPE_RAW_CONFIRM, + messageId = message.messageId + ), true + ) +} + +/** + * 上传运行日志 */ fun log(message: Any?, type: Int = WeworkMessageBean.ROBOT_LOG) { WeworkController.weworkService.webSocketManager.send( @@ -154,7 +177,7 @@ fun log(message: Any?, type: Int = WeworkMessageBean.ROBOT_LOG) { } /** - * 上传运行日志 简单封装 error log + * 上传运行日志 */ fun error(message: Any?) { log(message, WeworkMessageBean.ROBOT_ERROR_LOG) 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 8cc85d6..f49ec2a 100644 --- a/app/src/main/java/org/yameida/worktool/service/MyLooper.kt +++ b/app/src/main/java/org/yameida/worktool/service/MyLooper.kt @@ -46,12 +46,13 @@ object MyLooper { while (true) { threadHandler?.let { return it } LogUtils.e("threadHandler is not ready...") + sleep(Constant.POP_WINDOW_INTERVAL / 5) } } fun onMessage(webSocket: WebSocket?, text: String) { - val messageList = - GsonUtils.fromJson(text, WeworkMessageListBean::class.java) + val messageList: WeworkMessageListBean = + GsonUtils.fromJson>(text, object : TypeToken>(){}.type) if (messageList.socketType == WeworkMessageListBean.SOCKET_TYPE_HEARTBEAT) { return } @@ -82,7 +83,7 @@ object MyLooper { WeworkController.mainLoopRunning = false getInstance().sendMessage(Message.obtain().apply { what = message.type * message.hashCode() - obj = message + obj = message.apply { messageId = messageList.messageId } }) } getInstance().removeMessages(WeworkMessageBean.LOOP_RECEIVE_NEW_MESSAGE) @@ -129,6 +130,9 @@ object MyLooper { WeworkMessageBean.PUSH_OFFICE -> { WeworkController.pushOffice(message) } + WeworkMessageBean.PUSH_FILE -> { + WeworkController.pushFile(message) + } WeworkMessageBean.PASS_ALL_FRIEND_REQUEST -> { } WeworkMessageBean.ADD_FRIEND_BY_PHONE -> { 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 eb780a7..fabf9cc 100644 --- a/app/src/main/java/org/yameida/worktool/service/WeworkController.kt +++ b/app/src/main/java/org/yameida/worktool/service/WeworkController.kt @@ -47,8 +47,8 @@ object WeworkController { */ @RequestMapping fun sendMessage(message: WeworkMessageBean): Boolean { - LogUtils.d("sendMessage(): ${message.titleList} ${message.receivedContent} ${message.at}") - return WeworkOperationImpl.sendMessage(message.titleList, message.receivedContent, message.at) + LogUtils.d("sendMessage(): ${message.titleList} ${message.receivedContent} ${message.at} ${message.atList?.joinToString()}") + return WeworkOperationImpl.sendMessage(message.titleList, message.receivedContent, message.at, message.atList) } /** @@ -103,14 +103,16 @@ object WeworkController { * @param message#groupName 修改群名称 * @param message#selectList 添加群成员名称列表 选填 * @param message#groupAnnouncement 修改群公告 选填 + * @param message#groupRemark 修改群备注 选填 */ @RequestMapping fun initGroup(message: WeworkMessageBean): Boolean { - LogUtils.d("initGroup(): ${message.groupName} ${message.selectList} ${message.groupAnnouncement}") + LogUtils.d("initGroup(): ${message.groupName} ${message.selectList} ${message.groupAnnouncement} ${message.groupRemark}") return WeworkOperationImpl.initGroup( message.groupName, message.selectList, - message.groupAnnouncement + message.groupAnnouncement, + message.groupRemark ) } @@ -131,17 +133,19 @@ object WeworkController { * @param message#groupName 待修改的群 * @param message#newGroupName 修改群名 选填 * @param message#newGroupAnnouncement 修改群公告 选填 + * @param message#groupRemark 修改群备注 选填 * @param message#selectList 添加群成员名称列表/拉人 选填 * @param message#showMessageHistory 拉人是否附带历史记录 选填 * @param message#removeList 移除群成员名称列表/踢人 选填 */ @RequestMapping fun intoGroupAndConfig(message: WeworkMessageBean): Boolean { - LogUtils.d("intoGroupAndConfig(): ${message.groupName} ${message.newGroupName} ${message.newGroupAnnouncement} ${message.selectList} ${message.showMessageHistory} ${message.removeList}") + LogUtils.d("intoGroupAndConfig(): ${message.groupName} ${message.newGroupName} ${message.newGroupAnnouncement} ${message.selectList} ${message.showMessageHistory} ${message.removeList} ${message.groupRemark}") return WeworkOperationImpl.intoGroupAndConfig( message.groupName, message.newGroupName, message.newGroupAnnouncement, + message.groupRemark, message.selectList, message.showMessageHistory, message.removeList @@ -217,6 +221,27 @@ object WeworkController { ) } + /** + * 推送文件(网络图片视频和文件等) + * @see WeworkMessageBean.PUSH_FILE + * @param message#titleList 待发送姓名列表 + * @param message#objectName 文件名称 + * @param message#fileUrl 文件网络地址 + * @param message#fileType 文件类型 + * @param message#extraText 附加留言 可选 + */ + @RequestMapping + fun pushFile(message: WeworkMessageBean): Boolean { + LogUtils.d("pushFile(): ${message.titleList} ${message.objectName} ${message.fileUrl} ${message.fileType} ${message.extraText}") + return WeworkOperationImpl.pushFile( + message.titleList, + message.objectName, + message.fileUrl, + message.fileType, + message.extraText + ) + } + /** * 按手机号添加好友 * @see WeworkMessageBean.ADD_FRIEND_BY_PHONE diff --git a/app/src/main/java/org/yameida/worktool/service/WeworkGetImpl.kt b/app/src/main/java/org/yameida/worktool/service/WeworkGetImpl.kt index 0022da0..c30ac01 100644 --- a/app/src/main/java/org/yameida/worktool/service/WeworkGetImpl.kt +++ b/app/src/main/java/org/yameida/worktool/service/WeworkGetImpl.kt @@ -139,7 +139,7 @@ object WeworkGetImpl { } /** - * 获取群名、群主、群成员数、群公告 + * 获取群名、群主、群成员数、群公告、群备注 */ fun getGroupInfoDetail(): WeworkMessageBean { val weworkMessageBean = WeworkMessageBean() @@ -188,6 +188,12 @@ object WeworkGetImpl { LogUtils.d("群公告: " + tvAnnouncement.text) weworkMessageBean.groupAnnouncement = tvAnnouncement.text.toString() } + val tvRemarkFlag = AccessibilityUtil.findOnceByText(getRoot(), "备注", exact = true) + val tvRemark = AccessibilityUtil.findOnceByClazz(AccessibilityUtil.findBackNode(tvRemarkFlag), Views.TextView) + if (tvRemark != null && tvRemark.text != null) { + LogUtils.d("群备注: " + tvRemark.text) + weworkMessageBean.groupRemark = tvRemark.text.toString() + } backPress() return weworkMessageBean } 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 6e21c8f..c3613bd 100644 --- a/app/src/main/java/org/yameida/worktool/service/WeworkLoopImpl.kt +++ b/app/src/main/java/org/yameida/worktool/service/WeworkLoopImpl.kt @@ -73,7 +73,7 @@ object WeworkLoopImpl { if (nameList.isEmpty()) break //todo 可自定义执行任务 - Demo.test2(nameList[0]) +// Demo.test2(nameList[0]) } } return true 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 6c1ed9c..7c88090 100644 --- a/app/src/main/java/org/yameida/worktool/service/WeworkOperationImpl.kt +++ b/app/src/main/java/org/yameida/worktool/service/WeworkOperationImpl.kt @@ -3,13 +3,14 @@ package org.yameida.worktool.service import android.view.accessibility.AccessibilityNodeInfo import org.yameida.worktool.Constant import org.yameida.worktool.model.WeworkMessageBean -import org.yameida.worktool.utils.AccessibilityUtil -import org.yameida.worktool.utils.Views -import org.yameida.worktool.utils.WeworkRoomUtil -import org.yameida.worktool.utils.WeworkTextUtil import com.github.yoojia.qrcode.qrcode.QRCodeDecoder import com.blankj.utilcode.util.* +import com.lzy.okgo.OkGo +import org.yameida.worktool.utils.* +import java.io.File import java.lang.Exception +import java.text.SimpleDateFormat +import java.util.* /** @@ -24,21 +25,30 @@ object WeworkOperationImpl { * @param at 要at的昵称 * @see WeworkMessageBean.TEXT_TYPE */ - fun sendMessage(titleList: List, receivedContent: String?, at: String? = null): Boolean { + fun sendMessage( + titleList: List, + receivedContent: String?, + at: String? = null, + atList: List? = null + ): Boolean { if (receivedContent.isNullOrEmpty()) { LogUtils.d("未发现发送内容") return false } for (title in titleList) { if (WeworkRoomUtil.intoRoom(title)) { - sendChatMessage(receivedContent, at = at) - LogUtils.d("$title: 发送成功") + if (sendChatMessage(receivedContent, at = at, atList = atList)) { + LogUtils.d("$title: 发送成功") + return true + } else { + LogUtils.d("$title: 发送失败") + } } else { LogUtils.d("$title: 发送失败") error("$title: 发送失败 $receivedContent") } } - return true + return false } /** @@ -134,11 +144,15 @@ object WeworkOperationImpl { ) { LogUtils.d("开始转发") sleep(1000) - relaySelectTarget(nameList, extraText) + if (relaySelectTarget(nameList, extraText)) { + LogUtils.d("$title: 转发成功") + } else { + LogUtils.d("$title: 转发失败") + error("$title: 转发失败 $originalContent") + } } - LogUtils.d("$title: 转发成功") } else { - LogUtils.d("$title: 转发失败") + LogUtils.d("$title: 转发失败 未找到房间") error("$title: 转发失败 $originalContent") } } @@ -165,11 +179,13 @@ object WeworkOperationImpl { * @param groupName 修改群名称 * @param selectList 添加群成员名称列表 选填 * @param groupAnnouncement 修改群公告 选填 + * @param groupRemark 修改群备注 选填 */ fun initGroup( groupName: String, selectList: List?, - groupAnnouncement: String? + groupAnnouncement: String?, + groupRemark: String? ): Boolean { if (!WeworkRoomUtil.isGroupExists(groupName)) { if (!createGroup() || !groupRename(groupName) || !groupAddMember(selectList) @@ -180,8 +196,10 @@ object WeworkOperationImpl { || !groupChangeAnnouncement(groupAnnouncement) ) return false } - //TODO 暂移除获取群二维码 -// getGroupQrcode(groupName) + if (groupRemark != null) { + groupChangeRemark(groupRemark) + } +// getGroupQrcode(groupName, groupRemark) return true } @@ -191,6 +209,7 @@ object WeworkOperationImpl { * @param groupName 待修改的群 * @param newGroupName 修改群名 选填 * @param newGroupAnnouncement 修改群公告 选填 + * @param groupRemark 修改群备注 选填 * @param selectList 添加群成员名称列表/拉人 选填 * @param showMessageHistory 拉人是否附带历史记录 选填 * @param removeList 移除群成员名称列表/踢人 选填 @@ -199,6 +218,7 @@ object WeworkOperationImpl { groupName: String, newGroupName: String?, newGroupAnnouncement: String?, + groupRemark: String?, selectList: List?, showMessageHistory: Boolean = false, removeList: List? @@ -216,6 +236,9 @@ object WeworkOperationImpl { if (newGroupAnnouncement != null) { groupChangeAnnouncement(newGroupAnnouncement) } + if (groupRemark != null) { + groupChangeRemark(groupRemark) + } backPress() return true } @@ -249,10 +272,13 @@ object WeworkOperationImpl { AccessibilityUtil.performClick(shareFileButton) val shareToWorkButton = AccessibilityUtil.findOneByText(getRoot(true), "发送给同事") AccessibilityUtil.performClick(shareToWorkButton) - relaySelectTarget(titleList, extraText) - val stayButton = AccessibilityUtil.findOneByText(getRoot(), "留在企业微信") - AccessibilityUtil.performClick(stayButton) - return true + if (relaySelectTarget(titleList, extraText)) { + val stayButton = AccessibilityUtil.findOneByText(getRoot(), "留在企业微信") + AccessibilityUtil.performClick(stayButton) + return true + } else { + LogUtils.e("微盘文件转发失败: $objectName") + } } else { LogUtils.e("微盘未搜索到相关图片: $objectName") } @@ -290,8 +316,11 @@ object WeworkOperationImpl { AccessibilityUtil.performClick(imageViewList[1]) val shareFileButton = AccessibilityUtil.findOneByDesc(getRoot(), "转发") AccessibilityUtil.performClick(shareFileButton) - relaySelectTarget(titleList, extraText) - return true + if (relaySelectTarget(titleList, extraText)) { + return true + } else { + LogUtils.e("微盘文件转发失败: $objectName") + } } else { LogUtils.e("微盘未搜索到相关文件: $objectName") } @@ -370,8 +399,11 @@ object WeworkOperationImpl { AccessibilityUtil.performClick(imageViewList[1]) val shareFileButton = AccessibilityUtil.findOneByDesc(getRoot(), "转发") AccessibilityUtil.performClick(shareFileButton) - relaySelectTarget(titleList, extraText) - return true + if (relaySelectTarget(titleList, extraText)) { + return true + } else { + LogUtils.e("微盘文件转发失败: $objectName") + } } else { LogUtils.e("文档未搜索到相关文件: $objectName") } @@ -381,6 +413,52 @@ object WeworkOperationImpl { return false } + /** + * 推送文件(网络图片视频和文件等) + * @see WeworkMessageBean.PUSH_FILE + * @param titleList 待发送姓名列表 + * @param objectName 文件名称 + * @param fileUrl 文件网络地址 + * @param fileType 文件类型 + * @param extraText 附加留言 可选 + */ + fun pushFile( + titleList: List, + objectName: String, + fileUrl: String, + fileType: String, + extraText: String? = null + ): Boolean { + LogUtils.i("下载开始 $fileUrl") + val execute = OkGo.get(fileUrl).execute() + LogUtils.i("下载成功 $fileUrl") + val body = execute.body() + if (body != null) { + val df = SimpleDateFormat("yyyy-MM-dd") + val filePath = "${Utils.getApp().getExternalFilesDir("share")}/${df.format(Date())}/$objectName" + val newFile = File(filePath) + val create = FileUtils.createFileByDeleteOldFile(newFile) + if (create && newFile.canWrite()) { + newFile.writeBytes(body.bytes()) + LogUtils.i("文件存储本地成功 $filePath") + ShareUtil.share("${if (fileType.isBlank()) "*" else fileType}/*", newFile) + val shareToWorkButton = AccessibilityUtil.findOneByText(getRoot(true), "发送给同事") + AccessibilityUtil.performClick(shareToWorkButton) + if (relaySelectTarget(titleList, extraText)) { + val stayButton = AccessibilityUtil.findOneByText(getRoot(), "留在企业微信") + AccessibilityUtil.performClick(stayButton) + return true + } else { + LogUtils.e("文件转发失败: $objectName") + } + } else { + LogUtils.e("文件存储本地失败 $filePath") + error("文件存储本地失败 $filePath") + } + } + return false + } + /** * 手机号添加好友 * @see WeworkMessageBean.ADD_FRIEND_BY_PHONE @@ -888,39 +966,62 @@ object WeworkOperationImpl { return true } + /** + * 修改群备注 + * 注:首次为发布 后续为编辑 + * 注2:外部群为edittext 内部群为webview(只能追加文本) + */ + private fun groupChangeRemark(groupRemark: String? = null): Boolean { + if (groupRemark == null) return true + if (WeworkRoomUtil.intoGroupManager()) { + val textView = AccessibilityUtil.findOneByText(getRoot(), "备注", exact = true) + if (textView != null) { + AccessibilityUtil.performClick(textView) + if (AccessibilityUtil.findTextInput(getRoot(), groupRemark)) { + LogUtils.d("群备注修改: $groupRemark") + if (AccessibilityUtil.findTextAndClick(getRoot(), "确定")) { + return true + } else { + LogUtils.e("无法进行群备注发布: ") + } + } else { + LogUtils.e("无法进行群备注修改: ") + } + } else { + LogUtils.e("未找到群公告按钮") + } + } else { + LogUtils.e("进入群管理页失败") + } + return false + } + /** * 发送消息+@at */ - private fun sendChatMessage(text: String, at: String? = null, reply: Boolean? = false) { + private fun sendChatMessage(text: String, at: String? = null, atList: List? = null, reply: Boolean? = false): Boolean { val voiceFlag = AccessibilityUtil.findOnceByText(getRoot(), "按住 说话", "按住说话", exact = true) if (voiceFlag != null) { AccessibilityUtil.performClickWithSon(AccessibilityUtil.findFrontNode(voiceFlag)) } var atFailed = false - if (!at.isNullOrEmpty()) { - AccessibilityUtil.findTextInput(getRoot(), "@") - val atFlag = AccessibilityUtil.findOneByText(getRoot(), "选择提醒的人", timeout = 2000, exact = true) - if (atFlag != null) { - val rv = AccessibilityUtil.findOneByClazz(getRoot(), Views.RecyclerView) - if (rv != null) { - AccessibilityUtil.findTextInput(getRoot(), at) - val atNode = - AccessibilityUtil.findOneByText(rv, at, root = false, timeout = 2000) - if (atNode != null) { - AccessibilityUtil.performClick(atNode) - } else { - LogUtils.e("未找到at人: $at") - atFailed = true - backPress() - } - sleep(Constant.POP_WINDOW_INTERVAL) + val atList = if (!at.isNullOrEmpty()) listOf(at) else atList + if (!atList.isNullOrEmpty()) { + atList.forEachIndexed { index, at -> + if (index == 0) { + AccessibilityUtil.findTextInput(getRoot(), "@") } else { - val searchFlag = AccessibilityUtil.findOnceByText(getRoot(), "搜索", exact = true) - val list = AccessibilityUtil.findBackNode(searchFlag, minChildCount = 2) - if (list != null) { + AccessibilityUtil.sendTextForEditText(Utils.getApp(), + AccessibilityUtil.findOnceByClazz(getRoot(), Views.EditText), "@" + ) + } + val atFlag = AccessibilityUtil.findOneByText(getRoot(), "选择提醒的人", timeout = 2000, exact = true) + if (atFlag != null) { + val rv = AccessibilityUtil.findOneByClazz(getRoot(), Views.RecyclerView) + if (rv != null) { AccessibilityUtil.findTextInput(getRoot(), at) val atNode = - AccessibilityUtil.findOneByText(list, at, root = false, timeout = 2000) + AccessibilityUtil.findOneByText(rv, at, root = false, timeout = 2000) if (atNode != null) { AccessibilityUtil.performClick(atNode) } else { @@ -929,19 +1030,37 @@ object WeworkOperationImpl { backPress() } sleep(Constant.POP_WINDOW_INTERVAL) + } else { + val searchFlag = AccessibilityUtil.findOnceByText(getRoot(), "搜索", exact = true) + val list = AccessibilityUtil.findBackNode(searchFlag, minChildCount = 2) + if (list != null) { + AccessibilityUtil.findTextInput(getRoot(), at) + val atNode = + AccessibilityUtil.findOneByText(list, at, root = false, timeout = 2000) + if (atNode != null) { + AccessibilityUtil.performClick(atNode) + } else { + LogUtils.e("未找到at人: $at") + atFailed = true + backPress() + } + sleep(Constant.POP_WINDOW_INTERVAL) + } } } } } - val content = if (atFailed) "@$at $text" else "$text" - val append = (reply == true) || (!at.isNullOrEmpty() && !atFailed) + val content = if (atFailed) "@${atList?.joinToString()} $text" else text + val append = (reply == true) || (!atList.isNullOrEmpty() && !atFailed) if (AccessibilityUtil.findTextInput(getRoot(), content, append = append)) { + AccessibilityUtil.findOneByText(getRoot(), "发送", exact = true, timeout = 2000) val sendButton = AccessibilityUtil.findAllByClazz(getRoot(), Views.Button) .firstOrNull { it.text == "发送" } if (sendButton != null) { LogUtils.d("发送消息: \n$content") log("发送消息: \n$content") AccessibilityUtil.performClick(sendButton) + return true } else { LogUtils.e("未找到发送按钮") error("未找到发送按钮") @@ -950,6 +1069,7 @@ object WeworkOperationImpl { LogUtils.e("未找到输入框") error("未找到输入框") } + return false } /** @@ -1015,8 +1135,9 @@ object WeworkOperationImpl { /** * 获取群二维码并上传后台 */ - fun getGroupQrcode(groupName: String): Boolean { - if (WeworkRoomUtil.intoRoom(groupName) && WeworkRoomUtil.intoGroupManager()) { + fun getGroupQrcode(groupName: String, groupRemark: String?): Boolean { + if (AccessibilityUtil.findOneByText(getRoot(), "全部群成员", "微信用户创建", timeout = Constant.CHANGE_PAGE_INTERVAL) != null || + (WeworkRoomUtil.intoRoom(groupName) && WeworkRoomUtil.intoGroupManager())) { val tvList = AccessibilityUtil.findAllOnceByClazz(getRoot(), Views.TextView) tvList.forEachIndexed { index, tv -> if (tv.text != null && tv.text.contains("微信用户创建")) { @@ -1054,6 +1175,7 @@ object WeworkOperationImpl { val weworkMessageBean = WeworkMessageBean() weworkMessageBean.type = WeworkMessageBean.GET_GROUP_QRCODE weworkMessageBean.groupName = groupName + weworkMessageBean.groupRemark = groupRemark weworkMessageBean.qrcode = qrcode WeworkController.weworkService.webSocketManager.send( weworkMessageBean diff --git a/app/src/main/java/org/yameida/worktool/service/WeworkService.kt b/app/src/main/java/org/yameida/worktool/service/WeworkService.kt index 4f8eea6..a7abb9e 100644 --- a/app/src/main/java/org/yameida/worktool/service/WeworkService.kt +++ b/app/src/main/java/org/yameida/worktool/service/WeworkService.kt @@ -71,12 +71,14 @@ class WeworkService : AccessibilityService() { override fun onDestroy() { super.onDestroy() LogUtils.i("onDestroy") + //关闭自动回复 + WeworkController.enableLoopRunning = false //隐藏软键盘模式 softKeyboardController.showMode = SHOW_MODE_AUTO webSocketManager.close(1000, "service Destroy") } - class EchoWebSocketListener() : WebSocketListener() { + inner class EchoWebSocketListener : WebSocketListener() { private val TAG = "WeworkService.EchoWebSocketListener" private lateinit var socket: WebSocket override fun onOpen(webSocket: WebSocket, response: Response) { @@ -86,6 +88,8 @@ class WeworkService : AccessibilityService() { val appVersion = SPUtils.getInstance().getString("appVersion", "") val workVersion= SPUtils.getInstance().getString("workVersion", "") log("链接建立: $robotId appVersion: $appVersion workVersion: $workVersion") + LogUtils.i("设置自动跳转企业微信") + sendBroadcast(true) } override fun onMessage(webSocket: WebSocket, text: String) { @@ -102,18 +106,27 @@ class WeworkService : AccessibilityService() { super.onClosed(webSocket, code, reason) //服务器关闭后 Log.e(TAG, "链接关闭 $reason") + sendBroadcast(false) } override fun onClosing(webSocket: WebSocket, code: Int, reason: String) { super.onClosing(webSocket, code, reason) socket.close(code, reason) Log.e(TAG, "服务端关闭连接 $code: $reason") + sendBroadcast(false) } override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) { //服务器中断 - Log.e(TAG, "链接错误: " + t.toString() + response.toString()) + sendBroadcast(false) + } + + private fun sendBroadcast(switch: Boolean) { + sendBroadcast(Intent(Constant.WEWORK_NOTIFY).apply { + putExtra("type", "openWs") + putExtra("switch", switch) + }) } } } \ No newline at end of file diff --git a/app/src/main/java/org/yameida/worktool/utils/AccessibilityUtil.kt b/app/src/main/java/org/yameida/worktool/utils/AccessibilityUtil.kt index c3a8f4a..dd3669d 100644 --- a/app/src/main/java/org/yameida/worktool/utils/AccessibilityUtil.kt +++ b/app/src/main/java/org/yameida/worktool/utils/AccessibilityUtil.kt @@ -19,6 +19,10 @@ import java.lang.Exception import java.lang.Thread.sleep import com.blankj.utilcode.util.ScreenUtils import org.yameida.worktool.service.WeworkController +import android.content.ClipData +import android.content.ClipboardManager +import android.content.Context + /** * 1.查询类 @@ -56,6 +60,17 @@ object AccessibilityUtil { private const val SHORT_INTERVAL = 150L private const val SCROLL_INTERVAL = 500L + //编辑EditView(粘贴 不推荐) + fun sendTextForEditText(context: Context, nodeInfo: AccessibilityNodeInfo?, text: String): Boolean { + val nodeInfo: AccessibilityNodeInfo = nodeInfo ?: return false + val clipboard: ClipboardManager = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + val clip = ClipData.newPlainText("text", text) + clipboard.setPrimaryClip(clip) + nodeInfo.performAction(AccessibilityNodeInfo.ACTION_FOCUS) + nodeInfo.performAction(AccessibilityNodeInfo.ACTION_PASTE) + return true + } + //编辑EditView(非粘贴 推荐) fun editTextInput(nodeInfo: AccessibilityNodeInfo?, text: String): Boolean { val nodeInfo: AccessibilityNodeInfo = nodeInfo ?: return false diff --git a/app/src/main/java/org/yameida/worktool/utils/GenericFileProvider.kt b/app/src/main/java/org/yameida/worktool/utils/GenericFileProvider.kt new file mode 100644 index 0000000..ee31046 --- /dev/null +++ b/app/src/main/java/org/yameida/worktool/utils/GenericFileProvider.kt @@ -0,0 +1,8 @@ +package org.yameida.worktool.utils + +import androidx.core.content.FileProvider + +/** + * Created by Gallon on 2019/7/8. + */ +class GenericFileProvider: FileProvider() \ No newline at end of file diff --git a/app/src/main/java/org/yameida/worktool/utils/PermissionHelper.kt b/app/src/main/java/org/yameida/worktool/utils/PermissionHelper.kt new file mode 100644 index 0000000..92cb395 --- /dev/null +++ b/app/src/main/java/org/yameida/worktool/utils/PermissionHelper.kt @@ -0,0 +1,51 @@ +package org.yameida.worktool.utils + +import android.provider.Settings +import android.text.TextUtils +import com.blankj.utilcode.util.LogUtils +import com.blankj.utilcode.util.Utils +import org.yameida.worktool.service.WeworkService + +/** + * 无障碍服务开启辅助类 + */ +object PermissionHelper { + + fun isAccessibilitySettingOn(): Boolean { + val context = Utils.getApp() + var enable = 0 + val canonicalName = WeworkService::class.java.canonicalName ?: "" + val serviceName = context.packageName + "/" + canonicalName + val serviceShortName = context.packageName + "/" + canonicalName.replace(context.packageName, "") + LogUtils.i("isAccessibilitySettingOn: $serviceName $serviceShortName") + try { + enable = Settings.Secure.getInt( + context.contentResolver, + Settings.Secure.ACCESSIBILITY_ENABLED, + 0 + ) + } catch (e: Exception) { + e.printStackTrace() + } + if (enable == 1) { + val stringSplitter = TextUtils.SimpleStringSplitter(':') + val settingVal = Settings.Secure.getString( + context.contentResolver, + Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES + ) + if (settingVal != null) { + stringSplitter.setString(settingVal) + while (stringSplitter.hasNext()) { + val accessibilityService = stringSplitter.next() + if (accessibilityService == serviceName || accessibilityService == serviceShortName) { + LogUtils.i("isAccessibilitySettingOn: true") + return true + } + } + } + } + LogUtils.i("isAccessibilitySettingOn: false") + return false + } + +} \ No newline at end of file diff --git a/app/src/main/java/org/yameida/worktool/utils/PermissionPageManagement.java b/app/src/main/java/org/yameida/worktool/utils/PermissionPageManagement.java new file mode 100644 index 0000000..b02819e --- /dev/null +++ b/app/src/main/java/org/yameida/worktool/utils/PermissionPageManagement.java @@ -0,0 +1,220 @@ +package org.yameida.worktool.utils; + +import android.app.Activity; +import android.content.ComponentName; +import android.content.Intent; +import android.net.Uri; +import android.os.Build; +import android.provider.Settings; +import android.util.Log; + +/** + * 跳转到APP的权限配置页 + */ +public class PermissionPageManagement { + + private static final String TAG = "JumpPermissionManagement"; + + /** + * Build.MANUFACTURER + */ + private static final String MANUFACTURER_HUAWEI = "HUAWEI";//华为 + private static final String MANUFACTURER_MEIZU = "Meizu";//魅族 + private static final String MANUFACTURER_XIAOMI = "Xiaomi";//小米 + private static final String MANUFACTURER_SONY = "Sony";//索尼 + private static final String MANUFACTURER_OPPO = "OPPO";//oppo + private static final String MANUFACTURER_LG = "LG"; + private static final String MANUFACTURER_VIVO = "vivo";//vivo + private static final String MANUFACTURER_SAMSUNG = "samsung";//三星 + private static final String MANUFACTURER_ZTE = "ZTE";//中兴 + private static final String MANUFACTURER_YULONG = "YuLong";//酷派 + private static final String MANUFACTURER_LENOVO = "LENOVO";//联想 + + /** + * 此函数可以自己定义 + * @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; + } + } + + public static void Huawei(Activity activity) { + try { + Intent intent = new Intent(); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra("packageName", activity.getApplicationInfo().packageName); + ComponentName comp = new ComponentName("com.huawei.systemmanager", "com.huawei.permissionmanager.ui.MainActivity"); + intent.setComponent(comp); + activity.startActivity(intent); + } catch (Exception e) { + e.printStackTrace(); + goIntentSetting(activity); + } + } + + public static void Meizu(Activity activity) { + try { + Intent intent = new Intent("com.meizu.safe.security.SHOW_APPSEC"); + intent.addCategory(Intent.CATEGORY_DEFAULT); + intent.putExtra("packageName", activity.getPackageName()); + activity.startActivity(intent); + } catch (Exception e) { + e.printStackTrace(); + goIntentSetting(activity); + } + } + + public static void Xiaomi(Activity activity) { + try { + Intent intent = new Intent("miui.intent.action.APP_PERM_EDITOR"); + intent.putExtra("extra_pkgname", activity.getPackageName()); + ComponentName componentName = new ComponentName("com.miui.securitycenter", "com.miui.permcenter.permissions.PermissionsEditorActivity"); + intent.setComponent(componentName); + activity.startActivity(intent); + } catch (Exception e) { + e.printStackTrace(); + goIntentSetting(activity); + } + } + + public static void Sony(Activity activity) { + try { + Intent intent = new Intent(); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra("packageName", activity.getPackageName()); + ComponentName comp = new ComponentName("com.sonymobile.cta", "com.sonymobile.cta.SomcCTAMainActivity"); + intent.setComponent(comp); + activity.startActivity(intent); + } catch (Exception e) { + e.printStackTrace(); + goIntentSetting(activity); + } + } + + public static void OPPO(Activity activity) { + try { + Intent intent = new Intent(); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra("packageName", activity.getPackageName()); + // ComponentName comp = new ComponentName("com.color.safecenter", "com.color.safecenter.permission.PermissionManagerActivity"); + ComponentName comp = new ComponentName("com.coloros.securitypermission", "com.coloros.securitypermission.permission.PermissionAppAllPermissionActivity");//R11t 7.1.1 os-v3.2 + intent.setComponent(comp); + activity.startActivity(intent); + } catch (Exception e) { + e.printStackTrace(); + goIntentSetting(activity); + } + } + + public static void VIVO(Activity activity) { + Intent localIntent; + if (((Build.MODEL.contains("Y85")) && (!Build.MODEL.contains("Y85A"))) || (Build.MODEL.contains("vivo Y53L"))) { + localIntent = new Intent(); + localIntent.setClassName("com.vivo.permissionmanager", "com.vivo.permissionmanager.activity.PurviewTabActivity"); + localIntent.putExtra("packagename", activity.getPackageName()); + localIntent.putExtra("tabId", "1"); + activity.startActivity(localIntent); + } else { + localIntent = new Intent(); + localIntent.setClassName("com.vivo.permissionmanager", "com.vivo.permissionmanager.activity.SoftPermissionDetailActivity"); + localIntent.setAction("secure.intent.action.softPermissionDetail"); + localIntent.putExtra("packagename", activity.getPackageName()); + activity.startActivity(localIntent); + } + } + + public static void LG(Activity activity) { + try { + Intent intent = new Intent("android.intent.action.MAIN"); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra("packageName", activity.getPackageName()); + ComponentName comp = new ComponentName("com.android.settings", "com.android.settings.Settings$AccessLockSummaryActivity"); + intent.setComponent(comp); + activity.startActivity(intent); + } catch (Exception e) { + e.printStackTrace(); + goIntentSetting(activity); + } + } + + /** + * 只能打开到自带安全软件 + * @param activity + */ + public static void _360(Activity activity) { + Intent intent = new Intent("android.intent.action.MAIN"); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra("packageName", activity.getPackageName()); + ComponentName comp = new ComponentName("com.qihoo360.mobilesafe", "com.qihoo360.mobilesafe.ui.index.AppEnterActivity"); + intent.setComponent(comp); + activity.startActivity(intent); + } + + /** + * 应用信息界面 + * @param activity + */ + public static void ApplicationInfo(Activity activity){ + Intent localIntent = new Intent(); + localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + if (Build.VERSION.SDK_INT >= 9) { + localIntent.setAction("android.settings.APPLICATION_DETAILS_SETTINGS"); + localIntent.setData(Uri.fromParts("package", activity.getPackageName(), null)); + } else if (Build.VERSION.SDK_INT <= 8) { + localIntent.setAction(Intent.ACTION_VIEW); + localIntent.setClassName("com.android.settings", "com.android.settings.InstalledAppDetails"); + localIntent.putExtra("com.android.settings.ApplicationPkgName", activity.getPackageName()); + } + activity.startActivity(localIntent); + } + + /** + * 系统设置界面 + * @param activity + */ + public static void SystemConfig(Activity activity) { + Intent intent = new Intent(Settings.ACTION_SETTINGS); + activity.startActivity(intent); + } + + /** + * 默认打开应用详细页 + */ + private static void goIntentSetting(Activity pActivity) { + Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); + Uri uri = Uri.fromParts("package", pActivity.getPackageName(), null); + intent.setData(uri); + try { + pActivity.startActivity(intent); + } catch (Exception e) { + e.printStackTrace(); + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/org/yameida/worktool/utils/ShareUtil.kt b/app/src/main/java/org/yameida/worktool/utils/ShareUtil.kt new file mode 100644 index 0000000..30eee7d --- /dev/null +++ b/app/src/main/java/org/yameida/worktool/utils/ShareUtil.kt @@ -0,0 +1,68 @@ +package org.yameida.worktool.utils + +import android.content.Intent +import android.provider.Settings +import androidx.core.content.FileProvider +import com.blankj.utilcode.util.LogUtils +import com.blankj.utilcode.util.Utils +import java.io.File + +/** + * 文件分享工具类 + */ +object ShareUtil { + + /** + * Share Text + */ + const val TEXT = "text/plain" + + /** + * Share Image + */ + const val IMAGE = "image/*" + + /** + * Share Audio + */ + const val AUDIO = "audio/*" + + /** + * Share Video + */ + const val VIDEO = "video/*" + + /** + * Share File + */ + const val File = "*/*" + + /** + * 文件分享 需要先授权显示悬浮窗 + */ + fun share(type: String, path: String): Boolean { + return share(type, File(path)) + } + + /** + * 文件分享 需要先授权显示悬浮窗 + */ + fun share(type: String, file: File): Boolean { + val app = Utils.getApp() + if (!Settings.canDrawOverlays(app)) { + LogUtils.e("文件分享失败 没有悬浮窗权限~") + return false + } + val intent = Intent().apply { + action = Intent.ACTION_SEND + flags = Intent.FLAG_ACTIVITY_NEW_TASK + this.type = type + val fileURI = FileProvider.getUriForFile(app, app.packageName + ".fileprovider", file) + putExtra(Intent.EXTRA_STREAM, fileURI) + } + app.startActivity(Intent.createChooser(intent, "WorkTool文件分享").apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK }) + LogUtils.e("分享了 $type ${file.absolutePath}") + return true + } + +} \ No newline at end of file diff --git a/app/src/main/java/org/yameida/worktool/utils/WebSocketManager.java b/app/src/main/java/org/yameida/worktool/utils/WebSocketManager.java index 9f38738..3725403 100644 --- a/app/src/main/java/org/yameida/worktool/utils/WebSocketManager.java +++ b/app/src/main/java/org/yameida/worktool/utils/WebSocketManager.java @@ -4,6 +4,7 @@ import android.util.Log; import com.blankj.utilcode.util.GsonUtils; import com.blankj.utilcode.util.LogUtils; +import com.hjq.toast.ToastUtils; import org.yameida.worktool.model.WeworkMessageBean; import org.yameida.worktool.model.WeworkMessageListBean; @@ -24,15 +25,11 @@ import okhttp3.WebSocketListener; public class WebSocketManager { - public static final String HEARTBEAT = "{" + - "\"type\": " + WeworkMessageBean.HEART_BEAT + - ",\"hearBeat\": \"心跳检测\"" + - "}"; + public static final String HEARTBEAT = "{\"type\":" + WeworkMessageBean.HEART_BEAT + "}"; private static final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(); public static Map webSocketManager = new ConcurrentHashMap<>(); - private static final int reconnectTimes = 7; private static final int reconnectInt = 5000; //毫秒 - private static final long heartBeatRate = 15; //秒 + private static final long heartBeatRate = 10; //秒 private Map messageIdMap = new ConcurrentHashMap<>(); private ScheduledFuture task; private WebSocket socket; @@ -52,7 +49,7 @@ public class WebSocketManager { } public void send(WeworkMessageBean msg) { - send(new WeworkMessageListBean(msg, WeworkMessageListBean.SOCKET_TYPE_MESSAGE_LIST)); + send(new WeworkMessageListBean(msg, WeworkMessageListBean.SOCKET_TYPE_MESSAGE_LIST, null)); } /** @@ -101,7 +98,6 @@ public class WebSocketManager { Log.e(url, "链接关闭"); } - public static void closeManager() { Log.e("SocketManager", "关闭Manager:"); for (Map.Entry e : webSocketManager.entrySet()) { @@ -115,10 +111,14 @@ public class WebSocketManager { connecting = true; Log.e(url, "重连"); boolean isConnect = false; + int interval = reconnectInt; while (!isConnect) { try { isConnect = connect(); - Thread.sleep(reconnectInt); + Thread.sleep(interval); + if (interval < 600000) { + interval *= 2; + } } catch (Exception e) { e.printStackTrace(); } @@ -142,6 +142,7 @@ public class WebSocketManager { if (!connecting && (socket == null || !socket.send(HEARTBEAT))) { reConnect(); } + ToastUtils.show("机器人运行中 请勿人工操作手机~"); }; //每heartBeatRate秒发一次心跳包 diff --git a/app/src/main/java/org/yameida/worktool/utils/WeworkRoomUtil.kt b/app/src/main/java/org/yameida/worktool/utils/WeworkRoomUtil.kt index 132e05d..94aedaf 100644 --- a/app/src/main/java/org/yameida/worktool/utils/WeworkRoomUtil.kt +++ b/app/src/main/java/org/yameida/worktool/utils/WeworkRoomUtil.kt @@ -19,7 +19,7 @@ object WeworkRoomUtil { * @see WeworkMessageBean.ROOM_TYPE */ fun getRoomType(print: Boolean = true): Int { - val roomTitle = getRoomTitle() + val roomTitle = getRoomTitle(noCut = true) when { isExternalSingleChat(roomTitle) -> { LogUtils.d("ROOM_TYPE: ROOM_TYPE_EXTERNAL_CONTACT") @@ -52,7 +52,7 @@ object WeworkRoomUtil { * @see WeworkMessageBean.ROOM_TYPE_INTERNAL_GROUP * @see WeworkMessageBean.ROOM_TYPE_INTERNAL_CONTACT */ - fun getRoomTitle(print: Boolean = true): ArrayList { + fun getRoomTitle(print: Boolean = true, noCut: Boolean = false): ArrayList { val titleList = arrayListOf() //聊天消息列表 1ListView 0RecycleView xViewGroup val list = AccessibilityUtil.findOnceByClazz(getRoot(), Views.ListView) @@ -63,8 +63,8 @@ object WeworkRoomUtil { if (!textView.text.isNullOrBlank()) { val text = textView.text.toString() titleList.add(text.replace("\\(\\d+\\)$".toRegex(), "")) - if (text.contains("\\(\\d+\\)$".toRegex())) { - titleList.add(text) + if (noCut && text.contains("\\(\\d+\\)$".toRegex())) { + titleList.add(text) } } } diff --git a/app/src/main/res/layout/activity_listen.xml b/app/src/main/res/layout/activity_listen.xml index deebc05..2db304b 100644 --- a/app/src/main/res/layout/activity_listen.xml +++ b/app/src/main/res/layout/activity_listen.xml @@ -301,6 +301,41 @@ + + + + + + + + + + + +