From 43766975ce8c09cba31395a66fbaeb43513a2e6e Mon Sep 17 00:00:00 2001 From: yamleaf Date: Tue, 21 Jul 2026 18:59:38 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E6=89=93=E5=8D=A1?= =?UTF-8?q?=E5=BA=94=E7=94=A8=E9=80=9A=E7=9F=A5=E8=BD=AC=E7=A7=BB=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将目标打卡 App(飞书/企微/钉钉/M3)的通知经现有消息渠道(邮件/企业微信)转发到目标手机。 - NotificationMonitorService:新增 forwardNotificationIfEnabled,按开关转发目标 App 通知(轻量去重避免重复投递);handleRemoteCommand 增加 DT#开启转移 / DT#关闭转移 远程指令。 - Constant:新增 NOTIFICATION_TRANSFER_KEY。 - SettingsActivity / activity_settings:在“通知监听”下方新增“通知转移”开关(字体与同卡片一致),开启时校验通知监听与消息渠道。 - CommandActivity:指令列表登记两条通知转移指令。 --- .../app/service/NotificationMonitorService.kt | 49 +++++++++++++++++++ .../pengxh/daily/app/ui/CommandActivity.kt | 4 +- .../pengxh/daily/app/ui/SettingsActivity.kt | 23 +++++++++ .../com/pengxh/daily/app/utils/Constant.kt | 1 + app/src/main/res/layout/activity_settings.xml | 42 ++++++++++++++++ 5 files changed, 118 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/pengxh/daily/app/service/NotificationMonitorService.kt b/app/src/main/java/com/pengxh/daily/app/service/NotificationMonitorService.kt index 8b725ed3..bf2f8b4d 100644 --- a/app/src/main/java/com/pengxh/daily/app/service/NotificationMonitorService.kt +++ b/app/src/main/java/com/pengxh/daily/app/service/NotificationMonitorService.kt @@ -64,6 +64,10 @@ class NotificationMonitorService : NotificationListenerService() { private val serviceScope = CoroutineScope(Dispatchers.IO + SupervisorJob()) private var listenerConnected = false + // 通知转移去重:相同标题+内容在短时间窗口内只转发一次,避免系统重复投递刷屏 + private var lastTransferKey: String? = null + private var lastTransferTime = 0L + /** * 有可用的并且和通知管理器连接成功时回调 */ @@ -93,6 +97,9 @@ class NotificationMonitorService : NotificationListenerService() { // 保存指定包名的通知,其他的一律不保存 saveTargetNotice(pkg, targetApp, title, notice) + // 通知转移:开启后将目标打卡 App 的通知经现有消息渠道转发到目标手机 + forwardNotificationIfEnabled(pkg, targetApp, title, notice) + // 截屏模式选中 + 钉钉手动打卡 → 通知被第 99 行拦截,仅测试场景会出现 // 目标应用打卡通知,如果设置通知监听,那么结果来源只能选通知监听。 if (SaveKeyValues.loadInt(Constant.RESULT_SOURCE_KEY, Constant.DEFAULT_INDEX) == 0) { @@ -128,6 +135,34 @@ class NotificationMonitorService : NotificationListenerService() { } } + /** + * 通知转移:开启后,将目标打卡 App 的通知原文经现有消息渠道转发到目标手机。 + * 范围限制为当前目标打卡 App(飞书/企微/钉钉/M3/自定义),不会转发辅助聊天 App 或本应用自身。 + * 复用 [MessageDispatcher] 按用户已配置渠道(邮件/企业微信)自动分流,与打卡结果通知同一套通道。 + */ + private fun forwardNotificationIfEnabled(pkg: String, targetApp: String, title: String, notice: String) { + if (!SaveKeyValues.loadBoolean(Constant.NOTIFICATION_TRANSFER_KEY, false)) return + if (pkg != targetApp) return + if (notice.isBlank()) return + + // 轻量去重:相同标题+内容在 10s 内只转发一次 + val key = "$title|$notice" + val now = System.currentTimeMillis() + if (key == lastTransferKey && now - lastTransferTime < 10_000) return + lastTransferKey = key + lastTransferTime = now + + MessageDispatcher.sendMessage(title.ifBlank { "通知转移" }, notice) + } + + /** + * 校验通知转移依赖的消息渠道是否已配置,未配置时返回告警文案(仍允许保存开关态)。 + */ + private fun validateTransferConfig(): String { + val channel = SaveKeyValues.loadInt(Constant.MSG_CHANNEL_KEY, Constant.DEFAULT_INDEX) + return if (channel !in 0..1) "消息渠道未配置,转发可能失败" else "" + } + /** * 处理远程指令 */ @@ -204,6 +239,20 @@ class NotificationMonitorService : NotificationListenerService() { } } + notice.contains("开启转移") -> { + SaveKeyValues.saveBoolean(Constant.NOTIFICATION_TRANSFER_KEY, true) + val warning = validateTransferConfig() + MessageDispatcher.sendMessage( + "通知转移状态通知", + "通知转移已开启" + if (warning.isBlank()) "" else "\n⚠️$warning" + ) + } + + notice.contains("关闭转移") -> { + SaveKeyValues.saveBoolean(Constant.NOTIFICATION_TRANSFER_KEY, false) + MessageDispatcher.sendMessage("通知转移状态通知", "通知转移已关闭") + } + else -> { // 自定义打卡指令,用户可配置关键词(如 "打卡"),同样需要 DT# 前缀 val key = SaveKeyValues.loadString(Constant.REMOTE_COMMAND_KEY, "打卡") diff --git a/app/src/main/java/com/pengxh/daily/app/ui/CommandActivity.kt b/app/src/main/java/com/pengxh/daily/app/ui/CommandActivity.kt index d6f45f63..bba539a8 100644 --- a/app/src/main/java/com/pengxh/daily/app/ui/CommandActivity.kt +++ b/app/src/main/java/com/pengxh/daily/app/ui/CommandActivity.kt @@ -27,7 +27,9 @@ class CommandActivity : KotlinBaseActivity() { Triple("DT#考勤记录", "导出当天考勤记录", true), Triple("DT#打卡", "触发一次打卡(默认指令内容为「打卡」)", true), Triple("DT#状态查询", "查询当前状态(任务、服务、监听、电量、版本、日期等)", true), - Triple("DT#截屏", "截取目标应用屏幕并通过消息渠道返回给用户", true) + Triple("DT#截屏", "截取目标应用屏幕并通过消息渠道返回给用户", true), + Triple("DT#开启转移", "开启通知转移(将打卡应用通知转发到目标手机)", true), + Triple("DT#关闭转移", "关闭通知转移", true) ) private val clipboard by lazy { getSystemService(ClipboardManager::class.java) } diff --git a/app/src/main/java/com/pengxh/daily/app/ui/SettingsActivity.kt b/app/src/main/java/com/pengxh/daily/app/ui/SettingsActivity.kt index a81bf7ff..44b6ce7f 100644 --- a/app/src/main/java/com/pengxh/daily/app/ui/SettingsActivity.kt +++ b/app/src/main/java/com/pengxh/daily/app/ui/SettingsActivity.kt @@ -360,6 +360,27 @@ class SettingsActivity : KotlinBaseActivity() { SaveKeyValues.saveBoolean(Constant.POWER_SAVE_MODE_KEY, isChecked) } + binding.transferSwitch.setOnCheckedChangeListener { _, isChecked -> + if (syncingSwitchState) { + return@setOnCheckedChangeListener + } + if (isChecked) { + // 通知转移依赖通知监听服务才能收到目标 App 通知 + if (!notificationEnable()) { + "请先开启通知监听".show(this) + binding.transferSwitch.isChecked = false + return@setOnCheckedChangeListener + } + SaveKeyValues.saveBoolean(Constant.NOTIFICATION_TRANSFER_KEY, true) + val channel = SaveKeyValues.loadInt(Constant.MSG_CHANNEL_KEY, Constant.DEFAULT_INDEX) + if (channel !in 0..1) { + "未配置消息渠道,转发可能失败".show(this) + } + } else { + SaveKeyValues.saveBoolean(Constant.NOTIFICATION_TRANSFER_KEY, false) + } + } + binding.introduceLayout.setOnClickListener { navigatePageTo() } @@ -484,6 +505,8 @@ class SettingsActivity : KotlinBaseActivity() { SaveKeyValues.loadBoolean(Constant.BACK_TO_HOME_KEY, false) binding.powerSaveSwitch.isChecked = SaveKeyValues.loadBoolean(Constant.POWER_SAVE_MODE_KEY, false) + binding.transferSwitch.isChecked = + SaveKeyValues.loadBoolean(Constant.NOTIFICATION_TRANSFER_KEY, false) } finally { syncingSwitchState = false } diff --git a/app/src/main/java/com/pengxh/daily/app/utils/Constant.kt b/app/src/main/java/com/pengxh/daily/app/utils/Constant.kt index d194dd4f..b792948c 100644 --- a/app/src/main/java/com/pengxh/daily/app/utils/Constant.kt +++ b/app/src/main/java/com/pengxh/daily/app/utils/Constant.kt @@ -28,6 +28,7 @@ object Constant { const val RANDOM_TIME_KEY = "RANDOM_TIME_KEY" // 随机时间(Boolean) const val SKIP_HOLIDAY_KEY = "SKIP_HOLIDAY_KEY" // 跳过节假日(Boolean) const val POWER_SAVE_MODE_KEY = "POWER_SAVE_MODE_KEY" // 省电模式(Boolean) + const val NOTIFICATION_TRANSFER_KEY = "NOTIFICATION_TRANSFER_KEY" // 通知转移:将打卡应用通知经消息渠道转发到目标手机(Boolean) // 不导出的sp缓存 const val LAST_RESET_DATE_KEY = "LAST_RESET_DATE_KEY" diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml index 53ba4788..d0edb291 100644 --- a/app/src/main/res/layout/activity_settings.xml +++ b/app/src/main/res/layout/activity_settings.xml @@ -272,6 +272,48 @@ android:layout_centerVertical="true" /> + + + + + + + + + + + + + +