Update: 2026-05-14 20:19:28

This commit is contained in:
Hamza-Ayed
2026-05-14 20:19:28 +03:00
parent 736c3beae0
commit 950a4f618d
5 changed files with 63 additions and 23 deletions

View File

@@ -14,7 +14,9 @@ data class RideLogRequest(
val timeToPickup: String, val timeToPickup: String,
val isAccepted: Boolean, val isAccepted: Boolean,
val rawText: String, val rawText: String,
val fingerprint: String val fingerprint: String,
val latitude: Double? = null,
val longitude: Double? = null
) )
data class LocationPoint( data class LocationPoint(

View File

@@ -11,9 +11,11 @@ class UberParser : NotificationParser {
var price: Double? = null var price: Double? = null
var minutes: Int? = null var minutes: Int? = null
var distance: Double? = null
val priceRegex = """(\d+\.?\d*)\s*(JOD|د\.أ)""".toRegex() val priceRegex = """(\d+\.?\d*)\s*(JOD|د\.أ)""".toRegex()
val minutesRegex = """(\d+)\s*(min|دقيقة)""".toRegex() val minutesRegex = """(\d+)\s*(min|دقيقة)""".toRegex()
val distanceRegex = """(\d+\.?\d*)\s*(km|كم)""".toRegex()
val fullText = "$title $text" val fullText = "$title $text"
@@ -25,12 +27,15 @@ class UberParser : NotificationParser {
minutes = it.groupValues[1].toIntOrNull() minutes = it.groupValues[1].toIntOrNull()
} }
// We return the request even if price is null, we can filter it later distanceRegex.find(fullText)?.let {
distance = it.groupValues[1].toDoubleOrNull()
}
return RideRequest( return RideRequest(
appPackage = packageName, appPackage = packageName,
priceJod = price, priceJod = price,
minutesAway = minutes, minutesAway = minutes,
distanceKm = null, // Uber usually gives mins, not always distance distanceKm = distance,
title = title, title = title,
text = text text = text
) )

View File

@@ -1,5 +1,13 @@
package com.jordanbot.autoride.service package com.jordanbot.autoride.service
import android.Manifest
import android.annotation.SuppressLint
import android.content.pm.PackageManager
import androidx.core.content.ContextCompat
import com.google.android.gms.location.FusedLocationProviderClient
import com.google.android.gms.location.LocationServices
import com.google.android.gms.tasks.Tasks
import android.app.Notification import android.app.Notification
import android.service.notification.NotificationListenerService import android.service.notification.NotificationListenerService
import android.service.notification.StatusBarNotification import android.service.notification.StatusBarNotification
@@ -18,6 +26,13 @@ class RideNotificationListener : NotificationListenerService() {
private val serviceScope = CoroutineScope(Dispatchers.IO + SupervisorJob()) private val serviceScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
private val filterEngine = FilterEngine() private val filterEngine = FilterEngine()
private lateinit var fusedLocationClient: FusedLocationProviderClient
override fun onCreate() {
super.onCreate()
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
}
private val parsers = listOf( private val parsers = listOf(
UberParser(), UberParser(),
CareemParser(), CareemParser(),
@@ -35,12 +50,6 @@ class RideNotificationListener : NotificationListenerService() {
var packageName = sbn.packageName var packageName = sbn.packageName
Log.d("JordanBot", "Received notification from: $packageName") Log.d("JordanBot", "Received notification from: $packageName")
// TEMPORARY: Allow Shell notifications for testing
if (packageName == "com.android.shell") {
Log.d("JordanBot", "Testing mode: Treating Shell notification as Uber")
packageName = "com.ubercab.driver"
}
val parser = parsers.find { it.packageName == packageName } val parser = parsers.find { it.packageName == packageName }
if (parser == null) { if (parser == null) {
Log.d("JordanBot", "No parser found for package: $packageName") Log.d("JordanBot", "No parser found for package: $packageName")
@@ -76,6 +85,24 @@ class RideNotificationListener : NotificationListenerService() {
} }
private fun sendLogToBackend(ride: com.jordanbot.autoride.model.RideRequest, isAccepted: Boolean, rawText: String) { private fun sendLogToBackend(ride: com.jordanbot.autoride.model.RideRequest, isAccepted: Boolean, rawText: String) {
serviceScope.launch {
var lat: Double? = null
var lng: Double? = null
// Try to get current location
if (ContextCompat.checkSelfPermission(this@RideNotificationListener, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
try {
val location = Tasks.await(fusedLocationClient.lastLocation)
if (location != null) {
lat = location.latitude
lng = location.longitude
Log.d("JordanBot", "📍 Location attached to ride: $lat, $lng")
}
} catch (e: Exception) {
Log.e("JordanBot", "Failed to get location for ride log", e)
}
}
val logRequest = RideLogRequest( val logRequest = RideLogRequest(
platform = ride.appPackage, platform = ride.appPackage,
price = ride.priceJod ?: 0.0, price = ride.priceJod ?: 0.0,
@@ -84,10 +111,10 @@ class RideNotificationListener : NotificationListenerService() {
timeToPickup = ride.minutesAway?.toString() ?: "Unknown", timeToPickup = ride.minutesAway?.toString() ?: "Unknown",
isAccepted = isAccepted, isAccepted = isAccepted,
rawText = rawText, rawText = rawText,
fingerprint = DeviceUtils.getDeviceFingerprint(this) fingerprint = DeviceUtils.getDeviceFingerprint(this@RideNotificationListener),
latitude = lat,
longitude = lng
) )
serviceScope.launch {
try { try {
val response = ApiClient.service.logRide(logRequest) val response = ApiClient.service.logRide(logRequest)
if (response.success) { if (response.success) {

View File

@@ -49,10 +49,12 @@ $timeToPickup = $input['timeToPickup'] ?? 'Unknown';
$isAccepted = isset($input['isAccepted']) ? (int)$input['isAccepted'] : 0; $isAccepted = isset($input['isAccepted']) ? (int)$input['isAccepted'] : 0;
$rawText = $input['rawText'] ?? ''; $rawText = $input['rawText'] ?? '';
$fingerprint = $input['fingerprint'] ?? 'UNKNOWN_DEVICE'; $fingerprint = $input['fingerprint'] ?? 'UNKNOWN_DEVICE';
$latitude = $input['latitude'] ?? null;
$longitude = $input['longitude'] ?? null;
try { try {
$sql = "INSERT INTO rides (fingerprint, platform, price, pickup_distance, dropoff_distance, time_to_pickup, is_accepted, raw_text, created_at) $sql = "INSERT INTO rides (fingerprint, platform, price, pickup_distance, dropoff_distance, time_to_pickup, is_accepted, raw_text, latitude, longitude, created_at)
VALUES (:fingerprint, :platform, :price, :pickup_distance, :dropoff_distance, :time_to_pickup, :is_accepted, :raw_text, NOW())"; VALUES (:fingerprint, :platform, :price, :pickup_distance, :dropoff_distance, :time_to_pickup, :is_accepted, :raw_text, :latitude, :longitude, NOW())";
$stmt = $pdo->prepare($sql); $stmt = $pdo->prepare($sql);
$stmt->execute([ $stmt->execute([
@@ -63,7 +65,9 @@ try {
':dropoff_distance' => $dropoffDistance, ':dropoff_distance' => $dropoffDistance,
':time_to_pickup' => $timeToPickup, ':time_to_pickup' => $timeToPickup,
':is_accepted' => $isAccepted, ':is_accepted' => $isAccepted,
':raw_text' => $rawText ':raw_text' => $rawText,
':latitude' => $latitude,
':longitude' => $longitude
]); ]);
http_response_code(201); http_response_code(201);

View File

@@ -14,6 +14,8 @@ CREATE TABLE IF NOT EXISTS rides (
time_to_pickup VARCHAR(50), time_to_pickup VARCHAR(50),
is_accepted TINYINT(1) DEFAULT 0, is_accepted TINYINT(1) DEFAULT 0,
raw_text TEXT, raw_text TEXT,
latitude DOUBLE DEFAULT NULL,
longitude DOUBLE DEFAULT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX (fingerprint), INDEX (fingerprint),
INDEX (platform) INDEX (platform)