diff --git a/android/app/src/main/java/cn/ilapage/goauto/agent/MainActivity.kt b/android/app/src/main/java/cn/ilapage/goauto/agent/MainActivity.kt index 146b9d5..da3f61b 100644 --- a/android/app/src/main/java/cn/ilapage/goauto/agent/MainActivity.kt +++ b/android/app/src/main/java/cn/ilapage/goauto/agent/MainActivity.kt @@ -12,11 +12,15 @@ import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.Fragment import cn.ilapage.goauto.agent.service.AgentForegroundService import cn.ilapage.goauto.agent.service.AgentSettingsStore +import cn.ilapage.goauto.agent.ui.AccessibilityPauseText +import cn.ilapage.goauto.agent.ui.AccessibilityReadiness import cn.ilapage.goauto.agent.ui.AgentSettingsFragment +import cn.ilapage.goauto.agent.ui.AgentUiVisibility import cn.ilapage.goauto.agent.ui.AgentStatusFragment import cn.ilapage.goauto.agent.ui.TaskHistoryFragment import cn.ilapage.goauto.agent.update.AgentAppUpdateManager import com.google.android.material.bottomnavigation.BottomNavigationView +import com.google.android.material.dialog.MaterialAlertDialogBuilder class MainActivity : AppCompatActivity() { private var selectedTab = TAB_STATUS @@ -30,6 +34,18 @@ class MainActivity : AppCompatActivity() { } } + private var accessibilityDialog: androidx.appcompat.app.AlertDialog? = null + private val accessibilityReceiver = object : android.content.BroadcastReceiver() { + override fun onReceive(context: android.content.Context?, intent: android.content.Intent?) { + if (intent?.action != AgentForegroundService.ACTION_ACCESSIBILITY_ALERT) return + val readiness = runCatching { + AccessibilityReadiness.valueOf(intent.getStringExtra(AgentForegroundService.EXTRA_ACCESSIBILITY_READINESS).orEmpty()) + }.getOrNull() ?: return + showAccessibilityDialog(readiness) + } + } + private var accessibilityReceiverRegistered = false + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) selectedTab = AgentTabSelectionPolicy.initialTab( @@ -53,9 +69,15 @@ class MainActivity : AppCompatActivity() { cn.ilapage.goauto.agent.automation.GoAutoAccessibilityService.instance?.dismissPurchaseResultBubble() screenPolicyHandler.removeCallbacks(screenPolicyRefresh) screenPolicyHandler.post(screenPolicyRefresh) + AgentUiVisibility.resumed = true + registerAccessibilityReceiver() } override fun onPause() { + AgentUiVisibility.resumed = false + unregisterAccessibilityReceiver() + accessibilityDialog?.dismiss() + accessibilityDialog = null screenPolicyHandler.removeCallbacks(screenPolicyRefresh) super.onPause() } @@ -65,6 +87,38 @@ class MainActivity : AppCompatActivity() { super.onSaveInstanceState(outState) } + private fun showAccessibilityDialog(readiness: AccessibilityReadiness) { + accessibilityDialog?.dismiss() + accessibilityDialog = null + val detail = AccessibilityPauseText.recovery(readiness) ?: return // READY:恢复,仅关闭弹窗 + accessibilityDialog = MaterialAlertDialogBuilder(this) + .setTitle(AccessibilityPauseText.TITLE) + .setMessage(detail) + .setNegativeButton("稍后", null) + .setPositiveButton("去设置") { _, _ -> + startActivity(android.content.Intent(android.provider.Settings.ACTION_ACCESSIBILITY_SETTINGS)) + } + .show() + } + + private fun registerAccessibilityReceiver() { + if (accessibilityReceiverRegistered) return + val filter = android.content.IntentFilter(AgentForegroundService.ACTION_ACCESSIBILITY_ALERT) + if (Build.VERSION.SDK_INT >= 33) { + registerReceiver(accessibilityReceiver, filter, RECEIVER_NOT_EXPORTED) + } else { + @Suppress("DEPRECATION") + registerReceiver(accessibilityReceiver, filter) + } + accessibilityReceiverRegistered = true + } + + private fun unregisterAccessibilityReceiver() { + if (!accessibilityReceiverRegistered) return + runCatching { unregisterReceiver(accessibilityReceiver) } + accessibilityReceiverRegistered = false + } + private fun buildContent(): LinearLayout { val content = LinearLayout(this).apply { orientation = LinearLayout.VERTICAL diff --git a/android/app/src/main/java/cn/ilapage/goauto/agent/service/AgentForegroundService.kt b/android/app/src/main/java/cn/ilapage/goauto/agent/service/AgentForegroundService.kt index 28e5513..8bf3ee0 100644 --- a/android/app/src/main/java/cn/ilapage/goauto/agent/service/AgentForegroundService.kt +++ b/android/app/src/main/java/cn/ilapage/goauto/agent/service/AgentForegroundService.kt @@ -79,6 +79,13 @@ import cn.ilapage.goauto.agent.diagnostics.AccessibilitySnapshotSource import cn.ilapage.goauto.agent.diagnostics.FailureSnapshotCapture import cn.ilapage.goauto.agent.diagnostics.FailureSnapshotPolicy import cn.ilapage.goauto.agent.diagnostics.SnapshotContext +import cn.ilapage.goauto.agent.ui.AccessibilityAlertAction +import cn.ilapage.goauto.agent.ui.AccessibilityAlertTracker +import cn.ilapage.goauto.agent.ui.AccessibilityDispatchGate +import cn.ilapage.goauto.agent.ui.AccessibilityPauseText +import cn.ilapage.goauto.agent.ui.AccessibilityReadiness +import cn.ilapage.goauto.agent.ui.AccessibilityReadinessDetector +import cn.ilapage.goauto.agent.ui.AgentUiVisibility import cn.ilapage.goauto.agent.ui.PurchaseResultBubblePolicy import org.json.JSONArray import org.json.JSONObject @@ -103,6 +110,7 @@ class AgentForegroundService : Service() { private var repurchaseSession: RepurchaseSession? = null private val repurchaseEvidence = java.util.concurrent.ConcurrentHashMap>() private val taskMutex = TaskExecutionMutex() + private val accessibilityAlerts = AccessibilityAlertTracker() private val probeHandoff = PurchaseProbeHandoff() private val backfillGuard = OrderBackfillGuard(taskMutex) private val runningTaskId = AtomicReference(null) @@ -443,6 +451,15 @@ class AgentForegroundService : Service() { private fun scheduleTask(api: AgentApiClient, token: String): String { if (taskMutex.currentTaskId() != null) return MANUAL_BUSY + val readiness = AccessibilityReadinessDetector.current(this) + handleAccessibilityAlert(readiness) + if (!AccessibilityDispatchGate.allowsClaiming(readiness)) { + // 不调用任何领取/查询下一任务接口,任务留在服务端 pending;恢复后下一轮同步自动继续。 + cancelIdleReturn("无障碍未就绪") + stateStore.update("ONLINE", AccessibilityPauseText.TITLE, tokenStored = true) + updateNotification(AccessibilityPauseText.TITLE) + return MANUAL_ACCESSIBILITY_NOT_READY + } val collectionCooldown = activeCollectionCooldown() val purchaseTask = api.nextPurchaseTask(token) when (TaskDispatchPolicy.decide(purchaseTask?.status, collectionCooldown != null)) { @@ -489,6 +506,50 @@ class AgentForegroundService : Service() { return MANUAL_COLLECTION_TASK } + private fun handleAccessibilityAlert(readiness: AccessibilityReadiness) { + val action = accessibilityAlerts.onReadiness(readiness) + if (action == AccessibilityAlertAction.NONE) return + val manager = getSystemService(NotificationManager::class.java) + if (action == AccessibilityAlertAction.CANCEL) { + manager.cancel(ACCESSIBILITY_ALERT_NOTIFICATION_ID) + } else if (!AgentUiVisibility.resumed) { + postAccessibilityAlertNotification(manager, readiness) + } + // 前台时由 MainActivity 收到广播后弹窗;恢复时同一广播让它关闭弹窗。 + sendBroadcast(Intent(ACTION_ACCESSIBILITY_ALERT).apply { + setPackage(packageName) + putExtra(EXTRA_ACCESSIBILITY_READINESS, readiness.name) + }) + } + + private fun postAccessibilityAlertNotification(manager: NotificationManager, readiness: AccessibilityReadiness) { + // 通知权限被拒时静默跳过,不新增请求流程。 + if (!manager.areNotificationsEnabled()) return + runCatching { + val pendingIntent = PendingIntent.getActivity( + this, 1, Intent(this, MainActivity::class.java), + PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT, + ) + val builder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + Notification.Builder(this, ACCESSIBILITY_ALERT_CHANNEL_ID) + } else { + @Suppress("DEPRECATION") + Notification.Builder(this).setPriority(Notification.PRIORITY_HIGH) + } + val detail = AccessibilityPauseText.recovery(readiness) + manager.notify( + ACCESSIBILITY_ALERT_NOTIFICATION_ID, + builder.setSmallIcon(android.R.drawable.stat_sys_warning) + .setContentTitle(AccessibilityPauseText.TITLE) + .setContentText(detail) + .setStyle(Notification.BigTextStyle().bigText(detail)) + .setContentIntent(pendingIntent) + .setAutoCancel(true) + .build(), + ) + } + } + private fun publishManualCheckResult(result: String) { sendBroadcast(Intent(ACTION_CHECK_RESULT).apply { setPackage(packageName) @@ -1760,6 +1821,9 @@ class AgentForegroundService : Service() { manager.createNotificationChannel( NotificationChannel(CHANNEL_ID, "Agent 连接与心跳", NotificationManager.IMPORTANCE_LOW), ) + manager.createNotificationChannel( + NotificationChannel(ACCESSIBILITY_ALERT_CHANNEL_ID, "无障碍未就绪提醒", NotificationManager.IMPORTANCE_HIGH), + ) } } @@ -1877,6 +1941,9 @@ class AgentForegroundService : Service() { const val EXTRA_CURRENT_PAGE_MESSAGE = "current_page_message" const val EXTRA_REPLACEMENT_ORIGIN_TYPE = "replacement_origin_type" const val EXTRA_REPLACEMENT_ORIGIN_TASK_ID = "replacement_origin_task_id" + const val ACTION_ACCESSIBILITY_ALERT = "cn.ilapage.goauto.agent.ACCESSIBILITY_ALERT" + const val EXTRA_ACCESSIBILITY_READINESS = "accessibility_readiness" + const val MANUAL_ACCESSIBILITY_NOT_READY = "accessibility_not_ready" const val MANUAL_EMPTY = "empty" const val MANUAL_COLLECTION_TASK = "collection_task" const val MANUAL_PURCHASE_TASK = "purchase_task" @@ -1889,6 +1956,8 @@ class AgentForegroundService : Service() { const val MANUAL_COLLECTION_COOLDOWN_PREFIX = "collection_cooldown:" private const val CHANNEL_ID = "agent_connection" private const val NOTIFICATION_ID = 1001 + private const val ACCESSIBILITY_ALERT_CHANNEL_ID = "agent_accessibility_alert" + private const val ACCESSIBILITY_ALERT_NOTIFICATION_ID = 1002 private const val HEARTBEAT_SECONDS = 15L private const val TASK_WAKE_LOCK_TIMEOUT_MILLIS = 5 * 60 * 1000L private const val IDLE_RETURN_WAKE_LOCK_TIMEOUT_MILLIS = 30_000L diff --git a/android/app/src/main/java/cn/ilapage/goauto/agent/ui/AccessibilityDispatchGate.kt b/android/app/src/main/java/cn/ilapage/goauto/agent/ui/AccessibilityDispatchGate.kt new file mode 100644 index 0000000..68f355f --- /dev/null +++ b/android/app/src/main/java/cn/ilapage/goauto/agent/ui/AccessibilityDispatchGate.kt @@ -0,0 +1,45 @@ +package cn.ilapage.goauto.agent.ui + +/** 无障碍未就绪时暂停领取任务(#385)。纯策略,便于单元测试。 */ +object AccessibilityDispatchGate { + fun allowsClaiming(readiness: AccessibilityReadiness): Boolean = readiness == AccessibilityReadiness.READY +} + +/** 暂停提示的文案,状态 Tab、手动检查、弹窗和通知共用。 */ +object AccessibilityPauseText { + const val TITLE = "无障碍未就绪,已暂停领取任务" + + fun recovery(readiness: AccessibilityReadiness): String? = when (readiness) { + AccessibilityReadiness.READY -> null + AccessibilityReadiness.ENABLED_WAITING -> "请到 设置 → 无障碍,把 GoAuto 关闭后重新打开" + AccessibilityReadiness.DISABLED -> "请到 设置 → 无障碍 开启 GoAuto" + } +} + +enum class AccessibilityAlertAction { NONE, PROMPT, CANCEL } + +/** + * 只在「就绪 → 未就绪」时提示一次;持续未就绪(含 DISABLED 与 ENABLED_WAITING 互转)不重复; + * 恢复就绪时撤销提示,之后再次失效重新提示。进程内状态,不持久化。 + * 进程启动后的第一次观察视为转变:首次未就绪会提示,首次就绪会撤销可能残留的旧通知。 + */ +class AccessibilityAlertTracker { + private var previous: AccessibilityReadiness? = null + + @Synchronized + fun onReadiness(current: AccessibilityReadiness): AccessibilityAlertAction { + val last = previous + previous = current + val ready = current == AccessibilityReadiness.READY + return when { + ready && last != AccessibilityReadiness.READY -> AccessibilityAlertAction.CANCEL + !ready && (last == null || last == AccessibilityReadiness.READY) -> AccessibilityAlertAction.PROMPT + else -> AccessibilityAlertAction.NONE + } + } +} + +/** Agent 界面是否在前台(MainActivity onResume/onPause 维护),服务据此选择弹窗或通知。 */ +object AgentUiVisibility { + @Volatile var resumed: Boolean = false +} diff --git a/android/app/src/main/java/cn/ilapage/goauto/agent/ui/AgentStatusFragment.kt b/android/app/src/main/java/cn/ilapage/goauto/agent/ui/AgentStatusFragment.kt index 46c3e40..ec9f6ed 100644 --- a/android/app/src/main/java/cn/ilapage/goauto/agent/ui/AgentStatusFragment.kt +++ b/android/app/src/main/java/cn/ilapage/goauto/agent/ui/AgentStatusFragment.kt @@ -23,7 +23,7 @@ import java.util.Date internal object ManualTaskCheckPolicy { fun blockedMessage(stateCode: String, currentTaskId: Long?, readiness: AccessibilityReadiness): String? = when { currentTaskId != null || stateCode == "BUSY" -> "当前任务执行中,无需重复检查" - readiness != AccessibilityReadiness.READY -> "请先到“设置”开启采集采购助手" + readiness != AccessibilityReadiness.READY -> AccessibilityPauseText.recovery(readiness) stateCode == "CONFIG_REQUIRED" -> "请先到“设置”配置服务地址" stateCode == "AUTH_ERROR" -> "设备身份校验失败,请先检查设置" else -> null @@ -38,6 +38,7 @@ internal object ManualTaskCheckPolicy { result == AgentForegroundService.MANUAL_EMPTY -> "暂无新任务,Agent 会继续自动检查" result == AgentForegroundService.MANUAL_COLLECTION_TASK -> "已领取采集任务" result == AgentForegroundService.MANUAL_PURCHASE_TASK -> "已领取采购任务" + result == AgentForegroundService.MANUAL_ACCESSIBILITY_NOT_READY -> AccessibilityPauseText.TITLE result == AgentForegroundService.MANUAL_BUSY -> "当前任务执行中,无需重复检查" result == AgentForegroundService.MANUAL_CONFIG_REQUIRED -> "请先到“设置”配置服务地址" result == AgentForegroundService.MANUAL_AUTH_ERROR -> "设备身份校验失败,请先检查设置" @@ -52,10 +53,17 @@ internal object AgentStatusTextPolicy { "最近心跳:$heartbeat", ).joinToString("\n") - fun accessibilityDetail(readiness: AccessibilityReadiness): String? = when (readiness) { - AccessibilityReadiness.READY -> null - AccessibilityReadiness.ENABLED_WAITING -> "请到“设置”检查无障碍服务" - AccessibilityReadiness.DISABLED -> "请到“设置”开启" + fun accessibilityDetail(readiness: AccessibilityReadiness): String? = AccessibilityPauseText.recovery(readiness) + + fun accessibilityTitle(readiness: AccessibilityReadiness): String = when (readiness) { + AccessibilityReadiness.READY -> "已开启" + AccessibilityReadiness.ENABLED_WAITING -> "未连接,任务已暂停" + AccessibilityReadiness.DISABLED -> "未开启,任务已暂停" + } + + fun accessibilityColor(readiness: AccessibilityReadiness): Int = when (readiness) { + AccessibilityReadiness.READY -> R.color.agent_primary_light + else -> R.color.agent_error } } @@ -109,6 +117,7 @@ class AgentStatusFragment : Fragment() { AgentForegroundService.MANUAL_CONFIG_REQUIRED, AgentForegroundService.MANUAL_NETWORK_ERROR, AgentForegroundService.MANUAL_ERROR, + AgentForegroundService.MANUAL_ACCESSIBILITY_NOT_READY, ), ) } @@ -286,20 +295,8 @@ class AgentStatusFragment : Fragment() { "${if (purchase) "采购" else "采集"}任务 ${if (purchase) "CG-" else "#"}$id" } ?: "等待任务" val readiness = AccessibilityReadinessDetector.current(context) - when (readiness) { - AccessibilityReadiness.READY -> { - accessibilityTitle.text = "已开启" - accessibilityTitle.setTextColor(context.getColor(R.color.agent_primary_light)) - } - AccessibilityReadiness.ENABLED_WAITING -> { - accessibilityTitle.text = "等待连接" - accessibilityTitle.setTextColor(context.getColor(R.color.agent_warning)) - } - AccessibilityReadiness.DISABLED -> { - accessibilityTitle.text = "未开启" - accessibilityTitle.setTextColor(context.getColor(R.color.agent_error)) - } - } + accessibilityTitle.text = AgentStatusTextPolicy.accessibilityTitle(readiness) + accessibilityTitle.setTextColor(context.getColor(AgentStatusTextPolicy.accessibilityColor(readiness))) accessibilityDetail.text = AgentStatusTextPolicy.accessibilityDetail(readiness).orEmpty() accessibilityDetail.visibility = if (accessibilityDetail.text.isEmpty()) View.GONE else View.VISIBLE val mediaReadiness = MediaPermissionPolicy.current(context) diff --git a/android/app/src/test/java/cn/ilapage/goauto/agent/AccessibilityDispatchGateTest.kt b/android/app/src/test/java/cn/ilapage/goauto/agent/AccessibilityDispatchGateTest.kt new file mode 100644 index 0000000..b92c1af --- /dev/null +++ b/android/app/src/test/java/cn/ilapage/goauto/agent/AccessibilityDispatchGateTest.kt @@ -0,0 +1,53 @@ +package cn.ilapage.goauto.agent + +import cn.ilapage.goauto.agent.ui.AccessibilityAlertAction +import cn.ilapage.goauto.agent.ui.AccessibilityAlertTracker +import cn.ilapage.goauto.agent.ui.AccessibilityDispatchGate +import cn.ilapage.goauto.agent.ui.AccessibilityReadiness +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test + +class AccessibilityDispatchGateTest { + @Test fun pausesClaimingWhenDisabledOrWaiting() { + assertFalse(AccessibilityDispatchGate.allowsClaiming(AccessibilityReadiness.DISABLED)) + assertFalse(AccessibilityDispatchGate.allowsClaiming(AccessibilityReadiness.ENABLED_WAITING)) + } + + @Test fun allowsClaimingWhenReady() { + assertTrue(AccessibilityDispatchGate.allowsClaiming(AccessibilityReadiness.READY)) + } + + @Test fun promptsOnceWhenReadyBecomesNotReady() { + val tracker = AccessibilityAlertTracker() + assertEquals(AccessibilityAlertAction.CANCEL, tracker.onReadiness(AccessibilityReadiness.READY)) + assertEquals(AccessibilityAlertAction.NONE, tracker.onReadiness(AccessibilityReadiness.READY)) + assertEquals(AccessibilityAlertAction.PROMPT, tracker.onReadiness(AccessibilityReadiness.ENABLED_WAITING)) + assertEquals(AccessibilityAlertAction.NONE, tracker.onReadiness(AccessibilityReadiness.ENABLED_WAITING)) + assertEquals(AccessibilityAlertAction.NONE, tracker.onReadiness(AccessibilityReadiness.ENABLED_WAITING)) + } + + @Test fun switchingBetweenNotReadyStatesDoesNotPromptAgain() { + val tracker = AccessibilityAlertTracker() + tracker.onReadiness(AccessibilityReadiness.READY) + assertEquals(AccessibilityAlertAction.PROMPT, tracker.onReadiness(AccessibilityReadiness.DISABLED)) + assertEquals(AccessibilityAlertAction.NONE, tracker.onReadiness(AccessibilityReadiness.ENABLED_WAITING)) + assertEquals(AccessibilityAlertAction.NONE, tracker.onReadiness(AccessibilityReadiness.DISABLED)) + } + + @Test fun recoveryCancelsAndNextFailurePromptsAgain() { + val tracker = AccessibilityAlertTracker() + tracker.onReadiness(AccessibilityReadiness.READY) + tracker.onReadiness(AccessibilityReadiness.DISABLED) + assertEquals(AccessibilityAlertAction.CANCEL, tracker.onReadiness(AccessibilityReadiness.READY)) + assertEquals(AccessibilityAlertAction.PROMPT, tracker.onReadiness(AccessibilityReadiness.ENABLED_WAITING)) + } + + @Test fun firstObservationAfterProcessStartPromptsWhenNotReady() { + assertEquals( + AccessibilityAlertAction.PROMPT, + AccessibilityAlertTracker().onReadiness(AccessibilityReadiness.ENABLED_WAITING), + ) + } +} diff --git a/android/app/src/test/java/cn/ilapage/goauto/agent/AccessibilityReadinessTest.kt b/android/app/src/test/java/cn/ilapage/goauto/agent/AccessibilityReadinessTest.kt index f935f06..900c3cb 100644 --- a/android/app/src/test/java/cn/ilapage/goauto/agent/AccessibilityReadinessTest.kt +++ b/android/app/src/test/java/cn/ilapage/goauto/agent/AccessibilityReadinessTest.kt @@ -1,5 +1,6 @@ package cn.ilapage.goauto.agent +import cn.ilapage.goauto.agent.service.AgentForegroundService import cn.ilapage.goauto.agent.ui.AccessibilityReadiness import cn.ilapage.goauto.agent.ui.AccessibilityReadinessResolver import cn.ilapage.goauto.agent.ui.ManualTaskCheckPolicy @@ -46,9 +47,13 @@ class AccessibilityReadinessTest { ManualTaskCheckPolicy.blockedMessage("BUSY", 35L, AccessibilityReadiness.READY), ) assertEquals( - "请先到“设置”开启采集采购助手", + "请到 设置 → 无障碍 开启 GoAuto", ManualTaskCheckPolicy.blockedMessage("ONLINE", null, AccessibilityReadiness.DISABLED), ) + assertEquals( + "请到 设置 → 无障碍,把 GoAuto 关闭后重新打开", + ManualTaskCheckPolicy.blockedMessage("ONLINE", null, AccessibilityReadiness.ENABLED_WAITING), + ) assertEquals(null, ManualTaskCheckPolicy.blockedMessage("ONLINE", null, AccessibilityReadiness.READY)) } @@ -59,5 +64,9 @@ class AccessibilityReadinessTest { assertEquals("已领取采购任务", ManualTaskCheckPolicy.resultMessage("purchase_task")) assertEquals("采集间隔中,还需 12 秒", ManualTaskCheckPolicy.resultMessage("collection_cooldown:12")) assertEquals("检查失败,Agent 将继续自动重试", ManualTaskCheckPolicy.resultMessage("network_error")) + assertEquals( + "无障碍未就绪,已暂停领取任务", + ManualTaskCheckPolicy.resultMessage(AgentForegroundService.MANUAL_ACCESSIBILITY_NOT_READY), + ) } } diff --git a/android/app/src/test/java/cn/ilapage/goauto/agent/AgentStatusTextPolicyTest.kt b/android/app/src/test/java/cn/ilapage/goauto/agent/AgentStatusTextPolicyTest.kt index 50ac289..86d6f1d 100644 --- a/android/app/src/test/java/cn/ilapage/goauto/agent/AgentStatusTextPolicyTest.kt +++ b/android/app/src/test/java/cn/ilapage/goauto/agent/AgentStatusTextPolicyTest.kt @@ -25,12 +25,21 @@ class AgentStatusTextPolicyTest { @Test fun hidesReadyDetailButKeepsRecoveryMessages() { assertNull(AgentStatusTextPolicy.accessibilityDetail(AccessibilityReadiness.READY)) assertEquals( - "请到“设置”检查无障碍服务", + "请到 设置 → 无障碍,把 GoAuto 关闭后重新打开", AgentStatusTextPolicy.accessibilityDetail(AccessibilityReadiness.ENABLED_WAITING), ) assertEquals( - "请到“设置”开启", + "请到 设置 → 无障碍 开启 GoAuto", AgentStatusTextPolicy.accessibilityDetail(AccessibilityReadiness.DISABLED), ) } + + @Test fun accessibilityCardTitleAndColorFollowReadiness() { + assertEquals("已开启", AgentStatusTextPolicy.accessibilityTitle(AccessibilityReadiness.READY)) + assertEquals("未开启,任务已暂停", AgentStatusTextPolicy.accessibilityTitle(AccessibilityReadiness.DISABLED)) + assertEquals("未连接,任务已暂停", AgentStatusTextPolicy.accessibilityTitle(AccessibilityReadiness.ENABLED_WAITING)) + assertEquals(R.color.agent_primary_light, AgentStatusTextPolicy.accessibilityColor(AccessibilityReadiness.READY)) + assertEquals(R.color.agent_error, AgentStatusTextPolicy.accessibilityColor(AccessibilityReadiness.DISABLED)) + assertEquals(R.color.agent_error, AgentStatusTextPolicy.accessibilityColor(AccessibilityReadiness.ENABLED_WAITING)) + } }