Update: 2026-06-30 04:14:29
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -41,6 +41,7 @@ class ScraperAccessibilityService : AccessibilityService() {
|
|||||||
private var taxiFDestinationDone = false
|
private var taxiFDestinationDone = false
|
||||||
private val taxiFCollectedPrices = mutableListOf<Pair<Double, String>>()
|
private val taxiFCollectedPrices = mutableListOf<Pair<Double, String>>()
|
||||||
private var taxiFPriceScrollAttempts = 0
|
private var taxiFPriceScrollAttempts = 0
|
||||||
|
private var taxiFReadingPriceStartTime = 0L
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val TAXIF_EXCLUDED_TEXTS = setOf(
|
private val TAXIF_EXCLUDED_TEXTS = setOf(
|
||||||
@@ -397,21 +398,19 @@ class ScraperAccessibilityService : AccessibilityService() {
|
|||||||
Log.e(TAG, "Failed to submit price.")
|
Log.e(TAG, "Failed to submit price.")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait 3 seconds, then fetch next trip directly (no relaunch)
|
// Wait 3 seconds for price to settle, then fetch next task directly
|
||||||
delay(3000)
|
delay(3000)
|
||||||
|
|
||||||
val nextResult = workerClient.fetchTask()
|
val nextResult = workerClient.fetchTask()
|
||||||
if (nextResult != null && nextResult.optBoolean("has_task", false)) {
|
if (nextResult != null && nextResult.optBoolean("has_task", false)) {
|
||||||
val nextTask = nextResult.getJSONObject("task")
|
val nextTask = nextResult.getJSONObject("task")
|
||||||
val nextApp = nextTask.optString("app")
|
|
||||||
Log.i(TAG, "Multi-trip: Fetched next task for $nextApp, going directly to SEARCHING_START")
|
|
||||||
|
|
||||||
currentTask = nextTask
|
currentTask = nextTask
|
||||||
currentAppName = nextApp
|
|
||||||
taxiFPickupDone = false
|
taxiFPickupDone = false
|
||||||
taxiFDestinationDone = false
|
taxiFDestinationDone = false
|
||||||
currentState = BotState.SEARCHING_START
|
currentState = BotState.SEARCHING_START
|
||||||
|
Log.i(TAG, "TaxiF: Multi-trip fetched next task ${nextTask.optString("task_id")}, state -> SEARCHING_START")
|
||||||
} else {
|
} else {
|
||||||
Log.d(TAG, "Multi-trip: No more tasks, going IDLE.")
|
Log.i(TAG, "No more tasks, returning to IDLE.")
|
||||||
currentState = BotState.IDLE
|
currentState = BotState.IDLE
|
||||||
currentTask = null
|
currentTask = null
|
||||||
}
|
}
|
||||||
@@ -551,6 +550,7 @@ class ScraperAccessibilityService : AccessibilityService() {
|
|||||||
}
|
}
|
||||||
taxiFDestinationDone = true
|
taxiFDestinationDone = true
|
||||||
currentState = BotState.READING_PRICE
|
currentState = BotState.READING_PRICE
|
||||||
|
taxiFReadingPriceStartTime = System.currentTimeMillis()
|
||||||
taxiFCollectedPrices.clear()
|
taxiFCollectedPrices.clear()
|
||||||
taxiFPriceScrollAttempts = 0
|
taxiFPriceScrollAttempts = 0
|
||||||
return
|
return
|
||||||
@@ -561,7 +561,16 @@ class ScraperAccessibilityService : AccessibilityService() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
BotState.READING_PRICE -> {
|
BotState.READING_PRICE -> {
|
||||||
if (!hasJustClickedSuggestion()) {
|
if (hasJustClickedSuggestion()) return
|
||||||
|
if (System.currentTimeMillis() - taxiFReadingPriceStartTime < 2000) return
|
||||||
|
|
||||||
|
val ecoPrice = getEcoPrice(rootNode)
|
||||||
|
if (ecoPrice != null && ecoPrice > 0) {
|
||||||
|
Log.i(TAG, "TaxiF: Found ECO price: $ecoPrice JOD")
|
||||||
|
submitPriceToServer("$ecoPrice JOD")
|
||||||
|
currentState = BotState.IDLE
|
||||||
|
} else if (System.currentTimeMillis() - taxiFReadingPriceStartTime > 15000) {
|
||||||
|
Log.w(TAG, "TaxiF: ECO price not found within 15s timeout, falling back to collectAndScrollPrices")
|
||||||
collectAndScrollPrices(rootNode)
|
collectAndScrollPrices(rootNode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -570,44 +579,34 @@ class ScraperAccessibilityService : AccessibilityService() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun hasJustClickedHome(): Boolean {
|
private fun hasJustClickedHome(): Boolean {
|
||||||
return (System.currentTimeMillis() - taxiFHomeClickTime) < 1500
|
return (System.currentTimeMillis() - taxiFHomeClickTime) < 500
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun hasJustClickedLocation(): Boolean {
|
private fun hasJustClickedLocation(): Boolean {
|
||||||
return (System.currentTimeMillis() - taxiFLocationClickTime) < 1500
|
return (System.currentTimeMillis() - taxiFLocationClickTime) < 500
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun hasJustClickedSuggestion(): Boolean {
|
private fun hasJustClickedSuggestion(): Boolean {
|
||||||
return (System.currentTimeMillis() - taxiFSuggestionClickTime) < 1500
|
return (System.currentTimeMillis() - taxiFSuggestionClickTime) < 500
|
||||||
}
|
|
||||||
|
|
||||||
private fun getLocationTexts(node: android.view.accessibility.AccessibilityNodeInfo?): List<String> {
|
|
||||||
val texts = mutableListOf<String>()
|
|
||||||
collectLocationTexts(node, texts)
|
|
||||||
return texts
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun collectLocationTexts(node: android.view.accessibility.AccessibilityNodeInfo?, texts: MutableList<String>) {
|
|
||||||
if (node == null) return
|
|
||||||
val text = node.text?.toString()?.trim() ?: ""
|
|
||||||
val className = node.className?.toString() ?: ""
|
|
||||||
val isEditText = className == "android.widget.EditText" || node.isEditable
|
|
||||||
if (text.isNotBlank() && text.length > 2 && !isEditText && TAXIF_EXCLUDED_TEXTS.none { text.contains(it) }) {
|
|
||||||
texts.add(text)
|
|
||||||
}
|
|
||||||
for (i in 0 until node.childCount) {
|
|
||||||
collectLocationTexts(node.getChild(i), texts)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun shouldEditLocation(rootNode: android.view.accessibility.AccessibilityNodeInfo, task: JSONObject, isPickup: Boolean): Boolean {
|
private fun shouldEditLocation(rootNode: android.view.accessibility.AccessibilityNodeInfo, task: JSONObject, isPickup: Boolean): Boolean {
|
||||||
val locationTexts = getLocationTexts(rootNode)
|
val locationNodes = getLocationTextNodes(rootNode)
|
||||||
val taskLoc = if (isPickup) task.optString("start_location", "") else task.optString("end_location", "")
|
val taskLoc = if (isPickup) task.optString("start_location", "") else task.optString("end_location", "")
|
||||||
if (taskLoc.isEmpty()) return false
|
if (taskLoc.isEmpty()) return false
|
||||||
val matchFound = locationTexts.any { text ->
|
|
||||||
text.contains(taskLoc, ignoreCase = true) || taskLoc.contains(text, ignoreCase = true)
|
val rowNode = if (isPickup) {
|
||||||
|
locationNodes.firstOrNull()
|
||||||
|
} else {
|
||||||
|
val destIdx = locationNodes.indexOfFirst { it.text?.toString() == "عنوان الإنزال" }
|
||||||
|
if (destIdx >= 0) locationNodes[destIdx] else locationNodes.getOrNull(1)
|
||||||
}
|
}
|
||||||
Log.d(TAG, "TaxiF: Location texts=$locationTexts, taskLoc=$taskLoc, matchFound=$matchFound")
|
|
||||||
|
val rowText = rowNode?.text?.toString()?.trim() ?: ""
|
||||||
|
val matchFound = rowText.contains(taskLoc, ignoreCase = true) || taskLoc.contains(rowText, ignoreCase = true)
|
||||||
|
|
||||||
|
Log.d(TAG, "TaxiF: shouldEditLocation(isPickup=$isPickup): rowText='$rowText', taskLoc='$taskLoc', matchFound=$matchFound, locationNodes=[${locationNodes.joinToString { it.text?.toString() ?: "" }}]")
|
||||||
|
|
||||||
return !matchFound
|
return !matchFound
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -656,7 +655,9 @@ class ScraperAccessibilityService : AccessibilityService() {
|
|||||||
if (editTexts.isEmpty()) return false
|
if (editTexts.isEmpty()) return false
|
||||||
val editField = editTexts[0]
|
val editField = editTexts[0]
|
||||||
val currentText = editField.text?.toString() ?: ""
|
val currentText = editField.text?.toString() ?: ""
|
||||||
if (currentText != location) {
|
val normalizedCurrent = currentText.replace("-", " ").replace("\\s+".toRegex(), " ").trim()
|
||||||
|
val normalizedLocation = location.replace("-", " ").replace("\\s+".toRegex(), " ").trim()
|
||||||
|
if (!normalizedCurrent.contains(normalizedLocation, ignoreCase = true)) {
|
||||||
val arguments = android.os.Bundle().apply {
|
val arguments = android.os.Bundle().apply {
|
||||||
putCharSequence(android.view.accessibility.AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, location)
|
putCharSequence(android.view.accessibility.AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, location)
|
||||||
}
|
}
|
||||||
@@ -689,7 +690,9 @@ class ScraperAccessibilityService : AccessibilityService() {
|
|||||||
private fun suggestionContainsText(node: android.view.accessibility.AccessibilityNodeInfo?, target: String): Boolean {
|
private fun suggestionContainsText(node: android.view.accessibility.AccessibilityNodeInfo?, target: String): Boolean {
|
||||||
if (node == null) return false
|
if (node == null) return false
|
||||||
val text = node.text?.toString() ?: ""
|
val text = node.text?.toString() ?: ""
|
||||||
if (text.contains(target, ignoreCase = true)) return true
|
val normalizedText = text.replace("-", " ").replace("\\s+".toRegex(), " ").trim()
|
||||||
|
val normalizedTarget = target.replace("-", " ").replace("\\s+".toRegex(), " ").trim()
|
||||||
|
if (normalizedText.contains(normalizedTarget, ignoreCase = true)) return true
|
||||||
for (i in 0 until node.childCount) {
|
for (i in 0 until node.childCount) {
|
||||||
if (suggestionContainsText(node.getChild(i), target)) return true
|
if (suggestionContainsText(node.getChild(i), target)) return true
|
||||||
}
|
}
|
||||||
@@ -780,6 +783,25 @@ class ScraperAccessibilityService : AccessibilityService() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getEcoPrice(rootNode: android.view.accessibility.AccessibilityNodeInfo?): Double? {
|
||||||
|
if (rootNode == null) return null
|
||||||
|
val recycler = findHorizontalRecyclerView(rootNode) ?: return null
|
||||||
|
for (i in 0 until recycler.childCount) {
|
||||||
|
val card = recycler.getChild(i) ?: continue
|
||||||
|
if (findNodeByText(card, "ECO") != null) {
|
||||||
|
val jodNode = findNodeByText(card, "JOD") ?: continue
|
||||||
|
val text = jodNode.text?.toString() ?: continue
|
||||||
|
val converted = arabicToWestern(text)
|
||||||
|
val match = Regex("""(\d+\.?\d*)""").find(converted)
|
||||||
|
if (match != null) {
|
||||||
|
val price = match.value.toDoubleOrNull()
|
||||||
|
if (price != null && price > 0) return price
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
private fun handleJeenyAutomation(rootNode: android.view.accessibility.AccessibilityNodeInfo) {
|
private fun handleJeenyAutomation(rootNode: android.view.accessibility.AccessibilityNodeInfo) {
|
||||||
val task = currentTask ?: return
|
val task = currentTask ?: return
|
||||||
Log.d(TAG, "Jeeny Automation event. State: $currentState")
|
Log.d(TAG, "Jeeny Automation event. State: $currentState")
|
||||||
|
|||||||
Reference in New Issue
Block a user