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 val taxiFCollectedPrices = mutableListOf<Pair<Double, String>>()
|
||||
private var taxiFPriceScrollAttempts = 0
|
||||
private var taxiFReadingPriceStartTime = 0L
|
||||
|
||||
companion object {
|
||||
private val TAXIF_EXCLUDED_TEXTS = setOf(
|
||||
@@ -397,21 +398,19 @@ class ScraperAccessibilityService : AccessibilityService() {
|
||||
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)
|
||||
|
||||
val nextResult = workerClient.fetchTask()
|
||||
if (nextResult != null && nextResult.optBoolean("has_task", false)) {
|
||||
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
|
||||
currentAppName = nextApp
|
||||
taxiFPickupDone = false
|
||||
taxiFDestinationDone = false
|
||||
currentState = BotState.SEARCHING_START
|
||||
Log.i(TAG, "TaxiF: Multi-trip fetched next task ${nextTask.optString("task_id")}, state -> SEARCHING_START")
|
||||
} else {
|
||||
Log.d(TAG, "Multi-trip: No more tasks, going IDLE.")
|
||||
Log.i(TAG, "No more tasks, returning to IDLE.")
|
||||
currentState = BotState.IDLE
|
||||
currentTask = null
|
||||
}
|
||||
@@ -551,6 +550,7 @@ class ScraperAccessibilityService : AccessibilityService() {
|
||||
}
|
||||
taxiFDestinationDone = true
|
||||
currentState = BotState.READING_PRICE
|
||||
taxiFReadingPriceStartTime = System.currentTimeMillis()
|
||||
taxiFCollectedPrices.clear()
|
||||
taxiFPriceScrollAttempts = 0
|
||||
return
|
||||
@@ -561,7 +561,16 @@ class ScraperAccessibilityService : AccessibilityService() {
|
||||
}
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -570,44 +579,34 @@ class ScraperAccessibilityService : AccessibilityService() {
|
||||
}
|
||||
|
||||
private fun hasJustClickedHome(): Boolean {
|
||||
return (System.currentTimeMillis() - taxiFHomeClickTime) < 1500
|
||||
return (System.currentTimeMillis() - taxiFHomeClickTime) < 500
|
||||
}
|
||||
|
||||
private fun hasJustClickedLocation(): Boolean {
|
||||
return (System.currentTimeMillis() - taxiFLocationClickTime) < 1500
|
||||
return (System.currentTimeMillis() - taxiFLocationClickTime) < 500
|
||||
}
|
||||
|
||||
private fun hasJustClickedSuggestion(): Boolean {
|
||||
return (System.currentTimeMillis() - taxiFSuggestionClickTime) < 1500
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
return (System.currentTimeMillis() - taxiFSuggestionClickTime) < 500
|
||||
}
|
||||
|
||||
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", "")
|
||||
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
|
||||
}
|
||||
|
||||
@@ -656,7 +655,9 @@ class ScraperAccessibilityService : AccessibilityService() {
|
||||
if (editTexts.isEmpty()) return false
|
||||
val editField = editTexts[0]
|
||||
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 {
|
||||
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 {
|
||||
if (node == null) return false
|
||||
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) {
|
||||
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) {
|
||||
val task = currentTask ?: return
|
||||
Log.d(TAG, "Jeeny Automation event. State: $currentState")
|
||||
|
||||
Reference in New Issue
Block a user