diff --git a/app/src/main/java/com/jordanbot/autoride/api/BackendApi.kt b/app/src/main/java/com/jordanbot/autoride/api/BackendApi.kt index aeab59b..0b2a259 100644 --- a/app/src/main/java/com/jordanbot/autoride/api/BackendApi.kt +++ b/app/src/main/java/com/jordanbot/autoride/api/BackendApi.kt @@ -14,7 +14,9 @@ data class RideLogRequest( val timeToPickup: String, val isAccepted: Boolean, val rawText: String, - val fingerprint: String + val fingerprint: String, + val latitude: Double? = null, + val longitude: Double? = null ) data class LocationPoint( diff --git a/app/src/main/java/com/jordanbot/autoride/parser/UberParser.kt b/app/src/main/java/com/jordanbot/autoride/parser/UberParser.kt index 2bea7ab..82ca71a 100644 --- a/app/src/main/java/com/jordanbot/autoride/parser/UberParser.kt +++ b/app/src/main/java/com/jordanbot/autoride/parser/UberParser.kt @@ -11,9 +11,11 @@ class UberParser : NotificationParser { var price: Double? = null var minutes: Int? = null + var distance: Double? = null val priceRegex = """(\d+\.?\d*)\s*(JOD|د\.أ)""".toRegex() val minutesRegex = """(\d+)\s*(min|دقيقة)""".toRegex() + val distanceRegex = """(\d+\.?\d*)\s*(km|كم)""".toRegex() val fullText = "$title $text" @@ -25,12 +27,15 @@ class UberParser : NotificationParser { 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( appPackage = packageName, priceJod = price, minutesAway = minutes, - distanceKm = null, // Uber usually gives mins, not always distance + distanceKm = distance, title = title, text = text ) diff --git a/app/src/main/java/com/jordanbot/autoride/service/RideNotificationListener.kt b/app/src/main/java/com/jordanbot/autoride/service/RideNotificationListener.kt index a6bd90c..b491054 100644 --- a/app/src/main/java/com/jordanbot/autoride/service/RideNotificationListener.kt +++ b/app/src/main/java/com/jordanbot/autoride/service/RideNotificationListener.kt @@ -1,5 +1,13 @@ 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.service.notification.NotificationListenerService import android.service.notification.StatusBarNotification @@ -18,6 +26,13 @@ class RideNotificationListener : NotificationListenerService() { private val serviceScope = CoroutineScope(Dispatchers.IO + SupervisorJob()) private val filterEngine = FilterEngine() + private lateinit var fusedLocationClient: FusedLocationProviderClient + + override fun onCreate() { + super.onCreate() + fusedLocationClient = LocationServices.getFusedLocationProviderClient(this) + } + private val parsers = listOf( UberParser(), CareemParser(), @@ -35,12 +50,6 @@ class RideNotificationListener : NotificationListenerService() { var packageName = sbn.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 } if (parser == null) { Log.d("JordanBot", "No parser found for package: $packageName") @@ -76,18 +85,36 @@ class RideNotificationListener : NotificationListenerService() { } private fun sendLogToBackend(ride: com.jordanbot.autoride.model.RideRequest, isAccepted: Boolean, rawText: String) { - val logRequest = RideLogRequest( - platform = ride.appPackage, - price = ride.priceJod ?: 0.0, - pickupDistance = ride.distanceKm?.toString() ?: "Unknown", - dropoffDistance = "Unknown", - timeToPickup = ride.minutesAway?.toString() ?: "Unknown", - isAccepted = isAccepted, - rawText = rawText, - fingerprint = DeviceUtils.getDeviceFingerprint(this) - ) - 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( + platform = ride.appPackage, + price = ride.priceJod ?: 0.0, + pickupDistance = ride.distanceKm?.toString() ?: "Unknown", + dropoffDistance = "Unknown", + timeToPickup = ride.minutesAway?.toString() ?: "Unknown", + isAccepted = isAccepted, + rawText = rawText, + fingerprint = DeviceUtils.getDeviceFingerprint(this@RideNotificationListener), + latitude = lat, + longitude = lng + ) try { val response = ApiClient.service.logRide(logRequest) if (response.success) { diff --git a/backend/api/rides.php b/backend/api/rides.php index 71c1391..4e13eb9 100644 --- a/backend/api/rides.php +++ b/backend/api/rides.php @@ -49,10 +49,12 @@ $timeToPickup = $input['timeToPickup'] ?? 'Unknown'; $isAccepted = isset($input['isAccepted']) ? (int)$input['isAccepted'] : 0; $rawText = $input['rawText'] ?? ''; $fingerprint = $input['fingerprint'] ?? 'UNKNOWN_DEVICE'; +$latitude = $input['latitude'] ?? null; +$longitude = $input['longitude'] ?? null; try { - $sql = "INSERT INTO rides (fingerprint, platform, price, pickup_distance, dropoff_distance, time_to_pickup, is_accepted, raw_text, created_at) - VALUES (:fingerprint, :platform, :price, :pickup_distance, :dropoff_distance, :time_to_pickup, :is_accepted, :raw_text, NOW())"; + $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, :latitude, :longitude, NOW())"; $stmt = $pdo->prepare($sql); $stmt->execute([ @@ -63,7 +65,9 @@ try { ':dropoff_distance' => $dropoffDistance, ':time_to_pickup' => $timeToPickup, ':is_accepted' => $isAccepted, - ':raw_text' => $rawText + ':raw_text' => $rawText, + ':latitude' => $latitude, + ':longitude' => $longitude ]); http_response_code(201); diff --git a/backend/schema.sql b/backend/schema.sql index b0df261..842a624 100644 --- a/backend/schema.sql +++ b/backend/schema.sql @@ -14,6 +14,8 @@ CREATE TABLE IF NOT EXISTS rides ( time_to_pickup VARCHAR(50), is_accepted TINYINT(1) DEFAULT 0, raw_text TEXT, + latitude DOUBLE DEFAULT NULL, + longitude DOUBLE DEFAULT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, INDEX (fingerprint), INDEX (platform)