diff --git a/app/build.gradle b/app/build.gradle index 7d4d1d8..5c572cf 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -31,35 +31,12 @@ android { } dependencies { - implementation fileTree(dir: "libs", include: ["*.jar"]) - implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" - implementation 'androidx.core:core-ktx:1.3.2' - implementation 'androidx.appcompat:appcompat:1.3.1' - implementation 'com.google.android.material:material:1.4.0' - - //工具集 - 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' - //网络 - implementation 'com.github.bumptech.glide:okhttp3-integration:4.9.0' - - implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" - implementation 'androidx.core:core-ktx:1.3.2' + implementation project(':baselibrary') + implementation project(':floatwindow') + implementation fileTree(dir: "libs", include: ["*.jar", "*.aar"]) //友盟统计SDK implementation 'com.umeng.umsdk:common:9.4.7'// 必选 implementation 'com.umeng.umsdk:asms:1.4.1'// 必选 implementation 'com.umeng.umsdk:apm:1.5.2' // 错误分析升级为独立SDK,看crash数据请一定集成,可选 - - //自动更新 - implementation 'com.teprinciple:updateapputilsx:2.3.0' - //ok - implementation 'com.lzy.net:okgo:3.0.4' - //qrcode - implementation 'com.github.yoojia:next-qrcode:2.0-2' - //QMUI - implementation 'com.qmuiteam:qmui:2.0.0-alpha10' } \ No newline at end of file diff --git a/app/libs/lib_wwapi-2.0.12.11.aar b/app/libs/lib_wwapi-2.0.12.11.aar new file mode 100644 index 0000000..5f9a9cc Binary files /dev/null and b/app/libs/lib_wwapi-2.0.12.11.aar differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 75987d3..86fbf71 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -24,6 +24,7 @@ @@ -44,7 +46,24 @@ + + + + + + + SPUtils.getInstance().put("not_show_float_guide", isChecked) + } + + val alphaAnimation = AlphaAnimation(0.2F, 1F).apply { + duration = 800 + repeatCount = Animation.INFINITE + repeatMode = Animation.REVERSE + } + iv_over_finger.startAnimation(alphaAnimation) + } + + override fun onResume() { + super.onResume() + val canDrawOverlays = Settings.canDrawOverlays(Utils.getApp()) + LogUtils.d("Settings.canDrawOverlays: $canDrawOverlays") + if (canDrawOverlays) { + finish() + } + } + +} \ No newline at end of file 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 5987930..38c4e30 100644 --- a/app/src/main/java/org/yameida/worktool/activity/ListenActivity.kt +++ b/app/src/main/java/org/yameida/worktool/activity/ListenActivity.kt @@ -4,14 +4,12 @@ import android.os.Bundle import android.provider.Settings import android.view.WindowManager import android.widget.CompoundButton -import android.widget.Switch import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import com.blankj.utilcode.util.* import com.umeng.analytics.MobclickAgent import kotlinx.android.synthetic.main.activity_listen.* import org.yameida.worktool.* -import org.yameida.worktool.service.WeworkService import android.content.* import android.text.InputType import com.google.android.material.dialog.MaterialAlertDialogBuilder @@ -28,7 +26,9 @@ class ListenActivity : AppCompatActivity() { * @param type 0=游客登录 */ fun enterActivity(context: Context, type: Int) { + LogUtils.d("ListenActivity.enterActivity type: $type") context.startActivity(Intent(context, ListenActivity::class.java).apply { + this.flags = Intent.FLAG_ACTIVITY_NEW_TASK putExtra("type", type) }) } @@ -56,10 +56,7 @@ class ListenActivity : AppCompatActivity() { override fun onResume() { super.onResume() sw_overlay.isChecked = PermissionUtils.isGrantedDrawOverlays() - freshOpenServiceSwitch( - WeworkService::class.java, - sw_accessibility - ) + freshOpenServiceSwitch() if (needToWork) { needToWork = false goToWork() @@ -67,28 +64,19 @@ class ListenActivity : AppCompatActivity() { } private fun initView() { - et_channel.setText(SPUtils.getInstance().getString(Constant.LISTEN_CHANNEL_ID)) + iv_settings.setOnClickListener { + SettingsActivity.enterActivity(this) + } + et_channel.setText(Constant.robotId) bt_save.setOnClickListener { val channel = et_channel.text.toString().trim() - SPUtils.getInstance().put(Constant.LISTEN_CHANNEL_ID, channel) + Constant.robotId = channel ToastUtils.showLong("保存成功") sendBroadcast(Intent(Constant.WEWORK_NOTIFY).apply { putExtra("type", "modify_channel") }) MobclickAgent.onProfileSignIn(channel) } - sw_encrypt.isChecked = Constant.encryptType == 1 - sw_encrypt.setOnCheckedChangeListener(CompoundButton.OnCheckedChangeListener { buttonView, isChecked -> - LogUtils.i("sw_encrypt onCheckedChanged: $isChecked") - Constant.encryptType = if (isChecked) 1 else 0 - SPUtils.getInstance().put("encryptType", Constant.encryptType) - }) - sw_auto_reply.isChecked = Constant.autoReply == 1 - sw_auto_reply.setOnCheckedChangeListener(CompoundButton.OnCheckedChangeListener { buttonView, isChecked -> - LogUtils.i("sw_auto_reply onCheckedChanged: $isChecked") - Constant.autoReply = if (isChecked) 1 else 0 - SPUtils.getInstance().put("autoReply", Constant.autoReply) - }) tv_host.text = Constant.host tv_host.setOnClickListener { showSelectHostDialog() @@ -134,7 +122,7 @@ class ListenActivity : AppCompatActivity() { sw_accessibility.setOnCheckedChangeListener(CompoundButton.OnCheckedChangeListener { buttonView, isChecked -> LogUtils.i("sw_accessibility onCheckedChanged: $isChecked") if (isChecked) { - if (SPUtils.getInstance().getString(Constant.LISTEN_CHANNEL_ID).isNullOrBlank()) { + if (Constant.robotId.isBlank()) { sw_accessibility.isChecked = false ToastUtils.showLong("请先填写并保存链接号~") } else if (!PermissionHelper.isAccessibilitySettingOn()) { @@ -183,24 +171,15 @@ class ListenActivity : AppCompatActivity() { private fun openAccessibility() { val clickListener = DialogInterface.OnClickListener { dialog, which -> - freshOpenServiceSwitch( - WeworkService::class.java, - sw_accessibility - ) + freshOpenServiceSwitch() val intent = Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS) startActivity(intent) } val cancel = DialogInterface.OnCancelListener { - freshOpenServiceSwitch( - WeworkService::class.java, - sw_accessibility - ) + freshOpenServiceSwitch() } val cancelListener = DialogInterface.OnClickListener { dialog, which -> - freshOpenServiceSwitch( - WeworkService::class.java, - sw_accessibility - ) + freshOpenServiceSwitch() } val dialog: AlertDialog = AlertDialog.Builder(this) .setMessage(R.string.tips) @@ -211,20 +190,8 @@ class ListenActivity : AppCompatActivity() { dialog.show() } - private fun freshOpenServiceSwitch(clazz: Class<*>, s: Switch) { - if (PermissionHelper.isAccessibilitySettingOn()) { - s.isChecked = true - when (s.id) { - R.id.sw_accessibility -> { - } - } - } else { - s.isChecked = false - when (s.id) { - R.id.sw_accessibility -> { - } - } - } + private fun freshOpenServiceSwitch() { + sw_accessibility.isChecked = PermissionHelper.isAccessibilitySettingOn() } private fun showSelectHostDialog() { diff --git a/app/src/main/java/org/yameida/worktool/activity/SettingsActivity.kt b/app/src/main/java/org/yameida/worktool/activity/SettingsActivity.kt new file mode 100644 index 0000000..e00b7da --- /dev/null +++ b/app/src/main/java/org/yameida/worktool/activity/SettingsActivity.kt @@ -0,0 +1,279 @@ +package org.yameida.worktool.activity + +import android.content.Context +import android.content.DialogInterface +import android.content.Intent +import android.os.Bundle +import android.provider.Settings +import android.text.InputType +import android.view.WindowManager +import android.widget.CompoundButton +import androidx.appcompat.app.AlertDialog +import androidx.appcompat.app.AppCompatActivity +import com.blankj.utilcode.util.* +import com.lzy.okgo.OkGo +import com.lzy.okgo.callback.StringCallback +import com.lzy.okgo.model.Response +import com.qmuiteam.qmui.widget.dialog.QMUIDialog +import kotlinx.android.synthetic.main.activity_settings.* +import okhttp3.MediaType +import okhttp3.RequestBody +import org.json.JSONObject +import org.yameida.worktool.Constant +import org.yameida.worktool.R +import org.yameida.worktool.utils.* + + +/** + * 登录页 + */ +class SettingsActivity : AppCompatActivity() { + + companion object { + fun enterActivity(context: Context) { + LogUtils.d("SettingsActivity.enterActivity") + context.startActivity(Intent(context, SettingsActivity::class.java).apply { + this.flags = Intent.FLAG_ACTIVITY_NEW_TASK + }) + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) + setContentView(R.layout.activity_settings) + + initView() + } + + override fun onResume() { + super.onResume() + freshOpenFlow() + freshOpenMain() + } + + private fun initView() { + iv_back_left.setOnClickListener { finish() } + sw_encrypt.isChecked = Constant.encryptType == 1 + sw_encrypt.setOnCheckedChangeListener(CompoundButton.OnCheckedChangeListener { buttonView, isChecked -> + LogUtils.i("sw_encrypt onCheckedChanged: $isChecked") + Constant.encryptType = if (isChecked) 1 else 0 + SPUtils.getInstance().put("encryptType", Constant.encryptType) + }) + sw_receive.isChecked = Constant.autoReply == 1 + sw_receive.setOnCheckedChangeListener(CompoundButton.OnCheckedChangeListener { buttonView, isChecked -> + LogUtils.i("sw_receive onCheckedChanged: $isChecked") + Constant.autoReply = if (isChecked) 1 else 0 + SPUtils.getInstance().put("autoReply", Constant.autoReply) + }) + rl_reply_strategy.setOnClickListener { showReplyStrategyDialog() } + rl_qa_url.setOnClickListener { showQaUrlDialog() } + rl_donate.setOnClickListener { showDonateDialog() } + rl_share.setOnClickListener { showShareDialog() } + freshOpenFlow() + bt_open_flow.setOnClickListener { + freshOpenFlow() + if (Settings.canDrawOverlays(Utils.getApp())) { + if (!FlowPermissionHelper.canBackgroundStart(Utils.getApp())) { + ToastUtils.showLong("请同时打开后台弹出界面权限~") + PermissionPageManagement.goToSetting(this) + } + FloatWindowHelper.showWindow() + } else { + startActivityForResult(Intent(this, FloatViewGuideActivity::class.java), 0) + } + } + freshOpenMain() + bt_open_main.setOnClickListener { + freshOpenMain() + if (PermissionHelper.isAccessibilitySettingOn()) { + val intent = Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS) + startActivity(intent) + } else { + if (Constant.robotId.isBlank()) { + ToastUtils.showLong("请先填写并保存链接号~") + } else if (!PermissionHelper.isAccessibilitySettingOn()) { + openAccessibility() + } + } + } + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + freshOpenFlow() + if (Settings.canDrawOverlays(Utils.getApp())) { + if (!FlowPermissionHelper.canBackgroundStart(Utils.getApp())) { + ToastUtils.showLong("请同时打开后台弹出界面权限~") + PermissionPageManagement.goToSetting(this) + } + FloatWindowHelper.showWindow() + } + } + + private fun showReplyStrategyDialog() { + val strategyArray = arrayOf("只读消息不回调", "仅私聊和群聊@机器人回调", "私聊群聊全部回调") + QMUIDialog.CheckableDialogBuilder(this) + .setTitle("回复策略") + .addItems(strategyArray) { dialog, which -> + dialog.dismiss() + updateRobotReplyStrategy(which) + } + .setCheckedIndex(Constant.replyStrategy) + .create(R.style.QMUI_Dialog) + .show() + } + + private fun showQaUrlDialog() { + val builder = QMUIDialog.EditTextDialogBuilder(this) + builder.setTitle("消息回调地址") + .setPlaceholder("请输入回调接口地址") + .setDefaultText(Constant.qaUrl) + .setInputType(InputType.TYPE_CLASS_TEXT) + .addAction(getString(R.string.delete)) { dialog, index -> + dialog.dismiss() + updateRobotQaUrl("") + } + .addAction(getString(R.string.cancel)) { dialog, index -> dialog.dismiss() } + .addAction(getString(R.string.add)) { dialog, index -> + val text = builder.editText.text + if (text != null) { + if (text.matches("https?://[^/]+.*".toRegex())) { + dialog.dismiss() + updateRobotQaUrl(text.toString().trim()) + } else { + ToastUtils.showLong("格式异常!") + } + } else { + ToastUtils.showLong("请勿为空!") + } + } + .create(R.style.QMUI_Dialog).show() + } + + private fun showDonateDialog() { + DonateUtil.zfbDonate(this) + } + + private fun showShareDialog() { + startActivity(Intent.createChooser(Intent().apply { + action = Intent.ACTION_SEND + type = ShareUtil.TEXT + putExtra(Intent.EXTRA_TEXT, "我发现一个非常好用的企业微信机器人程序,文档地址: https://worktool.apifox.cn/ APP下载地址是: https://cdn.asrtts.cn/uploads/worktool/apk/worktool-latest.apk") + }, "分享")) + } + + private fun freshOpenFlow() { + if (Settings.canDrawOverlays(Utils.getApp())) { + if (FlowPermissionHelper.canBackgroundStart(Utils.getApp())) { + bt_open_flow.setBackgroundResource(R.drawable.comment_gray_btn) + bt_open_flow.text = "悬浮窗权限已开启" + } else { + bt_open_flow.setBackgroundResource(R.drawable.comment_red_btn) + bt_open_flow.text = "开启后台弹出界面" + } + } else { + bt_open_flow.setBackgroundResource(R.drawable.comment_red_btn) + bt_open_flow.text = "开启悬浮窗权限" + } + } + + private fun freshOpenMain() { + if (PermissionHelper.isAccessibilitySettingOn()) { + bt_open_main.setBackgroundResource(R.drawable.comment_gray_btn) + bt_open_main.text = "主功能已开启" + } else { + bt_open_main.setBackgroundResource(R.drawable.comment_red_btn) + bt_open_main.text = "开启主功能" + } + } + + /** + * 打开辅助 + */ + private fun openAccessibility() { + val clickListener = + DialogInterface.OnClickListener { dialog, which -> + freshOpenMain() + val intent = Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS) + startActivity(intent) + } + val cancel = DialogInterface.OnCancelListener { + freshOpenMain() + } + val cancelListener = DialogInterface.OnClickListener { dialog, which -> + freshOpenMain() + } + val dialog: AlertDialog = AlertDialog.Builder(this) + .setMessage(R.string.tips) + .setOnCancelListener(cancel) + .setNegativeButton("取消", cancelListener) + .setPositiveButton("确定", clickListener) + .create() + dialog.show() + } + + private fun updateRobotQaUrl(callbackUrl: String) { + try { + val json = hashMapOf() + json["robotId"] = Constant.robotId + if (callbackUrl.isEmpty()) { + json["openCallback"] = 0 + } else { + json["openCallback"] = 1 + json["callbackUrl"] = callbackUrl + } + val requestBody = RequestBody.create( + MediaType.parse("application/json;charset=UTF-8"), + GsonUtils.toJson(json) + ) + val call = object : StringCallback() { + override fun onSuccess(response: Response?) { + if (response != null && JSONObject(response.body()).getInt("code") == 200) { + Constant.qaUrl = callbackUrl + ToastUtils.showLong(if (callbackUrl.isEmpty()) "关闭成功" else "更新成功") + } else { + onError(response) + } + } + + override fun onError(response: Response?) { + ToastUtils.showLong(if (callbackUrl.isEmpty()) "关闭失败,请稍后再试~" else "更新失败,请稍后再试~") + } + } + OkGo.post(Constant.getRobotUpdateUrl()).upRequestBody(requestBody).execute(call) + } catch (e: Exception) { + throw RuntimeException(e) + } + } + + private fun updateRobotReplyStrategy(type: Int) { + try { + val json = hashMapOf() + json["robotId"] = Constant.robotId + json["replyAll"] = type + val requestBody = RequestBody.create( + MediaType.parse("application/json;charset=UTF-8"), + GsonUtils.toJson(json) + ) + val call = object : StringCallback() { + override fun onSuccess(response: Response?) { + if (response != null && JSONObject(response.body()).getInt("code") == 200) { + Constant.replyStrategy = type + ToastUtils.showLong("更新成功") + } else { + onError(response) + } + } + + override fun onError(response: Response?) { + ToastUtils.showLong("更新失败,请稍后再试~") + } + } + OkGo.post(Constant.getRobotUpdateUrl()).upRequestBody(requestBody).execute(call) + } catch (e: Exception) { + throw RuntimeException(e) + } + } +} 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 9136ca3..8ed3cdf 100644 --- a/app/src/main/java/org/yameida/worktool/model/WeworkMessageBean.java +++ b/app/src/main/java/org/yameida/worktool/model/WeworkMessageBean.java @@ -46,6 +46,7 @@ public class WeworkMessageBean { * 获取群信息 GET_GROUP_INFO * 获取好友信息 GET_FRIEND_INFO * 获取我的信息 GET_MY_INFO + * 获取最近聊天列表 GET_RECENT_LIST */ public static final int HEART_BEAT = 11; public static final int TYPE_RECEIVE_MESSAGE_LIST = 101; @@ -82,6 +83,7 @@ public class WeworkMessageBean { public static final int GET_FRIEND_INFO = 502; public static final int GET_MY_INFO = 503; public static final int GET_GROUP_QRCODE = 504; + public static final int GET_RECENT_LIST = 505; /** * roomType @@ -165,7 +167,7 @@ public class WeworkMessageBean { //转发附加留言 public String extraText; //接收消息类型 - public int textType; + public Integer textType; //群名 public String groupName; @@ -206,7 +208,7 @@ public class WeworkMessageBean { public WeworkMessageBean() {} - public WeworkMessageBean(String receivedName, String receivedContent, int type, Integer roomType, List titleList, List messageList, String log) { + public WeworkMessageBean(String receivedName, String receivedContent, Integer type, Integer roomType, List titleList, List messageList, String log) { this.type = type; this.roomType = roomType; this.titleList = titleList; @@ -217,36 +219,62 @@ public class WeworkMessageBean { } //消息类型 - public int type = 0; + public Integer type = 0; //消息列表的每条消息 public static class SubMessageBean { //0其他人 1机器人自己 2unknown(如系统消息) - public int sender = 0; + public Integer sender; //消息类型判断 仅针对sender=0 - public int textType; + public Integer textType; public List itemMessageList; public List nameList; - public SubMessageBean(int sender, int textType, List itemMessageList, List nameList) { + public SubMessageBean(Integer sender, Integer textType, List itemMessageList, List nameList) { this.sender = sender; this.textType = textType; this.itemMessageList = itemMessageList; this.nameList = nameList; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SubMessageBean that = (SubMessageBean) o; + return Objects.equals(sender, that.sender) && Objects.equals(textType, that.textType) && Objects.equals(itemMessageList, that.itemMessageList) && Objects.equals(nameList, that.nameList); + } + + @Override + public int hashCode() { + return Objects.hash(sender, textType, itemMessageList, nameList); + } } //消息列表每条消息的text推断 public static class ItemMessageBean { //0消息主体上方信息 如日期等 系统消息(拉人/撤回/外部群等居中的提示语) //2消息内容 - public int feature = 0; + public Integer feature; public String text; - public ItemMessageBean(int feature, String text) { + public ItemMessageBean(Integer feature, String text) { this.feature = feature; this.text = text; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ItemMessageBean that = (ItemMessageBean) o; + return Objects.equals(feature, that.feature) && Objects.equals(text, that.text); + } + + @Override + public int hashCode() { + return Objects.hash(feature, text); + } } //我的信息 @@ -315,7 +343,7 @@ 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(messageId, that.messageId) && 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(fileBase64, that.fileBase64) && Objects.equals(fileUrl, that.fileUrl) && Objects.equals(fileType, that.fileType); + return Objects.equals(messageId, that.messageId) && 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(textType, that.textType) && 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(showMessageHistory, that.showMessageHistory) && Objects.equals(myInfo, that.myInfo) && Objects.equals(objectName, that.objectName) && Objects.equals(qrcode, that.qrcode) && Objects.equals(friend, that.friend) && Objects.equals(fileBase64, that.fileBase64) && Objects.equals(fileUrl, that.fileUrl) && Objects.equals(fileType, that.fileType) && Objects.equals(type, that.type); } @Override 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 ecd7d8d..0fc11d0 100644 --- a/app/src/main/java/org/yameida/worktool/service/GlobalMethod.kt +++ b/app/src/main/java/org/yameida/worktool/service/GlobalMethod.kt @@ -144,12 +144,18 @@ fun backPress() { LogUtils.d("尝试点击确定/我知道了/暂不进入") AccessibilityUtil.performClick(confirm) } 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) + val stayButton = AccessibilityUtil.findOnceByText(getRoot(true), "关闭应用", "等待", exact = true) + if (stayButton != null) { + LogUtils.d("疑似ANR 尝试点击等待") + AccessibilityUtil.performClick(stayButton) + } 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) + } } } } 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 dda7f88..2f499f6 100644 --- a/app/src/main/java/org/yameida/worktool/service/MyLooper.kt +++ b/app/src/main/java/org/yameida/worktool/service/MyLooper.kt @@ -6,12 +6,14 @@ import android.os.Message import com.blankj.utilcode.util.EncryptUtils import com.blankj.utilcode.util.GsonUtils import com.blankj.utilcode.util.LogUtils +import com.blankj.utilcode.util.SPUtils import com.google.gson.reflect.TypeToken import okhttp3.WebSocket import org.yameida.worktool.Constant import org.yameida.worktool.model.ExecCallbackBean import org.yameida.worktool.model.WeworkMessageBean import org.yameida.worktool.model.WeworkMessageListBean +import org.yameida.worktool.utils.FloatWindowHelper import java.nio.charset.StandardCharsets import java.util.LinkedHashSet import kotlin.concurrent.thread @@ -21,12 +23,16 @@ object MyLooper { private var threadHandler: Handler? = null val looper = thread { - LogUtils.e("myLooper starting...") + LogUtils.i("myLooper starting...") Looper.prepare() val myLooper = Looper.myLooper() if (myLooper != null) { threadHandler = object : Handler(myLooper) { override fun handleMessage(msg: Message) { + while (FloatWindowHelper.isPause) { + LogUtils.i("主功能暂停...") + sleep(Constant.CHANGE_PAGE_INTERVAL) + } LogUtils.d("handle message: " + Thread.currentThread().name, msg) try { dealWithMessage(msg.obj as WeworkMessageBean) @@ -42,7 +48,11 @@ object MyLooper { Looper.loop() } - fun init() {} + fun init() { + LogUtils.i("init myLooper...") + SPUtils.getInstance("noTipMessage").clear() + SPUtils.getInstance("limit").clear() + } fun getInstance(): Handler { while (true) { @@ -168,6 +178,9 @@ object MyLooper { WeworkMessageBean.GET_MY_INFO -> { WeworkController.getMyInfo(message) } + WeworkMessageBean.GET_RECENT_LIST -> { + WeworkController.getRecentList(message) + } WeworkMessageBean.ROBOT_CONTROLLER_TEST -> { WeworkController.test(message) } 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 14ffb43..c2b6334 100644 --- a/app/src/main/java/org/yameida/worktool/service/WeworkController.kt +++ b/app/src/main/java/org/yameida/worktool/service/WeworkController.kt @@ -390,4 +390,14 @@ object WeworkController { return WeworkGetImpl.getMyInfo(message) } + /** + * 获取最近聊天列表 + * @see WeworkMessageBean.GET_RECENT_LIST + */ + @RequestMapping + fun getRecentList(message: WeworkMessageBean): Boolean { + LogUtils.d("getRecentList():") + return WeworkGetImpl.getRecentList(message) + } + } \ No newline at end of file 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 8be45ea..9119ce9 100644 --- a/app/src/main/java/org/yameida/worktool/service/WeworkGetImpl.kt +++ b/app/src/main/java/org/yameida/worktool/service/WeworkGetImpl.kt @@ -197,4 +197,45 @@ object WeworkGetImpl { backPress() return weworkMessageBean } + + /** + * 获取最近聊天列表 + */ + fun getRecentList(message: WeworkMessageBean): Boolean { + goHome() + AccessibilityUtil.scrollToTop(WeworkController.weworkService, getRoot()) + val list = AccessibilityUtil.findOneByClazz(getRoot(), Views.RecyclerView, Views.ListView, Views.ViewGroup) + if (list != null && list.childCount >= 2) { + val listBriefList = LinkedHashSet() + val onScrollListener = object : AccessibilityUtil.OnScrollListener() { + override fun onScroll(): Boolean { + list.refresh() + for (i in 0 until list.childCount) { + val item = list.getChild(i) + val tempList = arrayListOf() + val tvList = AccessibilityUtil.findAllOnceByClazz(item, Views.TextView).mapNotNull { it.text } + tvList.forEach { tempList.add(WeworkMessageBean.ItemMessageBean(null, it.toString())) } + listBriefList.add(WeworkMessageBean.SubMessageBean(null, null, tempList, null)) + //tvList title/time/content + if (tvList.size == 3) { + //只查看最近一周内的消息 + if (tvList[1].isNotBlank() && !tvList[1].contains("(刚刚)|(分钟前)|(上午)|(下午)|(昨天)|(星期)|(日程)|(会议)".toRegex())) { + return true + } + } + } + return false + } + } + //滚动前先获取一次 + onScrollListener.onScroll() + AccessibilityUtil.scrollToBottom(WeworkController.weworkService, getRoot(), listener = onScrollListener) + LogUtils.d("最近聊天列表", GsonUtils.toJson(listBriefList)) + val weworkMessageBean = WeworkMessageBean() + weworkMessageBean.type = WeworkMessageBean.GET_RECENT_LIST + weworkMessageBean.messageList = listBriefList.toList() + WeworkController.weworkService.webSocketManager.send(weworkMessageBean) + } + return true + } } \ No newline at end of file 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 7a13884..16486a4 100644 --- a/app/src/main/java/org/yameida/worktool/service/WeworkLoopImpl.kt +++ b/app/src/main/java/org/yameida/worktool/service/WeworkLoopImpl.kt @@ -3,8 +3,8 @@ 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 org.yameida.worktool.Constant -import org.yameida.worktool.Demo import org.yameida.worktool.model.WeworkMessageBean import org.yameida.worktool.service.WeworkController.mainLoopRunning import org.yameida.worktool.utils.* @@ -141,9 +141,7 @@ object WeworkLoopImpl { if (lastMessage != null) { var tempContent = "" for (itemMessage in lastMessage.itemMessageList) { - if (itemMessage.text.contains("@" + Constant.myName) - || itemMessage.text.isDigitsOnly() - ) { + if (itemMessage.text.contains("@" + Constant.myName)) { tempContent = itemMessage.text } } @@ -235,8 +233,24 @@ object WeworkLoopImpl { } } if (logIndex % 120 == 0) { + //让企微切换页面使APP保持活跃 goHomeTab("通讯录") goHomeTab("消息") + //滚动到顶端查看是否有无提示消息 + AccessibilityUtil.scrollToTop(WeworkController.weworkService, getRoot()) + val listview = AccessibilityUtil.findOneByClazz(getRoot(), Views.RecyclerView, Views.ListView, Views.ViewGroup) + if (listview != null && listview.childCount >= 2) { + if (checkNoTipMessage(listview) != 1) { + AccessibilityUtil.scrollToBottom(WeworkController.weworkService, getRoot(), listener = object : AccessibilityUtil.OnScrollListener() { + override fun onScroll(): Boolean { + if (checkNoTipMessage(listview) != 0) { + return true + } + return false + } + }) + } + } } if (!isAtHome()) return true if (logIndex++ % 30 == 0) { @@ -244,15 +258,15 @@ object WeworkLoopImpl { if (logIndex % 120 == 0) log("读取首页聊天列表") } val listview = AccessibilityUtil.findOneByClazz(getRoot(), Views.RecyclerView, Views.ListView, Views.ViewGroup) - if (listview != null) { - if (listview.childCount >= 2) { - if (checkUnreadChatRoom(listview)) { - //进入聊天页 - return true - } + if (listview != null && listview.childCount >= 2) { + if (checkUnreadChatRoom(listview)) { + //如果有红点 点击进入聊天页 + return true + } else if (checkNoTipMessage(listview) == 1) { + //如果发现拉入群聊/修改群名/移出群聊 点击进入聊天页 + return true } else { - LogUtils.e("读取聊天列表失败") - error("读取聊天列表失败") + LogUtils.v("未发现新消息或无提示消息") } } else { LogUtils.e("读取聊天列表失败") @@ -263,7 +277,6 @@ object WeworkLoopImpl { /** * 检查首页-聊天列表是否有未读红点并点击进入 - * 获取红点 */ private fun checkUnreadChatRoom(list: AccessibilityNodeInfo): Boolean { val spotNodeList = arrayListOf() @@ -296,6 +309,45 @@ object WeworkLoopImpl { } } + /** + * 检查首页-聊天列表是否有拉入群聊/修改群名/移出群聊等无提示消息 + * @return -1当前列表不存在一周内消息 0未发现无提示消息 1发现无提示消息 + */ + private fun checkNoTipMessage(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 } + 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) + if (interval > 3600) { + LogUtils.i("发现无提示消息: $tvList") + log("发现无提示消息: $tvList") + if (AccessibilityUtil.performClick(item)) { + //进入聊天页 下一步 getChatMessageList + } else { + AccessibilityUtil.clickByNode(WeworkController.weworkService, item) + } + SPUtils.getInstance("noTipMessage").put(tvList[0].toString(), System.currentTimeMillis() / 1000) + return 1 + } else { + LogUtils.v("发现无提示消息: $tvList 消息在 $interval 秒前已被查看") + } + } + } else { + return -1 + } + } + } + return 0 + } + /** * 解析消息列表里的一条消息 */ 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 ab05195..d6ac7ed 100644 --- a/app/src/main/java/org/yameida/worktool/service/WeworkOperationImpl.kt +++ b/app/src/main/java/org/yameida/worktool/service/WeworkOperationImpl.kt @@ -215,6 +215,10 @@ object WeworkOperationImpl { ): Boolean { val startTime = System.currentTimeMillis() if (!WeworkRoomUtil.isGroupExists(groupName)) { + if (!beforeCreateGroupCheck()) { + uploadCommandResult(message, ExecCallbackBean.ERROR_CREATE_GROUP_LIMIT, "建群达到上限", startTime) + return false + } if (!createGroup()) { uploadCommandResult(message, ExecCallbackBean.ERROR_CREATE_GROUP, "创建群失败", startTime) return false @@ -720,9 +724,13 @@ object WeworkOperationImpl { Views.ImageView ) ) + } else if (AccessibilityUtil.findOnceByText(getRoot(), "该用户不存在") != null) { + LogUtils.e("该用户不存在: ${friend.phone}") + uploadCommandResult(message, ExecCallbackBean.ERROR_TARGET, "该用户不存在: ${friend.phone}", startTime) + return false } if (modifyFriendInfo(friend)) { - if (AccessibilityUtil.findTextAndClick(getRoot(), "添加为联系人")) { + if (AccessibilityUtil.findTextAndClick(getRoot(), "添加为联系人", timeout = 2000)) { LogUtils.d("准备发送好友邀请: ${friend.phone}") if (!friend.leavingMsg.isNullOrEmpty()) { AccessibilityUtil.findTextInput(getRoot(), friend.leavingMsg) @@ -833,7 +841,7 @@ object WeworkOperationImpl { } } if (modifyFriendInfo(friend)) { - if (AccessibilityUtil.findTextAndClick(getRoot(), "添加为联系人")) { + if (AccessibilityUtil.findTextAndClick(getRoot(), "添加为联系人", timeout = 2000)) { LogUtils.d("准备发送好友邀请: ${friend.name}") if (!friend.leavingMsg.isNullOrEmpty()) { AccessibilityUtil.findTextInput(getRoot(), friend.leavingMsg) @@ -906,6 +914,9 @@ object WeworkOperationImpl { ): Boolean { val startTime = System.currentTimeMillis() goHome() + if (AccessibilityUtil.findOnceByText(getRoot(), "日程", exact = true) == null) { + AccessibilityUtil.scrollToTop(WeworkController.weworkService, getRoot()) + } val tvDiaryFlag = AccessibilityUtil.findOneByText(getRoot(), "日程", exact = true) if (tvDiaryFlag != null && (tvDiaryFlag.parent?.childCount == 2 || tvDiaryFlag.parent?.childCount == 3)) { AccessibilityUtil.performClick(tvDiaryFlag) @@ -1141,11 +1152,34 @@ object WeworkOperationImpl { } /** - * 检查是否达到当日建群上限 + * 建群前检查是否达到当日建群上限 + * @return true允许建群 false不允许建群 + */ + private fun beforeCreateGroupCheck(): Boolean { + //有建群权限且最近300秒内发现限制建群 + if (SPUtils.getInstance("limit").getBoolean("canCreateGroup", false)) { + val interval = System.currentTimeMillis() / 1000 - SPUtils.getInstance("limit").getLong("createGroupLimit", 0) + if (interval < 300) { + LogUtils.e("发现达到当日建群上限 请等待${300 - interval}秒后再试!") + return false + } + } + return true + } + + /** + * 建群后检查是否达到当日建群上限 + * @return true达到上限 false为达到上限 */ private fun createGroupLimit(): Boolean { val hasLimit = AccessibilityUtil.findOneByText(getRoot(), "新建群聊功能暂时被限制", "未验证企业", timeout = 2000) + if (hasLimit == null) { + SPUtils.getInstance("limit").put("canCreateGroup", true) + } else if (SPUtils.getInstance("limit").getBoolean("canCreateGroup", false)) { + SPUtils.getInstance("limit").put("createGroupLimit", System.currentTimeMillis() / 1000) + LogUtils.e("发现达到当日建群上限") + } return hasLimit != null } 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 5bfbcae..ade4651 100644 --- a/app/src/main/java/org/yameida/worktool/service/WeworkService.kt +++ b/app/src/main/java/org/yameida/worktool/service/WeworkService.kt @@ -15,6 +15,7 @@ import org.yameida.worktool.Constant import org.yameida.worktool.Demo import org.yameida.worktool.utils.* import java.lang.Exception +import kotlin.concurrent.thread /** * 企业微信辅助服务 @@ -24,6 +25,8 @@ import java.lang.Exception class WeworkService : AccessibilityService() { private val TAG = "WeworkService" lateinit var webSocketManager: WebSocketManager + var currentPackage = "" + var currentClass = "" override fun onServiceConnected() { LogUtils.i("初始化成功") @@ -35,7 +38,7 @@ class WeworkService : AccessibilityService() { //初始化消息处理器 MyLooper.init() //开发者可以在这里添加测试代码 启动时调用一次 - Demo.test(AppUtils.isAppDebug()) + thread { Demo.test(AppUtils.isAppDebug()) } //监听是否修改链接号并重新长连接 registerReceiver(object : BroadcastReceiver() { @@ -62,6 +65,11 @@ class WeworkService : AccessibilityService() { * @param event */ override fun onAccessibilityEvent(event: AccessibilityEvent) { + currentPackage = event.packageName?.toString() ?: "" + val className = event.className?.toString() ?: "" + if (className.contains(currentPackage)) { + currentClass = className + } } override fun onInterrupt() { @@ -83,13 +91,13 @@ class WeworkService : AccessibilityService() { private lateinit var socket: WebSocket override fun onOpen(webSocket: WebSocket, response: Response) { socket = webSocket - Log.e(TAG, "链接建立") - val robotId = SPUtils.getInstance().getString(Constant.LISTEN_CHANNEL_ID, "") + Log.e(TAG, "连接建立") + val robotId = Constant.robotId val appVersion = SPUtils.getInstance().getString("appVersion", "") val workVersion = SPUtils.getInstance().getString("workVersion", "") val deviceRooted = SPUtils.getInstance().getBoolean("deviceRooted", false) val hook = SPUtils.getInstance().getBoolean("hook", false) - log("链接建立: $robotId appVersion: $appVersion workVersion: $workVersion deviceRooted: $deviceRooted hook: $hook") + log("连接建立: $robotId appVersion: $appVersion workVersion: $workVersion deviceRooted: $deviceRooted hook: $hook") LogUtils.i("设置自动跳转企业微信") sendBroadcast(true) } @@ -107,7 +115,7 @@ class WeworkService : AccessibilityService() { override fun onClosed(webSocket: WebSocket, code: Int, reason: String) { super.onClosed(webSocket, code, reason) //服务器关闭后 - Log.e(TAG, "链接关闭 $reason") + Log.e(TAG, "连接关闭 $reason") sendBroadcast(false) } @@ -120,7 +128,7 @@ class WeworkService : AccessibilityService() { override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) { //服务器中断 - Log.e(TAG, "链接错误: " + t.toString() + response.toString()) + Log.e(TAG, "连接错误: " + t.toString() + response.toString()) sendBroadcast(false) } 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 c1dc28a..7e2257a 100644 --- a/app/src/main/java/org/yameida/worktool/utils/AccessibilityUtil.kt +++ b/app/src/main/java/org/yameida/worktool/utils/AccessibilityUtil.kt @@ -22,6 +22,7 @@ import org.yameida.worktool.service.WeworkController import android.content.ClipData import android.content.ClipboardManager import android.content.Context +import java.lang.StringBuilder /** @@ -54,11 +55,21 @@ import android.content.Context * performLongClick 对某个节点或父节点进行长按 * performLongClickWithSon 对某个节点或子节点进行长按 * + * 注意:操作均为阻塞式,原则上本工具类所有操作都应在子线程执行 */ object AccessibilityUtil { private const val tag = "AccessibilityUtil" private const val SHORT_INTERVAL = 150L - private const val SCROLL_INTERVAL = 500L + private const val SCROLL_INTERVAL_NATIVE = 500L + private const val SCROLL_INTERVAL = 800L + + /** + * 滚动监听 + * 如果期望停止滚动则在onScroll回调中返回true 否则返回false + */ + abstract class OnScrollListener { + abstract fun onScroll(): Boolean + } //编辑EditView(粘贴 不推荐) fun sendTextForEditText(context: Context, nodeInfo: AccessibilityNodeInfo?, text: String): Boolean { @@ -84,8 +95,13 @@ object AccessibilityUtil { } //寻找第一个文本匹配(关键词)并点击 - fun findTextAndClick(nodeInfo: AccessibilityNodeInfo?, vararg textList: String): Boolean { - val textView = findOneByText(nodeInfo, *textList) ?: return false + fun findTextAndClick(nodeInfo: AccessibilityNodeInfo?, + vararg textList: String, + exact: Boolean = false, + timeout: Long = 5000, + root: Boolean = true + ): Boolean { + val textView = findOneByText(nodeInfo, *textList, exact = exact, timeout = timeout, root = root) ?: return false return performClick(textView) } @@ -117,6 +133,106 @@ object AccessibilityUtil { return false } + //滚动到顶部 + fun scrollToTop( + service: AccessibilityService, + nodeInfo: AccessibilityNodeInfo, + scrollNodeIndex: Int = 0, + tryUseGesture: Boolean = true, + listener: OnScrollListener? = null, + maxRetry: Int = 10 + ): Boolean { + var textChanged = false + var index = 0 + while (index++ < maxRetry) { + val scrollBefore = findAllOnceByClazz(getRoot(), Views.TextView) + performScrollUp(nodeInfo, scrollNodeIndex) + if (scrollBefore == findAllOnceByClazz(getRoot(), Views.TextView)) { + LogUtils.d("已经滚动到顶部") + if (textChanged) { + return true + } else { + break + } + } else { + textChanged = true + LogUtils.v("未滚动到顶部 $index") + } + } + if (tryUseGesture) { + LogUtils.d("未找到可滚动列表 使用手势滚动") + val width = ScreenUtils.getScreenWidth() + val height = ScreenUtils.getScreenHeight() + index = 0 + while (index++ < maxRetry) { + val scrollBefore = findAllOnceByClazz(getRoot(), Views.TextView) + scrollByXY(service, width / 2, (height / 2.2).toInt(), 0, height / 3) + if (scrollBefore == findAllOnceByClazz(getRoot(), Views.TextView)) { + LogUtils.d("已经滚动到顶部") + break + } else { + LogUtils.v("未滚动到顶部 $index") + if (listener != null && listener.onScroll()) { + LogUtils.d("提前终止滚动") + return true + } + } + } + return true + } + return false + } + + //滚动到顶部 + fun scrollToBottom( + service: AccessibilityService, + nodeInfo: AccessibilityNodeInfo, + scrollNodeIndex: Int = 0, + tryUseGesture: Boolean = true, + listener: OnScrollListener? = null, + maxRetry: Int = 10 + ): Boolean { + var textChanged = false + var index = 0 + while (index++ < maxRetry) { + val scrollBefore = findAllOnceByClazz(getRoot(), Views.TextView) + performScrollDown(nodeInfo, scrollNodeIndex) + if (scrollBefore == findAllOnceByClazz(getRoot(), Views.TextView)) { + LogUtils.d("已经滚动到底部") + if (textChanged) { + return true + } else { + break + } + } else { + textChanged = true + LogUtils.v("未滚动到底部 $index") + if (listener != null && listener.onScroll()) { + LogUtils.d("提前终止滚动") + return true + } + } + } + if (tryUseGesture) { + LogUtils.d("未找到可滚动列表 使用手势滚动") + val width = ScreenUtils.getScreenWidth() + val height = ScreenUtils.getScreenHeight() + index = 0 + while (index++ < maxRetry) { + val scrollBefore = findAllOnceByClazz(getRoot(), Views.TextView) + scrollByXY(service, width / 2, (height / 2.2).toInt(), 0, -height / 3) + if (scrollBefore == findAllOnceByClazz(getRoot(), Views.TextView)) { + LogUtils.d("已经滚动到底部") + break + } else { + LogUtils.v("未滚动到底部 $index") + } + } + return true + } + return false + } + //滚动并按文本寻找第一个控件 fun scrollAndFindByText( service: AccessibilityService, @@ -124,6 +240,10 @@ object AccessibilityUtil { vararg textList: String, maxRetry: Int = 3 ): AccessibilityNodeInfo? { + val node = findOnceByText(nodeInfo, *textList) + if (node != null) { + return node + } var index = 0 while (index++ < maxRetry) { performScrollDown(nodeInfo, 0) @@ -146,8 +266,7 @@ object AccessibilityUtil { val height = ScreenUtils.getScreenHeight() index = 0 while (index++ < maxRetry * 2) { - scrollByXY(service, width / 2, height / 2, 0, -height / 2) - sleep(SCROLL_INTERVAL) + scrollByXY(service, width / 2, (height / 2.2).toInt(), 0, -height / 3) val node = findOnceByText(nodeInfo, *textList) if (node != null) { return node @@ -155,8 +274,7 @@ object AccessibilityUtil { } index = 0 while (index++ < maxRetry * 3) { - scrollByXY(service, width / 2, height / 2, 0, height / 2) - sleep(SCROLL_INTERVAL) + scrollByXY(service, width / 2, (height / 2.2).toInt(), 0, height / 3) val node = findOnceByText(nodeInfo, *textList) if (node != null) { return node @@ -299,7 +417,7 @@ object AccessibilityUtil { val canScrollNodeList = findCanScrollNode(node) if (canScrollNodeList.size > index) { canScrollNodeList[index].performAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD) - sleep(SCROLL_INTERVAL) + sleep(SCROLL_INTERVAL_NATIVE) return true } return false @@ -311,7 +429,7 @@ object AccessibilityUtil { val canScrollNodeList = findCanScrollNode(node) if (canScrollNodeList.size > index) { canScrollNodeList[index].performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD) - sleep(SCROLL_INTERVAL) + sleep(SCROLL_INTERVAL_NATIVE) return true } return false @@ -814,22 +932,31 @@ object AccessibilityUtil { node: AccessibilityNodeInfo?, printText: Boolean = true, depth: Int = 0 - ) { - if (node == null) return + ): StringBuilder { + val sb = StringBuilder() + if (node == null) return sb var s = "" for (i in 0 until depth) { s += "---" } - Log.d(tag, "$s depth: $depth className: " + node.className + " isClickable: " + node.isClickable) + val temp = "$s depth: $depth className: " + node.className + " isClickable: " + node.isClickable + Log.d(tag, temp) + sb.append(temp).append("\n") + var text = "" if (printText && node.text != null) { - Log.d(tag, "$s depth: $depth text: " + node.text) + text = "$s depth: $depth text: " + node.text + Log.d(tag, text) + sb.append(text).append("\n") } if (printText && node.contentDescription != null) { - Log.d(tag, "$s depth: $depth desc: " + node.contentDescription) + val desc = "$s depth: $depth desc: " + node.contentDescription + Log.d(tag, desc) + sb.append(desc).append("\n") } for (i in 0 until node.childCount) { - printNodeClazzTree(node.getChild(i), printText, depth + 1) + sb.append(printNodeClazzTree(node.getChild(i), printText, depth + 1)) } + return sb } /** @@ -946,7 +1073,7 @@ object AccessibilityUtil { path.lineTo(x.toFloat() + distanceX, y.toFloat() + distanceY) builder.addStroke(StrokeDescription(path, 0L, 300L)) val gesture = builder.build() - return service.dispatchGesture(gesture, object : GestureResultCallback() { + val dispatchGesture = service.dispatchGesture(gesture, object : GestureResultCallback() { override fun onCompleted(gestureDescription: GestureDescription) { LogUtils.v("scroll ok onCompleted") } @@ -955,5 +1082,7 @@ object AccessibilityUtil { LogUtils.v("scroll ok onCancelled") } }, null) + sleep(SCROLL_INTERVAL) + return dispatchGesture } } \ No newline at end of file diff --git a/app/src/main/java/org/yameida/worktool/utils/DonateUtil.kt b/app/src/main/java/org/yameida/worktool/utils/DonateUtil.kt new file mode 100644 index 0000000..d2ed9c9 --- /dev/null +++ b/app/src/main/java/org/yameida/worktool/utils/DonateUtil.kt @@ -0,0 +1,33 @@ +package org.yameida.worktool.utils + +import android.content.Intent +import org.yameida.worktool.R +import android.content.Context +import android.net.Uri +import com.blankj.utilcode.util.ToastUtils +import com.blankj.utilcode.util.Utils +import com.qmuiteam.qmui.widget.dialog.QMUIDialog + +object DonateUtil { + + fun zfbDonate(context: Context) { + try { + QMUIDialog.MessageDialogBuilder(context) + .setTitle(context.getString(R.string.host_list)) + .setTitle("捐赠") + .setMessage("如果你觉得${context.getString(R.string.app_name)}很棒,可否愿意花一点点钱请作者喝杯咖啡") + .addAction("支付宝") { + dialog, index -> dialog.dismiss() + ToastUtils.showLong(Utils.getApp().getString(R.string.app_name) + " 因为有你的支持而能够不断更新、完善,非常感谢支持!") + val intent = Intent(Intent.ACTION_VIEW) + intent.data = Uri.parse("alipays://platformapi/startapp?saId=10000007&clientVersion=3.7.0.0718&qrcode=https%3A%2F%2Fqr.alipay.com%2Ffkx15436xnv3mzpuufhvn52%3F_s%3Dweb-other") + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + context.startActivity(intent) + } + .create(R.style.QMUI_Dialog) + .show() + } catch (e: Throwable) { + ToastUtils.showShort("打开支付宝失败,你可能还没有安装支付宝客户端") + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/yameida/worktool/utils/FloatWindowHelper.kt b/app/src/main/java/org/yameida/worktool/utils/FloatWindowHelper.kt index e5cb8c8..26b643a 100644 --- a/app/src/main/java/org/yameida/worktool/utils/FloatWindowHelper.kt +++ b/app/src/main/java/org/yameida/worktool/utils/FloatWindowHelper.kt @@ -1,12 +1,145 @@ package org.yameida.worktool.utils +import android.accessibilityservice.AccessibilityService +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.content.ServiceConnection +import android.os.IBinder +import android.os.Message +import android.view.View +import android.widget.ImageView +import com.blankj.utilcode.util.FileUtils import com.blankj.utilcode.util.LogUtils +import com.blankj.utilcode.util.ToastUtils +import com.blankj.utilcode.util.Utils +import com.bumptech.glide.Glide +import org.yameida.floatwindow.FloatWindowManager +import org.yameida.floatwindow.DefaultFloatService +import org.yameida.floatwindow.listener.OnClickListener +import org.yameida.worktool.R +import org.yameida.worktool.activity.ListenActivity +import org.yameida.worktool.activity.SettingsActivity +import org.yameida.worktool.model.WeworkMessageBean +import org.yameida.worktool.service.* +import java.io.File +import java.text.SimpleDateFormat +import java.util.* +import kotlin.concurrent.thread object FloatWindowHelper { + var isPause = false + fun showWindow() { LogUtils.d("FloatWindowHelper.showWindow()") + FloatWindowManager.show(DefaultFloatService::class.java) + + val app = Utils.getApp() + val intent = Intent(app, DefaultFloatService::class.java) + app.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE) + } + + /** + * 主功能继续 + */ + private fun accessibilityServiceResume() { + if (PermissionHelper.isAccessibilitySettingOn()) { + LogUtils.i("主功能继续") + ToastUtils.showShort("主功能继续~") + //隐藏软键盘模式 + WeworkController.weworkService.softKeyboardController.showMode = AccessibilityService.SHOW_MODE_HIDDEN + isPause = false + MyLooper.getInstance().removeMessages(WeworkMessageBean.LOOP_RECEIVE_NEW_MESSAGE) + MyLooper.getInstance().sendMessage(Message.obtain().apply { + what = WeworkMessageBean.LOOP_RECEIVE_NEW_MESSAGE + obj = WeworkMessageBean().apply { type = WeworkMessageBean.LOOP_RECEIVE_NEW_MESSAGE } + }) + } else { + LogUtils.e("请先打开WorkTool主功能~") + } + } + + /** + * 主功能暂停 + */ + private fun accessibilityServicePause() { + if (PermissionHelper.isAccessibilitySettingOn()) { + LogUtils.i("主功能暂停") + ToastUtils.showShort("主功能暂停~") + //显示软键盘模式 + WeworkController.weworkService.softKeyboardController.showMode = AccessibilityService.SHOW_MODE_AUTO + isPause = true + WeworkController.mainLoopRunning = false + } else { + LogUtils.e("请先打开WorkTool主功能~") + } + } + + private val serviceConnection = object : ServiceConnection { + override fun onServiceConnected(name: ComponentName?, iBinder: IBinder?) { + LogUtils.i("DefaultFloatService 服务连接") + val service = (iBinder as DefaultFloatService.DefaultFloatServiceBinder).getService() + service.onClickListener = object : OnClickListener { + override fun onClick(v: View, event: Int) { + when (event) { + 1 -> { + if (PermissionHelper.isAccessibilitySettingOn()) { + if (!isPause) { + ToastUtils.showShort("请先暂停WorkTool主功能~") + return + } + thread { + val printNodeClazzTree = + AccessibilityUtil.printNodeClazzTree(getRoot(true)) + val df = SimpleDateFormat("MMdd_HHmmss") + val filePath = "${ + Utils.getApp().getExternalFilesDir("share") + }/${df.format(Date())}/${df.format(Date())}_printNode.txt" + val newFile = File(filePath) + val create = FileUtils.createFileByDeleteOldFile(newFile) + if (create && newFile.canWrite()) { + printNodeClazzTree.append("\n") + .append(WeworkController.weworkService.currentPackage) + .append("\n") + .append(WeworkController.weworkService.currentClass) + newFile.writeBytes(printNodeClazzTree.toString().toByteArray()) + LogUtils.i("打印节点文件存储本地成功 $filePath") + } + ShareUtil.share("*", newFile) + } + } else { + ToastUtils.showShort("请先打开WorkTool主功能~") + } + } + 2 -> { + if (PermissionHelper.isAccessibilitySettingOn()) { + if (isPause) { + Glide.with(Utils.getApp()).load(R.drawable.float_icon_pause).into(v as ImageView) + accessibilityServiceResume() + } else { + Glide.with(Utils.getApp()).load(R.drawable.float_icon_play).into(v as ImageView) + accessibilityServicePause() + } + } else { + ToastUtils.showShort("请先打开WorkTool主功能~") + } + } + 3 -> { + ListenActivity.enterActivity(Utils.getApp(), 0) + } + 4 -> { + SettingsActivity.enterActivity(Utils.getApp()) + } + } + } + } + } + + override fun onServiceDisconnected(name: ComponentName?) { + LogUtils.i("DefaultFloatService 服务断开") + } } } \ No newline at end of file diff --git a/app/src/main/java/org/yameida/worktool/utils/FlowPermissionHelper.kt b/app/src/main/java/org/yameida/worktool/utils/FlowPermissionHelper.kt new file mode 100644 index 0000000..9a070ce --- /dev/null +++ b/app/src/main/java/org/yameida/worktool/utils/FlowPermissionHelper.kt @@ -0,0 +1,81 @@ +package org.yameida.worktool.utils + +import android.app.AppOpsManager +import android.content.Context +import android.net.Uri +import android.os.Build +import android.provider.Settings +import java.lang.reflect.Method + +object FlowPermissionHelper { + + fun isXiaoMi(): Boolean { + return checkManufacturer("xiaomi") + } + + fun isOppo(): Boolean { + return checkManufacturer("oppo") + } + + fun isVivo(): Boolean { + return checkManufacturer("vivo") + } + + private fun checkManufacturer(manufacturer: String): Boolean { + return manufacturer.equals(Build.MANUFACTURER, true) + } + + fun canBackgroundStart(context: Context): Boolean { + if (isXiaoMi()) { + return isXiaomiBgStartPermissionAllowed(context) + } + + if (isVivo()) { + return isVivoBgStartPermissionAllowed(context) + } + + if (isOppo() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + return Settings.canDrawOverlays(context) + } + return true + } + + + private fun isXiaomiBgStartPermissionAllowed(context: Context): Boolean { + val ops = context.getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager + try { + val op = 10021 + val method: Method = ops.javaClass.getMethod("checkOpNoThrow", Int::class.javaPrimitiveType, Int::class.javaPrimitiveType, String::class.java) + val result = method.invoke(ops, op, android.os.Process.myUid(), context.packageName) as Int + return result == AppOpsManager.MODE_ALLOWED + } catch (e: Exception) { + e.printStackTrace() + } + return false + } + + private fun isVivoBgStartPermissionAllowed(context: Context): Boolean { + return getVivoBgStartPermissionStatus(context) == 0 + } + + /** + * 判断Vivo后台弹出界面状态, 1无权限,0有权限 + * @param context context + */ + private fun getVivoBgStartPermissionStatus(context: Context): Int { + val uri: Uri = Uri.parse("content://com.vivo.permissionmanager.provider.permission/start_bg_activity") + val selection = "pkgname = ?" + val selectionArgs = arrayOf(context.packageName) + var state = 1 + try { + context.contentResolver.query(uri, null, selection, selectionArgs, null)?.use { + if (it.moveToFirst()) { + state = it.getInt(it.getColumnIndex("currentstate")) + } + } + } catch (e: Exception) { + e.printStackTrace() + } + return state + } +} diff --git a/app/src/main/java/org/yameida/worktool/utils/IWWAPIUtil.kt b/app/src/main/java/org/yameida/worktool/utils/IWWAPIUtil.kt new file mode 100644 index 0000000..72490a0 --- /dev/null +++ b/app/src/main/java/org/yameida/worktool/utils/IWWAPIUtil.kt @@ -0,0 +1,74 @@ +package org.yameida.worktool.utils + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.drawable.BitmapDrawable +import android.widget.Toast +import com.blankj.utilcode.util.AppUtils +import com.blankj.utilcode.util.Utils +import com.tencent.wework.api.IWWAPI +import com.tencent.wework.api.WWAPIFactory +import com.tencent.wework.api.model.WWMediaLink +import com.tencent.wework.api.model.WWMediaMiniProgram +import com.tencent.wework.api.model.WWSimpleRespMessage + + +object IWWAPIUtil { + + private var iwwapi: IWWAPI? = null + + var appid = "wwe51e5ed82702b49b" //企业唯一标识。创建企业后显示在,我的企业 CorpID字段 + var agentid = "1000002" //应用唯一标识。显示在具体应用下的 AgentId字段 + var schema = "wwauthe51e5ed82702b49b000002" + + fun init(context: Context, schema: String = this.schema) { + this.schema = schema + iwwapi = WWAPIFactory.createWWAPI(context) + iwwapi?.registerApp(schema) + } + + fun sendLink(thumbUrl: String?, webpageUrl: String?, title: String?, description: String?) { + val link = WWMediaLink() + link.thumbUrl = thumbUrl + link.webpageUrl = webpageUrl + link.title = title + link.description = description + link.appPkg = AppUtils.getAppPackageName() + link.appName = AppUtils.getAppName() + link.appId = appid + link.agentId = agentid + iwwapi?.sendMessage(link) + } + + fun sendMicroProgram() { + val miniProgram = WWMediaMiniProgram() + miniProgram.appPkg = AppUtils.getAppPackageName() + miniProgram.appName = AppUtils.getAppName() + miniProgram.appId = appid + miniProgram.agentId = agentid + miniProgram.schema = schema + miniProgram.username = "gh_dde54cb88ce7@app" //必须是应用关联的小程序,注意要有@app后缀 + miniProgram.description = "dddddd" + miniProgram.path = "/pages/plugin/index.html?plugid=1cbd3b7c8674e61769436b5e354ddb2f" +// val bitmap = (getDrawable(R.drawable.test) as BitmapDrawable).bitmap +// val stream = ByteArrayOutputStream() +// bitmap.compress(Bitmap.CompressFormat.JPEG, 0, stream) +// val byteArray: ByteArray = stream.toByteArray() + +// miniProgram.hdImageData = byteArray + miniProgram.title = "测试_MaHow" + iwwapi!!.sendMessage(miniProgram) { resp -> + if (resp is WWSimpleRespMessage) { + val rsp = resp as WWSimpleRespMessage + var t: String? = "" + Toast.makeText( + Utils.getApp(), + "发小程序," + rsp.errCode + "," + rsp.errMsg.also { + t = it + }, + Toast.LENGTH_LONG + ).show() + } + } + } +} \ 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 index 92cb395..b568a2f 100644 --- a/app/src/main/java/org/yameida/worktool/utils/PermissionHelper.kt +++ b/app/src/main/java/org/yameida/worktool/utils/PermissionHelper.kt @@ -17,7 +17,6 @@ object PermissionHelper { 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, @@ -27,6 +26,7 @@ object PermissionHelper { } catch (e: Exception) { e.printStackTrace() } + var flag = false if (enable == 1) { val stringSplitter = TextUtils.SimpleStringSplitter(':') val settingVal = Settings.Secure.getString( @@ -38,14 +38,14 @@ object PermissionHelper { while (stringSplitter.hasNext()) { val accessibilityService = stringSplitter.next() if (accessibilityService == serviceName || accessibilityService == serviceShortName) { - LogUtils.i("isAccessibilitySettingOn: true") - return true + flag = true + break } } } } - LogUtils.i("isAccessibilitySettingOn: false") - return false + LogUtils.v("isAccessibilitySettingOn: $serviceName $serviceShortName $flag") + return flag } } \ No newline at end of file diff --git a/app/src/main/java/org/yameida/worktool/utils/RegexHelper.kt b/app/src/main/java/org/yameida/worktool/utils/RegexHelper.kt index cdeec4f..fb82606 100644 --- a/app/src/main/java/org/yameida/worktool/utils/RegexHelper.kt +++ b/app/src/main/java/org/yameida/worktool/utils/RegexHelper.kt @@ -15,9 +15,10 @@ object RegexHelper { .replace("{", "\\{") .replace("}", "\\}") .replace("|", "\\|") -// .replace("-", "\\-") //企微自身限制 -// .replace("(", "\\(") //企微自身限制 -// .replace(")", "\\)") //企微自身限制 + //企微自身存在限制 + .replace("-", "\\-") + .replace("(", "\\(") + .replace(")", "\\)") } } \ No newline at end of file diff --git a/app/src/main/java/org/yameida/worktool/utils/ShareCommentsUtil.java b/app/src/main/java/org/yameida/worktool/utils/ShareCommentsUtil.java new file mode 100644 index 0000000..ab8c783 --- /dev/null +++ b/app/src/main/java/org/yameida/worktool/utils/ShareCommentsUtil.java @@ -0,0 +1,67 @@ +package org.yameida.worktool.utils; + +import android.app.Activity; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.net.Uri; +import android.os.Environment; + +import com.blankj.utilcode.util.ToastUtils; + +import org.yameida.worktool.R; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.util.ArrayList; + +public class ShareCommentsUtil { + private static boolean checkInstallation(Context context, String packageName) { + try { + context.getPackageManager().getPackageInfo(packageName, PackageManager.GET_ACTIVITIES); + return true; + } catch (PackageManager.NameNotFoundException e) { + return false; + } + } + + public static void shareToWeChat(Context context) { + try { + if (!checkInstallation(context, "com.tencent.mm")) { + ToastUtils.showShort("未发现微信"); + return; + } + Intent intent = new Intent(); //分享精确到微信的页面,朋友圈页面,或者选择好友分享页面 + ComponentName comp = new ComponentName("com.tencent.mm", "com.tencent.mm.ui.tools.ShareToTimeLineUI"); + intent.setComponent(comp); + intent.setAction(Intent.ACTION_SEND_MULTIPLE); + intent.setType("image/*"); // intent.setType("text/plain");添加Uri图片地址 + String msg = "我想分享"; + intent.putExtra("Kdescription", msg); + ArrayList imageUris = new ArrayList(); + File dir = context.getExternalFilesDir(null); + if (dir == null || dir.getAbsolutePath().equals("")) { + dir = new File(Environment.getExternalStorageDirectory().getAbsolutePath()); + } + File pic = new File(dir, "bigbang.jpg"); + pic.deleteOnExit(); + BitmapDrawable bitmapDrawable; + bitmapDrawable = (BitmapDrawable) context.getDrawable(R.mipmap.ic_launcher); + try { + bitmapDrawable.getBitmap().compress(Bitmap.CompressFormat.JPEG, 75, new FileOutputStream(pic)); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + Uri uri = Uri.parse(android.provider.MediaStore.Images.Media.insertImage(context.getContentResolver(), pic.getAbsolutePath(), "bigbang.jpg", null)); + imageUris.add(uri); + intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, imageUris); + ((Activity) context).startActivityForResult(intent, 1000); + } catch (Throwable e) { + ToastUtils.showShort("分享到微信失败"); + } + } +} \ 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 a1baec0..e9e355c 100644 --- a/app/src/main/java/org/yameida/worktool/utils/WebSocketManager.java +++ b/app/src/main/java/org/yameida/worktool/utils/WebSocketManager.java @@ -37,6 +37,7 @@ public class WebSocketManager { private String url; private WebSocketListener listener; private boolean connecting = false; + private long lastConnectedTime = 0L; public WebSocketManager(String url, WebSocketListener listener) { Log.e(url, "新建链接"); @@ -113,18 +114,25 @@ public class WebSocketManager { Log.e(url, "重连"); boolean isConnect = false; int interval = reconnectInt; - while (!isConnect) { + while (true) { try { isConnect = connect(); - Thread.sleep(interval); - if (interval < 600000) { - interval *= 2; + if (isConnect) { + connecting = false; + break; } } catch (Exception e) { e.printStackTrace(); } + try { + Thread.sleep(interval); + if (interval < 600000) { + interval *= 2; + } + } catch (InterruptedException e) { + e.printStackTrace(); + } } - connecting = false; } private boolean connect() { @@ -137,6 +145,7 @@ public class WebSocketManager { } private ScheduledFuture heartCheckStart() { + lastConnectedTime = System.currentTimeMillis(); Runnable r = () -> { SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式 Log.e(url, "心跳检测" + df.format(new Date()));// new Date()为获取当前系统时间 @@ -145,8 +154,12 @@ public class WebSocketManager { WeworkController.INSTANCE.setEnableLoopRunning(false); //断开链接后进入重连 reConnect(); + //重连后刷新连接时间 + lastConnectedTime = System.currentTimeMillis(); + } + if (System.currentTimeMillis() - lastConnectedTime > heartBeatRate * 2000 && !FloatWindowHelper.INSTANCE.isPause()) { + ToastUtils.show("机器人运行中 请勿人工操作手机~"); } -// 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 4e28d03..e9b1633 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(noCut = true) + val roomTitle = getRoomTitle(noCut = true, print = false) when { isExternalSingleChat(roomTitle) -> { LogUtils.d("ROOM_TYPE: ROOM_TYPE_EXTERNAL_CONTACT") diff --git a/app/src/main/java/org/yameida/worktool/utils/envcheck/CheckRoot.java b/app/src/main/java/org/yameida/worktool/utils/envcheck/CheckRoot.java index af03372..b87a097 100644 --- a/app/src/main/java/org/yameida/worktool/utils/envcheck/CheckRoot.java +++ b/app/src/main/java/org/yameida/worktool/utils/envcheck/CheckRoot.java @@ -104,7 +104,7 @@ public class CheckRoot { fullResponse.add(line); } } catch (Exception e) { - e.printStackTrace(); + Log.i(LOG_TAG, "Unexpected error - Here is what I know: " + e.getMessage()); } Log.i(LOG_TAG, "–> Full response was: " + fullResponse); return fullResponse; @@ -137,7 +137,7 @@ public class CheckRoot { } process.destroy(); } catch (Exception e) { - e.printStackTrace(); + Log.i(LOG_TAG, "Unexpected error - Here is what I know: " + e.getMessage()); } } } @@ -181,8 +181,7 @@ public class CheckRoot { return false; } } catch (Exception e) { - Log.i(LOG_TAG, "Unexpected error - Here is what I know: " - + e.getMessage()); + Log.i(LOG_TAG, "Unexpected error - Here is what I know: " + e.getMessage()); return false; } } @@ -195,7 +194,7 @@ public class CheckRoot { fout.close(); return true; } catch (Exception e) { - e.printStackTrace(); + Log.i(LOG_TAG, "Unexpected error - Here is what I know: " + e.getMessage()); return false; } } @@ -214,7 +213,7 @@ public class CheckRoot { Log.i(LOG_TAG, result); return result; } catch (Exception e) { - e.printStackTrace(); + Log.i(LOG_TAG, "Unexpected error - Here is what I know: " + e.getMessage()); return null; } } diff --git a/app/src/main/res/layout/activity_browser.xml b/app/src/main/res/layout/activity_browser.xml new file mode 100644 index 0000000..50d2f76 --- /dev/null +++ b/app/src/main/res/layout/activity_browser.xml @@ -0,0 +1,18 @@ + + + + + + + + + diff --git a/app/src/main/res/layout/activity_float_guide.xml b/app/src/main/res/layout/activity_float_guide.xml new file mode 100644 index 0000000..57568f6 --- /dev/null +++ b/app/src/main/res/layout/activity_float_guide.xml @@ -0,0 +1,234 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_listen.xml b/app/src/main/res/layout/activity_listen.xml index d43d529..eeb8d3c 100644 --- a/app/src/main/res/layout/activity_listen.xml +++ b/app/src/main/res/layout/activity_listen.xml @@ -5,9 +5,46 @@ android:layout_height="match_parent" android:background="@color/background"> + + + + + + + + + + + android:layout_height="match_parent" + android:layout_below="@id/rl_bar"> - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml new file mode 100644 index 0000000..4e18a52 --- /dev/null +++ b/app/src/main/res/layout/activity_settings.xml @@ -0,0 +1,612 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +