From 1b285fbaea4fe3737b7000af9e03777576554c6e Mon Sep 17 00:00:00 2001 From: Hamza-Ayed Date: Tue, 30 Jun 2026 00:22:47 +0300 Subject: [PATCH] Update: 2026-06-30 00:22:46 --- .../executionHistory/executionHistory.bin | Bin 867540 -> 867540 bytes .../executionHistory/executionHistory.lock | Bin 17 -> 17 bytes .../.gradle/8.13/fileHashes/fileHashes.bin | Bin 72465 -> 72465 bytes .../.gradle/8.13/fileHashes/fileHashes.lock | Bin 17 -> 17 bytes .../8.13/fileHashes/resourceHashesCache.bin | Bin 19993 -> 20231 bytes .../buildOutputCleanup.lock | Bin 17 -> 17 bytes android_bot/app/src/main/AndroidManifest.xml | 10 ++ .../siro/android_bot/network/WorkerClient.kt | 2 +- .../service/ScraperAccessibilityService.kt | 128 +++++++++++++++--- 9 files changed, 123 insertions(+), 17 deletions(-) diff --git a/android_bot/.gradle/8.13/executionHistory/executionHistory.bin b/android_bot/.gradle/8.13/executionHistory/executionHistory.bin index 5c5a01e34ec2c21089143e1523fecc707176a114..965cd613c7b66ef727e25ad9ae87876e3dcbde30 100644 GIT binary patch delta 4062 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 delta 3968 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 PUHSl{Zq-FW_#DE2evjSZ diff --git a/android_bot/.gradle/8.13/fileHashes/fileHashes.lock b/android_bot/.gradle/8.13/fileHashes/fileHashes.lock index 86289929e545d89ea22bba7e2ed41e3da4cc3163..8cb9ccae1d1116517750955f4644cb0e8bc573ad 100644 GIT binary patch literal 17 VcmZQpDUVz8=j>v01~6dW0{}G(1q%QG literal 17 VcmZQpDUVz8=j>v01~6cj0su5=1Ze;O diff --git a/android_bot/.gradle/8.13/fileHashes/resourceHashesCache.bin b/android_bot/.gradle/8.13/fileHashes/resourceHashesCache.bin index 45f9aa0b5262736f775acea5825f8726cbc20ec7..9d8a44fdc45fa46716c47bafb1d52cd8249b789e 100644 GIT binary patch delta 327 zcmbO^hp~Mg;|3E6M&rq*5)%X#TxH{koAXPA0Sx?}PTni=L*UAj2n~InpdP4T{p7Wh z3L3Yra-0(n{q6}Bl>83`cRV~M{1nnZ1r=X7`KY9Vz}@pKF{Se+*g*wjC*PH{5MVZ* zB)>4p>={(hdNZ$71fzgmnCjp2micF)qK7xfN;5Mu-rT78LwuuwgYafYj~lFV#z0v~ zAQl8+feU%zZ?B$CdB5-5s*@WRE|Q!a?PDUa;>9(g;?)bE{bLpuFq3H3pS;oAB*Jm~ zo`dO%8*fzp@&43W_-!pvJDOQ)IqM`t&Y%4x8xl9^!|&?Fm@4+nbgL8GVXA$9ZjM9W J9|n+97?6|Q81R$j85ol-8W@vZ8Zfh^8c+d~4IKLcF|i@| N7qKwt6SF}`*abd)6vY4l diff --git a/android_bot/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/android_bot/.gradle/buildOutputCleanup/buildOutputCleanup.lock index 98ac2328f31fe187d6a8d36b9fed3d2b5563ccdd..3f403c28df857d560ffa47c5624d02e3874914a8 100644 GIT binary patch literal 17 VcmZSn@TkOm3HOb23}C?Y002H<1-JkJ literal 17 VcmZSn@TkOm3HOb23}C=`2LL`=1+xGE diff --git a/android_bot/app/src/main/AndroidManifest.xml b/android_bot/app/src/main/AndroidManifest.xml index e7164506..5148acb5 100644 --- a/android_bot/app/src/main/AndroidManifest.xml +++ b/android_bot/app/src/main/AndroidManifest.xml @@ -5,6 +5,16 @@ + + + + + + + + + + handleYallaGoAutomation(rootNode) "com.zakinn.app" -> handleZakinnAutomation(rootNode) @@ -497,29 +505,92 @@ class ScraperAccessibilityService : AccessibilityService() { private fun handleJeenyAutomation(rootNode: android.view.accessibility.AccessibilityNodeInfo) { val task = currentTask ?: return Log.d(TAG, "Jeeny Automation event. State: $currentState") + when (currentState) { BotState.SEARCHING_START -> { - val pickupEdit = findEditableNode(rootNode) - if (pickupEdit != null) { - val startLoc = task.optString("start_location", "Amman") - val arguments = android.os.Bundle().apply { - putCharSequence(android.view.accessibility.AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, startLoc) + // Look for the "Where to" button/view on the main screen + val whereToNode = findNodeByText(rootNode, "إلى اين تريد الذهاب") + ?: findNodeByText(rootNode, "Where to") + ?: findNodeByText(rootNode, "إلى أين") + + if (whereToNode != null) { + // Click the parent clickable view if the TextView itself is not clickable + var clickableParent = whereToNode + while (clickableParent != null && !clickableParent.isClickable) { + clickableParent = clickableParent.parent + } + if (clickableParent != null) { + clickableParent.performAction(android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK) + Log.i(TAG, "Jeeny: Clicked where to button/view.") + currentState = BotState.SEARCHING_END + } else { + whereToNode.performAction(android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK) + Log.i(TAG, "Jeeny: Clicked where to button/view (direct).") + currentState = BotState.SEARCHING_END + } + } else { + // If we are already on the search screen (we don't see "Where to" main button, but we see EditTexts) + val editTexts = findAllEditTexts(rootNode) + if (editTexts.isNotEmpty()) { + Log.i(TAG, "Jeeny: Already on search screen with ${editTexts.size} input fields.") + currentState = BotState.SEARCHING_END } - pickupEdit.performAction(android.view.accessibility.AccessibilityNodeInfo.ACTION_SET_TEXT, arguments) - Log.i(TAG, "Jeeny: Entered start: $startLoc") - currentState = BotState.SEARCHING_END } } BotState.SEARCHING_END -> { - val destEdit = findEditableNode(rootNode) - if (destEdit != null) { - val endLoc = task.optString("end_location", "Airport") - val arguments = android.os.Bundle().apply { - putCharSequence(android.view.accessibility.AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, endLoc) + val endLoc = task.optString("end_location", "Airport") + val firstWord = endLoc.split(" ").firstOrNull() ?: "" + val resultNode = if (firstWord.isNotEmpty()) findNodeByText(rootNode, firstWord) else null + val isEditText = resultNode?.className == "android.widget.EditText" || resultNode?.isEditable == true + + // If a search result item containing the target location words is found (not the input field itself) + if (resultNode != null && !isEditText) { + var clickableParent = resultNode + while (clickableParent != null && !clickableParent.isClickable) { + clickableParent = clickableParent.parent + } + if (clickableParent != null) { + clickableParent.performAction(android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK) + Log.i(TAG, "Jeeny: Clicked search result containing '$firstWord'.") + currentState = BotState.READING_PRICE + return + } + } + + // Otherwise, enter the locations to populate the list + val editTexts = findAllEditTexts(rootNode) + if (editTexts.isNotEmpty()) { + val startLoc = task.optString("start_location", "Amman") + + if (editTexts.size >= 2) { + val pickupField = editTexts[0] + val destField = editTexts[1] + + if (pickupField.text?.toString() != startLoc) { + val pickupArgs = android.os.Bundle().apply { + putCharSequence(android.view.accessibility.AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, startLoc) + } + pickupField.performAction(android.view.accessibility.AccessibilityNodeInfo.ACTION_SET_TEXT, pickupArgs) + Log.i(TAG, "Jeeny: Set pickup -> $startLoc") + } + + if (destField.text?.toString() != endLoc) { + val destArgs = android.os.Bundle().apply { + putCharSequence(android.view.accessibility.AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, endLoc) + } + destField.performAction(android.view.accessibility.AccessibilityNodeInfo.ACTION_SET_TEXT, destArgs) + Log.i(TAG, "Jeeny: Set destination -> $endLoc") + } + } else { + val destField = editTexts[0] + if (destField.text?.toString() != endLoc) { + val destArgs = android.os.Bundle().apply { + putCharSequence(android.view.accessibility.AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, endLoc) + } + destField.performAction(android.view.accessibility.AccessibilityNodeInfo.ACTION_SET_TEXT, destArgs) + Log.i(TAG, "Jeeny: Set single field -> $endLoc") + } } - destEdit.performAction(android.view.accessibility.AccessibilityNodeInfo.ACTION_SET_TEXT, arguments) - Log.i(TAG, "Jeeny: Entered end: $endLoc") - currentState = BotState.READING_PRICE } } BotState.READING_PRICE -> { @@ -529,6 +600,31 @@ class ScraperAccessibilityService : AccessibilityService() { } } + private fun dumpNodeHierarchy(node: android.view.accessibility.AccessibilityNodeInfo?, depth: Int) { + if (node == null) return + val indent = " ".repeat(depth) + val className = node.className ?: "unknown" + val text = node.text ?: "" + val resourceId = node.viewIdResourceName ?: "no-id" + val isClickable = node.isClickable + val isEditable = node.isEditable + Log.d("HierarchyDump", "$indent[$className] ID: $resourceId | Text: \"$text\" | Clickable: $isClickable | Editable: $isEditable") + for (i in 0 until node.childCount) { + dumpNodeHierarchy(node.getChild(i), depth + 1) + } + } + + private fun findAllEditTexts(node: android.view.accessibility.AccessibilityNodeInfo?, list: MutableList = mutableListOf()): List { + if (node == null) return list + if (node.className == "android.widget.EditText" || node.isEditable) { + list.add(node) + } + for (i in 0 until node.childCount) { + findAllEditTexts(node.getChild(i), list) + } + return list + } + override fun onInterrupt() { Log.w(TAG, "Accessibility Service Interrupted") }