fix(#65): tap exact purchase address entry
This commit is contained in:
+46
@@ -149,6 +149,52 @@ class GoAutoAccessibilityService : AccessibilityService(), UiDriver, PddCollecto
|
||||
return if (node.performAction(AccessibilityNodeInfo.ACTION_CLICK)) FreshActionResult.SUCCESS else FreshActionResult.FAILED
|
||||
}
|
||||
|
||||
override fun tapPurchaseFresh(target: SnapshotNode): FreshActionResult {
|
||||
val root = rootInActiveWindow ?: return FreshActionResult.NOT_FOUND
|
||||
val candidates = mutableListOf<AccessibilityNodeInfo>()
|
||||
walk(root) { node ->
|
||||
val ownLabel = (node.text ?: node.contentDescription)?.toString()?.trim().orEmpty()
|
||||
val bounds = Rect().also(node::getBoundsInScreen)
|
||||
if (ownLabel == target.label &&
|
||||
node.className?.toString() == target.className &&
|
||||
node.isVisibleToUser && node.isEnabled &&
|
||||
kotlin.math.abs(bounds.centerX() - target.bounds.centerX) <= 32 &&
|
||||
kotlin.math.abs(bounds.centerY() - target.bounds.centerY) <= 32
|
||||
) candidates += node
|
||||
}
|
||||
if (candidates.isEmpty()) return FreshActionResult.NOT_FOUND
|
||||
if (candidates.size != 1) return FreshActionResult.AMBIGUOUS
|
||||
val bounds = Rect().also(candidates.single()::getBoundsInScreen)
|
||||
if (bounds.width() < 2 || bounds.height() < 2 || Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
|
||||
return FreshActionResult.FAILED
|
||||
}
|
||||
val path = Path().apply {
|
||||
moveTo(bounds.centerX().toFloat(), bounds.centerY().toFloat())
|
||||
}
|
||||
val completed = AtomicBoolean(false)
|
||||
val latch = CountDownLatch(1)
|
||||
val queued = dispatchGesture(
|
||||
GestureDescription.Builder().addStroke(GestureDescription.StrokeDescription(path, 0, 80)).build(),
|
||||
object : GestureResultCallback() {
|
||||
override fun onCompleted(gestureDescription: GestureDescription?) {
|
||||
completed.set(true)
|
||||
latch.countDown()
|
||||
}
|
||||
|
||||
override fun onCancelled(gestureDescription: GestureDescription?) {
|
||||
latch.countDown()
|
||||
}
|
||||
},
|
||||
null,
|
||||
)
|
||||
if (!queued) return FreshActionResult.FAILED
|
||||
return if (latch.await(1_500, TimeUnit.MILLISECONDS) && completed.get()) {
|
||||
FreshActionResult.SUCCESS
|
||||
} else {
|
||||
FreshActionResult.FAILED
|
||||
}
|
||||
}
|
||||
|
||||
override fun inputFresh(target: SnapshotNode, value: String): FreshActionResult {
|
||||
val root = rootInActiveWindow ?: return FreshActionResult.NOT_FOUND
|
||||
val candidates = mutableListOf<AccessibilityNodeInfo>()
|
||||
|
||||
+9
-4
@@ -40,9 +40,14 @@ class PurchaseLiveAutomation(
|
||||
var unchangedCount = 0
|
||||
for (attempt in 0 until 5) {
|
||||
pageProblem(snapshot)
|
||||
val entries = mergedClickable(snapshot, snapshot.nodes.filter { it.visible && MASKED_PHONE.containsMatchIn(it.label) })
|
||||
val entries = mergedDirect(snapshot.nodes.filter { it.visible && it.enabled && MASKED_PHONE.containsMatchIn(it.label) })
|
||||
if (entries.size == 1) {
|
||||
click(entries.single(), "收货地址入口")
|
||||
when (driver.tapPurchaseFresh(entries.single())) {
|
||||
FreshActionResult.SUCCESS -> Unit
|
||||
FreshActionResult.AMBIGUOUS -> fail("PURCHASE_ADDRESS_ENTRY_AMBIGUOUS", "收货地址入口不唯一,未创建订单")
|
||||
else -> fail("PURCHASE_ADDRESS_ENTRY_NOT_READY", "收货地址入口点击失败,未创建订单")
|
||||
}
|
||||
pause(1_000)
|
||||
snapshot = waitFor("PURCHASE_ADDRESS_PANEL_TIMEOUT", "收货地址页面打开超时,未创建订单") {
|
||||
it.nodes.any { node -> node.visible && node.label.replace(" ", "") == "收货地址" }
|
||||
}
|
||||
@@ -192,8 +197,8 @@ class PurchaseLiveAutomation(
|
||||
}.distinctBy { it.path }
|
||||
}
|
||||
|
||||
private fun mergedClickable(snapshot: UiSnapshot, nodes: List<SnapshotNode>): List<SnapshotNode> {
|
||||
val candidates = uniqueClickable(snapshot, nodes)
|
||||
private fun mergedDirect(nodes: List<SnapshotNode>): List<SnapshotNode> {
|
||||
val candidates = nodes.distinctBy { it.path }
|
||||
val merged = mutableListOf<SnapshotNode>()
|
||||
candidates.sortedBy { it.bounds.width.toLong() * it.bounds.height }.forEach { candidate ->
|
||||
if (merged.none { existing -> nearDuplicate(existing.bounds, candidate.bounds) }) merged += candidate
|
||||
|
||||
+1
@@ -6,6 +6,7 @@ import java.net.URLDecoder
|
||||
interface PurchaseUiDriver {
|
||||
fun capture(): UiSnapshot
|
||||
fun clickFresh(target: SnapshotNode): FreshActionResult
|
||||
fun tapPurchaseFresh(target: SnapshotNode): FreshActionResult
|
||||
fun inputFresh(target: SnapshotNode, value: String): FreshActionResult
|
||||
fun swipePurchase(direction: SwipeDirection, durationMs: Long): Boolean
|
||||
fun swipePurchaseIn(target: SnapshotNode, direction: SwipeDirection, durationMs: Long): Boolean
|
||||
|
||||
@@ -22,6 +22,7 @@ class PurchaseLiveAutomationTest {
|
||||
val automation = PurchaseLiveAutomation(driver, pause = {})
|
||||
val address = automation.updateShippingAddress("_cg11")
|
||||
assertEquals("广东省广州市天园街道骏景花园骏晖轩1202_cg11", address.expectedAddress)
|
||||
assertEquals(1, driver.addressTaps)
|
||||
val final = automation.finalConfirmation(input(), address)
|
||||
assertEquals("_cg11", final.addressSuffix)
|
||||
|
||||
@@ -93,6 +94,7 @@ class PurchaseLiveAutomationTest {
|
||||
var submitClicks = 0
|
||||
var scopedSwipes = 0
|
||||
var genericSwipes = 0
|
||||
var addressTaps = 0
|
||||
|
||||
override fun capture(): UiSnapshot = when (page) {
|
||||
"panel" -> snapshot(listOf(node("title", "收货地址"), node("modify", "修改", clickable = true), node("address", address)))
|
||||
@@ -107,7 +109,7 @@ class PurchaseLiveAutomationTest {
|
||||
)
|
||||
if (duplicatePanels) nodes += node("panel2", "", scrollable = true, bounds = NodeBounds(0, 500, 1080, 2000))
|
||||
if (addressVisible) {
|
||||
nodes += node("phone", "138****5678", clickable = true)
|
||||
nodes += node("phone", "138****5678")
|
||||
nodes += node("address", address)
|
||||
}
|
||||
snapshot(nodes)
|
||||
@@ -125,6 +127,13 @@ class PurchaseLiveAutomationTest {
|
||||
return FreshActionResult.SUCCESS
|
||||
}
|
||||
|
||||
override fun tapPurchaseFresh(target: SnapshotNode): FreshActionResult {
|
||||
addressTaps++
|
||||
clicked += target.label
|
||||
if (target.label == "138****5678") page = "panel"
|
||||
return FreshActionResult.SUCCESS
|
||||
}
|
||||
|
||||
override fun inputFresh(target: SnapshotNode, value: String): FreshActionResult { address = value; return FreshActionResult.SUCCESS }
|
||||
override fun swipePurchase(direction: SwipeDirection, durationMs: Long): Boolean { genericSwipes++; return true }
|
||||
override fun swipePurchaseIn(target: SnapshotNode, direction: SwipeDirection, durationMs: Long): Boolean {
|
||||
|
||||
@@ -287,6 +287,8 @@ class PurchaseRehearsalExecutorTest {
|
||||
return FreshActionResult.SUCCESS
|
||||
}
|
||||
|
||||
override fun tapPurchaseFresh(target: SnapshotNode): FreshActionResult = clickFresh(target)
|
||||
|
||||
override fun inputFresh(target: SnapshotNode, value: String): FreshActionResult {
|
||||
quantity = value.toLong()
|
||||
return FreshActionResult.SUCCESS
|
||||
|
||||
Reference in New Issue
Block a user