fix(android): retry copy-link click after share rerender (#133)

This commit is contained in:
QiuSW
2026-08-29 10:51:42 +08:00
parent 9b044ed0e5
commit b453634b11
3 changed files with 60 additions and 8 deletions
+2 -2
View File
@@ -11,8 +11,8 @@ android {
applicationId = "cn.ilapage.goauto.agent"
minSdk = 23
targetSdk = 34
versionCode = 32
versionName = "0.9.19"
versionCode = 33
versionName = "0.9.20"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
@@ -146,13 +146,10 @@ class CurrentPageIdentityRunner(
AgentDiagnosticReason.PANEL_FOUND,
elapsedMs = now() - startedAt,
)
val copy = uniqueAction(panel, config.copyLinkAliases, topOnly = false)
?: return failure("PDD_COPY_LINK_UNAVAILABLE", "复制链接入口不唯一")
val copiedAt = now()
when (driver.clickFresh(copy)) {
FreshActionResult.SUCCESS -> Unit
FreshActionResult.AMBIGUOUS -> return failure("PDD_COPY_LINK_UNAVAILABLE", "复制链接入口不唯一")
else -> return failure("PDD_COPY_LINK_UNAVAILABLE", "复制商品链接失败")
val copyResult = clickCopyLinkWithRetry(config.copyLinkAliases)
if (!copyResult.successful) {
return failure("PDD_COPY_LINK_UNAVAILABLE", copyResult.message)
}
val clipboard = readFreshClipboard(copiedAt, config.clipboardTimeoutMs)
diagnose(
@@ -354,6 +351,35 @@ class CurrentPageIdentityRunner(
}
return ShareClickResult(false, message)
}
private fun clickCopyLinkWithRetry(aliases: List<String>): ShareClickResult {
val deadline = now() + COPY_LINK_CLICK_RETRY_MILLIS
var lastReason = FreshClickReason.TARGET_NOT_FOUND
do {
val snapshot = driver.capture()
detailProblem(snapshot)?.let { return ShareClickResult(false, it.message) }
val candidates = actionCandidates(snapshot, aliases, topOnly = false)
if (candidates.size > 1) return ShareClickResult(false, "复制链接入口不唯一")
val target = candidates.singleOrNull()
if (target != null) {
val outcome = driver.clickFreshDetailed(target)
lastReason = outcome.reason
when (outcome.result) {
FreshActionResult.SUCCESS -> return ShareClickResult(true, "")
FreshActionResult.AMBIGUOUS -> return ShareClickResult(false, "复制链接入口不唯一")
FreshActionResult.NOT_FOUND, FreshActionResult.FAILED -> Unit
}
}
pause(POLL_MILLIS)
} while (now() <= deadline)
return ShareClickResult(
false,
when (lastReason) {
FreshClickReason.NO_CLICKABLE_ANCESTOR -> "复制链接入口暂时无法点击"
else -> "复制商品链接失败"
},
)
}
private fun waitForAction(aliases: List<String>, timeoutMillis: Long): UiSnapshot? {
val deadline = now() + timeoutMillis
@@ -517,6 +543,7 @@ class CurrentPageIdentityRunner(
const val POLL_MILLIS = 100L
const val SHARE_STABLE_MILLIS = 300L
const val SHARE_CLICK_RETRY_MILLIS = 1_000L
const val COPY_LINK_CLICK_RETRY_MILLIS = 1_000L
const val PDD_PACKAGE = "com.xunmeng.pinduoduo"
}
}
@@ -294,6 +294,31 @@ class CurrentPageCollectionTest {
assertTrue(events.any { it.reason == AgentDiagnosticReason.LONG_LINK_DIRECT })
}
@Test
fun retriesCopyLinkWhenSharePanelRerendersBeforeFreshClick() {
var currentTime = 0L
val driver = FakeDriver(
snapshots = List(6) { detailSnapshot() } + List(4) { sharePanelSnapshot() },
freshOutcomes = ArrayDeque(
listOf(
FreshClickOutcome(FreshActionResult.SUCCESS, FreshClickReason.SUCCESS, candidateCount = 1),
FreshClickOutcome(FreshActionResult.NOT_FOUND, FreshClickReason.TARGET_NOT_FOUND),
FreshClickOutcome(FreshActionResult.SUCCESS, FreshClickReason.SUCCESS, candidateCount = 1),
),
),
)
val result = CurrentPageIdentityRunner(
driver = driver,
readFreshClipboard = { _, _ -> ClipboardReadOutcome("https://mobile.yangkeduo.com/goods.html?goods_id=12345") },
now = { currentTime },
pause = { currentTime += it },
).identify(rule())
assertTrue(result.successful)
assertEquals(listOf("分享", "复制链接", "复制链接"), driver.clicked)
}
private class FakeDriver(
private val snapshots: List<UiSnapshot>,
private val freshOutcomes: ArrayDeque<FreshClickOutcome> = ArrayDeque(),