Update: 2026-06-30 02:33:51

This commit is contained in:
Hamza-Ayed
2026-06-30 02:33:51 +03:00
parent c8546bb744
commit 152d4df0fc
8 changed files with 45 additions and 125 deletions

View File

@@ -42,11 +42,6 @@ class ScraperAccessibilityService : AccessibilityService() {
private val taxiFCollectedPrices = mutableListOf<Pair<Double, String>>()
private var taxiFPriceScrollAttempts = 0
// Multi-trip batch fields
private val taxiFMultiTripTrips = mutableListOf<JSONObject>()
private var taxiFMultiTripIndex = 0
private var taxiFPriceSubmitTime = 0L
companion object {
private val TAXIF_EXCLUDED_TEXTS = setOf(
"JOD", "ECO", "TaxiF", "SUV", "EV", "Mini", "Female", "Van",
@@ -90,25 +85,11 @@ class ScraperAccessibilityService : AccessibilityService() {
currentTask = task
currentAppName = task.optString("app")
val taskId = task.optString("task_id")
val taskType = task.optString("type", "price_check")
Log.i(TAG, "Received Task: $taskId for App: $currentAppName, Type: $taskType")
Log.i(TAG, "Received Task: $taskId for App: $currentAppName")
taxiFPickupDone = false
taxiFDestinationDone = false
taxiFMultiTripTrips.clear()
taxiFMultiTripIndex = 0
taxiFPriceSubmitTime = 0L
if (taskType == "batch_multi_trip") {
val tripsJson = task.optJSONArray("trips")
if (tripsJson != null) {
for (i in 0 until tripsJson.length()) {
taxiFMultiTripTrips.add(tripsJson.getJSONObject(i))
}
}
Log.i(TAG, "TaxiF: Batch multi-trip loaded with ${taxiFMultiTripTrips.size} trips")
}
currentState = BotState.LAUNCHING_APP
// Launch the App
@@ -384,26 +365,17 @@ class ScraperAccessibilityService : AccessibilityService() {
val task = currentTask ?: return
val taskId = task.optString("task_id")
val startLat: Double
val startLng: Double
val endLat: Double
val endLng: Double
val tripCoords = getCurrentTripLatLng()
if (tripCoords != null) {
startLat = tripCoords.first[0]
startLng = tripCoords.first[1]
endLat = tripCoords.second[0]
endLng = tripCoords.second[1]
} else {
val payload = task.optJSONObject("payload")
startLat = payload?.optDouble("start_lat", 0.0) ?: 0.0
startLng = payload?.optDouble("start_lng", 0.0) ?: 0.0
endLat = payload?.optDouble("end_lat", 0.0) ?: 0.0
endLng = payload?.optDouble("end_lng", 0.0) ?: 0.0
}
// Extract nested payload object where coordinates reside
val payload = task.optJSONObject("payload")
val startLat = payload?.optDouble("start_lat", 0.0) ?: 0.0
val startLng = payload?.optDouble("start_lng", 0.0) ?: 0.0
val endLat = payload?.optDouble("end_lat", 0.0) ?: 0.0
val endLng = payload?.optDouble("end_lng", 0.0) ?: 0.0
// Calculate distance
val distanceKm = calculateDistanceInKm(startLat, startLng, endLat, endLng)
// Extract numeric digits from price
val numericPrice = rawPrice.replace(Regex("[^0-9.]"), "").toDoubleOrNull() ?: 0.0
serviceScope.launch {
@@ -424,6 +396,10 @@ class ScraperAccessibilityService : AccessibilityService() {
} else {
Log.e(TAG, "Failed to submit price.")
}
// Go back to IDLE
currentState = BotState.IDLE
currentTask = null
}
}
@@ -510,10 +486,7 @@ class ScraperAccessibilityService : AccessibilityService() {
private fun handleTaxiFAutomation(rootNode: android.view.accessibility.AccessibilityNodeInfo) {
val task = currentTask ?: return
// 3-second cool-down between multi-trips
if ((System.currentTimeMillis() - taxiFPriceSubmitTime) < 3000) return
Log.d(TAG, "TaxiF Automation event. State: $currentState" +
if (taxiFMultiTripTrips.isNotEmpty()) " Trip ${taxiFMultiTripIndex + 1}/${taxiFMultiTripTrips.size}" else "")
Log.d(TAG, "TaxiF Automation event. State: $currentState")
when (currentState) {
BotState.NAVIGATING_HOME -> {
if (hasJustClickedHome()) return
@@ -539,7 +512,7 @@ class ScraperAccessibilityService : AccessibilityService() {
BotState.SEARCHING_START -> {
if (detectTaxiFPriceScreen(rootNode)) {
if (hasJustClickedLocation() || hasJustClickedSuggestion()) return
if (!taxiFPickupDone && shouldEditLocation(rootNode, isPickup = true)) {
if (!taxiFPickupDone && shouldEditLocation(rootNode, task, isPickup = true)) {
clickLocationRow(rootNode, isPickup = true)
taxiFLocationClickTime = System.currentTimeMillis()
return
@@ -548,7 +521,7 @@ class ScraperAccessibilityService : AccessibilityService() {
currentState = BotState.SEARCHING_END
return
}
if (!taxiFPickupDone && handleTaxiFSearchScreen(rootNode, getCurrentStartLocation())) {
if (!taxiFPickupDone && handleTaxiFSearchScreen(rootNode, task.optString("start_location", "Amman"))) {
taxiFPickupDone = true
Log.i(TAG, "TaxiF: Handled pickup search, waiting for price screen")
}
@@ -556,7 +529,7 @@ class ScraperAccessibilityService : AccessibilityService() {
BotState.SEARCHING_END -> {
if (detectTaxiFPriceScreen(rootNode)) {
if (hasJustClickedLocation() || hasJustClickedSuggestion()) return
if (!taxiFDestinationDone && shouldEditLocation(rootNode, isPickup = false)) {
if (!taxiFDestinationDone && shouldEditLocation(rootNode, task, isPickup = false)) {
clickLocationRow(rootNode, isPickup = false)
taxiFLocationClickTime = System.currentTimeMillis()
return
@@ -567,7 +540,7 @@ class ScraperAccessibilityService : AccessibilityService() {
taxiFPriceScrollAttempts = 0
return
}
if (!taxiFDestinationDone && handleTaxiFSearchScreen(rootNode, getCurrentEndLocation())) {
if (!taxiFDestinationDone && handleTaxiFSearchScreen(rootNode, task.optString("end_location", "Airport"))) {
taxiFDestinationDone = true
Log.i(TAG, "TaxiF: Handled dest search, waiting for price screen")
}
@@ -593,30 +566,6 @@ class ScraperAccessibilityService : AccessibilityService() {
return (System.currentTimeMillis() - taxiFSuggestionClickTime) < 1500
}
private fun getCurrentStartLocation(): String {
return if (taxiFMultiTripTrips.isNotEmpty() && taxiFMultiTripIndex < taxiFMultiTripTrips.size)
taxiFMultiTripTrips[taxiFMultiTripIndex].optString("start_location", "Amman")
else
currentTask?.optString("start_location", "Amman") ?: "Amman"
}
private fun getCurrentEndLocation(): String {
return if (taxiFMultiTripTrips.isNotEmpty() && taxiFMultiTripIndex < taxiFMultiTripTrips.size)
taxiFMultiTripTrips[taxiFMultiTripIndex].optString("end_location", "Airport")
else
currentTask?.optString("end_location", "Airport") ?: "Airport"
}
private fun getCurrentTripLatLng(): Pair<DoubleArray, DoubleArray>? {
return if (taxiFMultiTripTrips.isNotEmpty() && taxiFMultiTripIndex < taxiFMultiTripTrips.size) {
val trip = taxiFMultiTripTrips[taxiFMultiTripIndex]
Pair(
doubleArrayOf(trip.optDouble("start_lat", 0.0), trip.optDouble("start_lng", 0.0)),
doubleArrayOf(trip.optDouble("end_lat", 0.0), trip.optDouble("end_lng", 0.0))
)
} else null
}
private fun getLocationTexts(node: android.view.accessibility.AccessibilityNodeInfo?): List<String> {
val texts = mutableListOf<String>()
collectLocationTexts(node, texts)
@@ -636,9 +585,9 @@ class ScraperAccessibilityService : AccessibilityService() {
}
}
private fun shouldEditLocation(rootNode: android.view.accessibility.AccessibilityNodeInfo, isPickup: Boolean): Boolean {
private fun shouldEditLocation(rootNode: android.view.accessibility.AccessibilityNodeInfo, task: JSONObject, isPickup: Boolean): Boolean {
val locationTexts = getLocationTexts(rootNode)
val taskLoc = if (isPickup) getCurrentStartLocation() else getCurrentEndLocation()
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)
@@ -776,40 +725,13 @@ class ScraperAccessibilityService : AccessibilityService() {
Log.i(TAG, "TaxiF: Found price option: ${label.take(50)} = $price JOD")
}
val cheapest = prices.minByOrNull { it.first } ?: prices.first()
val tripLabel = if (taxiFMultiTripTrips.isNotEmpty())
"Trip ${taxiFMultiTripIndex + 1}/${taxiFMultiTripTrips.size}: " else ""
Log.i(TAG, "TaxiF: ${tripLabel}Cheapest price: ${cheapest.first} JOD")
Log.i(TAG, "TaxiF: Submitting cheapest price: ${cheapest.second.take(50)} = ${cheapest.first} JOD")
submitPriceToServer("${cheapest.first} JOD")
advanceToNextTaxiFTrip()
} else {
Log.w(TAG, "TaxiF: No JOD prices found${if (taxiFMultiTripTrips.isNotEmpty()) " for trip ${taxiFMultiTripIndex + 1}" else ""}")
if (taxiFMultiTripTrips.isEmpty()) {
searchPriceByCurrency(rootNode)
currentState = BotState.IDLE
}
}
}
private fun advanceToNextTaxiFTrip() {
taxiFPriceSubmitTime = System.currentTimeMillis()
if (taxiFMultiTripTrips.isNotEmpty()) {
taxiFMultiTripIndex++
taxiFPickupDone = false
taxiFDestinationDone = false
if (taxiFMultiTripIndex < taxiFMultiTripTrips.size) {
Log.i(TAG, "TaxiF: 3s cool-down then trip ${taxiFMultiTripIndex + 1}/${taxiFMultiTripTrips.size}")
currentState = BotState.SEARCHING_START
} else {
Log.i(TAG, "TaxiF: All ${taxiFMultiTripTrips.size} trips completed!")
currentState = BotState.IDLE
currentTask = null
taxiFMultiTripTrips.clear()
taxiFMultiTripIndex = 0
}
} else {
currentState = BotState.IDLE
Log.w(TAG, "TaxiF: No JOD prices found, falling back to generic search")
searchPriceByCurrency(rootNode)
}
currentState = BotState.IDLE
}
private fun findHorizontalRecyclerView(node: android.view.accessibility.AccessibilityNodeInfo?): android.view.accessibility.AccessibilityNodeInfo? {