Update: 2026-07-04 15:39:18
This commit is contained in:
@@ -45,6 +45,7 @@ import 'ui_interactions_controller.dart';
|
||||
import 'map_socket_controller.dart';
|
||||
import '../decode_polyline_isolate.dart';
|
||||
import '../ios_live_activity_service.dart';
|
||||
import '../../../services/android_live_update_service.dart';
|
||||
import '../../firebase/local_notification.dart';
|
||||
import '../../firebase/notification_service.dart';
|
||||
import '../../functions/audio_record1.dart';
|
||||
@@ -737,8 +738,10 @@ class RideLifecycleController extends GetxController {
|
||||
void _showAiNegotiatorDialog() {
|
||||
final double currentPrice = double.tryParse(totalPassenger) ?? 0;
|
||||
final double suggestedPrice = currentPrice * 1.05; // 5% AI suggestion
|
||||
final String currency = box.read(BoxName.serverChosen)?.toString().contains('Syria') == true
|
||||
? 'ل.س' : 'SAR';
|
||||
final String currency =
|
||||
box.read(BoxName.serverChosen)?.toString().contains('Syria') == true
|
||||
? 'ل.س'
|
||||
: 'SAR';
|
||||
|
||||
Get.dialog(
|
||||
Dialog(
|
||||
@@ -757,24 +760,30 @@ class RideLifecycleController extends GetxController {
|
||||
borderRadius: BorderRadius.circular(50),
|
||||
border: Border.all(color: const Color(0xFF0F3460), width: 2),
|
||||
),
|
||||
child: const Icon(Icons.psychology_rounded, color: Color(0xFF4FC3F7), size: 36),
|
||||
child: const Icon(Icons.psychology_rounded,
|
||||
color: Color(0xFF4FC3F7), size: 36),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'🤖 مقترح الذكاء الاصطناعي'.tr,
|
||||
style: const TextStyle(color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold),
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
'لم يقبل أي سائق طلبك بالسعر الحالي. يقترح AI رفع السعر قليلاً لتسريع القبول.'.tr,
|
||||
'لم يقبل أي سائق طلبك بالسعر الحالي. يقترح AI رفع السعر قليلاً لتسريع القبول.'
|
||||
.tr,
|
||||
style: const TextStyle(color: Color(0xFFB0BEC5), fontSize: 13),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
// Price comparison
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF0F3460),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
@@ -784,17 +793,28 @@ class RideLifecycleController extends GetxController {
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
Text('السعر الحالي'.tr, style: const TextStyle(color: Color(0xFF90A4AE), fontSize: 11)),
|
||||
Text('السعر الحالي'.tr,
|
||||
style: const TextStyle(
|
||||
color: Color(0xFF90A4AE), fontSize: 11)),
|
||||
Text('${currentPrice.toStringAsFixed(0)} $currency',
|
||||
style: const TextStyle(color: Colors.white70, fontSize: 15, decoration: TextDecoration.lineThrough)),
|
||||
style: const TextStyle(
|
||||
color: Colors.white70,
|
||||
fontSize: 15,
|
||||
decoration: TextDecoration.lineThrough)),
|
||||
],
|
||||
),
|
||||
const Icon(Icons.arrow_forward_rounded, color: Color(0xFF4FC3F7)),
|
||||
const Icon(Icons.arrow_forward_rounded,
|
||||
color: Color(0xFF4FC3F7)),
|
||||
Column(
|
||||
children: [
|
||||
Text('السعر المقترح'.tr, style: const TextStyle(color: Color(0xFF4FC3F7), fontSize: 11)),
|
||||
Text('السعر المقترح'.tr,
|
||||
style: const TextStyle(
|
||||
color: Color(0xFF4FC3F7), fontSize: 11)),
|
||||
Text('${suggestedPrice.toStringAsFixed(0)} $currency',
|
||||
style: const TextStyle(color: Color(0xFF4FC3F7), fontSize: 17, fontWeight: FontWeight.bold)),
|
||||
style: const TextStyle(
|
||||
color: Color(0xFF4FC3F7),
|
||||
fontSize: 17,
|
||||
fontWeight: FontWeight.bold)),
|
||||
],
|
||||
),
|
||||
],
|
||||
@@ -811,9 +831,11 @@ class RideLifecycleController extends GetxController {
|
||||
},
|
||||
style: OutlinedButton.styleFrom(
|
||||
side: const BorderSide(color: Colors.red),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10)),
|
||||
),
|
||||
child: Text('إلغاء'.tr, style: const TextStyle(color: Colors.red)),
|
||||
child: Text('إلغاء'.tr,
|
||||
style: const TextStyle(color: Colors.red)),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
@@ -825,9 +847,13 @@ class RideLifecycleController extends GetxController {
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: const Color(0xFF4FC3F7),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10)),
|
||||
),
|
||||
child: Text('قبول +5%'.tr, style: const TextStyle(color: Colors.black, fontWeight: FontWeight.bold)),
|
||||
child: Text('قبول +5%'.tr,
|
||||
style: const TextStyle(
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.bold)),
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -846,12 +872,14 @@ class RideLifecycleController extends GetxController {
|
||||
update();
|
||||
|
||||
// تحديث قاعدة البيانات بالسعر الجديد
|
||||
await CRUD().post(link: "${AppLink.server}/ride/rides/update.php", payload: {
|
||||
await CRUD()
|
||||
.post(link: "${AppLink.server}/ride/rides/update.php", payload: {
|
||||
"id": rideId,
|
||||
"price": newPrice.toStringAsFixed(2),
|
||||
});
|
||||
|
||||
Log.print('[AI Negotiator Phase 2] Price accepted by passenger: $newPrice — restarting search...');
|
||||
Log.print(
|
||||
'[AI Negotiator Phase 2] Price accepted by passenger: $newPrice — restarting search...');
|
||||
notifiedDrivers.clear();
|
||||
|
||||
// إعادة البحث مع السعر الجديد (Phase 2 retry)
|
||||
@@ -1016,6 +1044,12 @@ class RideLifecycleController extends GetxController {
|
||||
etaText: stringRemainingTimeToPassenger,
|
||||
progress: currentProgress.clamp(0.0, 1.0),
|
||||
);
|
||||
|
||||
// Android Live Update: تحديث التقدم أثناء انتظار السائق
|
||||
AndroidLiveUpdateService.updateProgress(
|
||||
progress: currentProgress.clamp(0.0, 1.0),
|
||||
etaText: stringRemainingTimeToPassenger,
|
||||
);
|
||||
}
|
||||
|
||||
if (_driverEtaCountdownTicks % beginRideInterval == 0) {
|
||||
@@ -1149,6 +1183,12 @@ class RideLifecycleController extends GetxController {
|
||||
etaText: stringRemainingTimeRideBegin,
|
||||
progress: progressTimerRideBegin.clamp(0.0, 1.0),
|
||||
);
|
||||
|
||||
// Android Live Update: تحديث التقدم أثناء الرحلة
|
||||
AndroidLiveUpdateService.updateProgress(
|
||||
progress: progressTimerRideBegin.clamp(0.0, 1.0),
|
||||
etaText: stringRemainingTimeRideBegin,
|
||||
);
|
||||
}
|
||||
|
||||
if (remainingSeconds % 60 == 0 || remainingSeconds == 0) {
|
||||
@@ -2093,7 +2133,8 @@ class RideLifecycleController extends GetxController {
|
||||
// 🆕 Save is_destination_match so retry_search_drivers can pass it forward
|
||||
final int isMatchFlag = (res['is_destination_match'] ?? 0) is int
|
||||
? res['is_destination_match'] ?? 0
|
||||
: int.tryParse(res['is_destination_match']?.toString() ?? '0') ?? 0;
|
||||
: int.tryParse(res['is_destination_match']?.toString() ?? '0') ??
|
||||
0;
|
||||
box.write(BoxName.isDestinationMatch, isMatchFlag.toString());
|
||||
|
||||
totalPassenger = totalPassengerSpeed;
|
||||
@@ -2135,12 +2176,15 @@ class RideLifecycleController extends GetxController {
|
||||
|
||||
if (res is Map && res['status'] == 'success') {
|
||||
hasCompetitorData = res['has_competitor_data'] == true;
|
||||
competitorAvgPrice = double.tryParse(res['competitor_avg_price']?.toString() ?? '');
|
||||
savingsPercent = double.tryParse(res['savings_percent']?.toString() ?? '');
|
||||
competitorAvgPrice =
|
||||
double.tryParse(res['competitor_avg_price']?.toString() ?? '');
|
||||
savingsPercent =
|
||||
double.tryParse(res['savings_percent']?.toString() ?? '');
|
||||
savingsLabel = res['savings_label']?.toString();
|
||||
topCompetitor = res['top_competitor']?.toString();
|
||||
driverExtraLabel = res['driver_extra_label']?.toString();
|
||||
driverExtraAmount = double.tryParse(res['driver_extra_amount']?.toString() ?? '');
|
||||
driverExtraAmount =
|
||||
double.tryParse(res['driver_extra_amount']?.toString() ?? '');
|
||||
update();
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -2912,6 +2956,15 @@ class RideLifecycleController extends GetxController {
|
||||
statusRide = 'Arrived';
|
||||
await RideLiveNotification.showDriverArrived(driverName);
|
||||
|
||||
// Android Live Update: تحديث الحالة إلى "وصل السائق"
|
||||
await AndroidLiveUpdateService.updateStatus(
|
||||
status: 'driver_arrived',
|
||||
driverName: driverName,
|
||||
etaText: 'لقد وصل السائق',
|
||||
progress: 1.0,
|
||||
carDetails: '$make • $model • $carColor',
|
||||
);
|
||||
|
||||
uiInteractions.driverArrivePassengerDialoge();
|
||||
startTimerDriverWaitPassenger5Minute();
|
||||
|
||||
@@ -2957,7 +3010,9 @@ class RideLifecycleController extends GetxController {
|
||||
"Please make sure not to leave any personal belongings in the car.".tr,
|
||||
'tone1',
|
||||
);
|
||||
// End both iOS and Android Live Updates
|
||||
IosLiveActivityService.endRideActivity();
|
||||
await AndroidLiveUpdateService.endLiveUpdate();
|
||||
PipService.disablePip();
|
||||
await RideLiveNotification.cancel();
|
||||
|
||||
@@ -2980,6 +3035,7 @@ class RideLifecycleController extends GetxController {
|
||||
if (Get.isDialogOpen == true) Get.back();
|
||||
await RideLiveNotification.cancel();
|
||||
IosLiveActivityService.endRideActivity();
|
||||
await AndroidLiveUpdateService.endLiveUpdate();
|
||||
PipService.disablePip();
|
||||
|
||||
Get.defaultDialog(
|
||||
@@ -3685,7 +3741,8 @@ class RideLifecycleController extends GetxController {
|
||||
for (final m in newMarkers) {
|
||||
await ctrl.addMarker(m);
|
||||
}
|
||||
Log.print('✅ Added ${newMarkers.length} markers via controller.addMarker()');
|
||||
Log.print(
|
||||
'✅ Added ${newMarkers.length} markers via controller.addMarker()');
|
||||
} else {
|
||||
Log.print('⚠️ mapController is null, relying on declarative markers');
|
||||
}
|
||||
@@ -3792,6 +3849,7 @@ class RideLifecycleController extends GetxController {
|
||||
await getUpdatedRideForDriverApply(rideId);
|
||||
}
|
||||
|
||||
// Start iOS Live Activity
|
||||
await IosLiveActivityService.startRideActivity(
|
||||
rideId: rideId,
|
||||
driverName: driverName,
|
||||
@@ -3800,6 +3858,15 @@ class RideLifecycleController extends GetxController {
|
||||
progress: 0.0,
|
||||
);
|
||||
|
||||
// Start Android Live Update (Progress Style)
|
||||
await AndroidLiveUpdateService.startLiveUpdate(
|
||||
rideId: rideId,
|
||||
driverName: driverName,
|
||||
etaText: stringRemainingTimeToPassenger,
|
||||
progress: 0.0,
|
||||
carDetails: '$make • $model • $carColor',
|
||||
);
|
||||
|
||||
_showRideStartNotifications();
|
||||
final etaText = stringRemainingTimeToPassenger;
|
||||
final carInfo = '$make • $model • $licensePlate';
|
||||
@@ -3918,6 +3985,7 @@ class RideLifecycleController extends GetxController {
|
||||
currentRideState.value = RideState.cancelled;
|
||||
await RideLiveNotification.cancel();
|
||||
IosLiveActivityService.endRideActivity();
|
||||
await AndroidLiveUpdateService.endLiveUpdate();
|
||||
PipService.disablePip();
|
||||
|
||||
if (rideId != 'yet' && rideId != null) {
|
||||
@@ -4033,6 +4101,7 @@ class RideLifecycleController extends GetxController {
|
||||
stopAllTimers();
|
||||
await RideLiveNotification.cancel();
|
||||
IosLiveActivityService.endRideActivity();
|
||||
await AndroidLiveUpdateService.endLiveUpdate();
|
||||
PipService.disablePip();
|
||||
_isCancelProcessed = false;
|
||||
currentRideState.value = RideState.noRide;
|
||||
@@ -4066,7 +4135,8 @@ class RideLifecycleController extends GetxController {
|
||||
|
||||
try {
|
||||
final bool isPhase2 = newPrice != null && newPrice > 0;
|
||||
Log.print("🔄 ${isPhase2 ? '[Phase 2]' : '[Phase 1]'} Retrying search for ride ID: $rideId");
|
||||
Log.print(
|
||||
"🔄 ${isPhase2 ? '[Phase 2]' : '[Phase 1]'} Retrying search for ride ID: $rideId");
|
||||
|
||||
var payload = {
|
||||
"ride_id": rideId.toString(),
|
||||
@@ -4090,7 +4160,8 @@ class RideLifecycleController extends GetxController {
|
||||
"price_for_driver": costForDriver.toString(),
|
||||
"car_type": box.read(BoxName.carType).toString(),
|
||||
"is_wallet": Get.find<PaymentController>().isWalletChecked.toString(),
|
||||
"is_destination_match": box.read(BoxName.isDestinationMatch)?.toString() ?? "0",
|
||||
"is_destination_match":
|
||||
box.read(BoxName.isDestinationMatch)?.toString() ?? "0",
|
||||
"has_steps": Get.find<WayPointController>().wayPoints.length > 1
|
||||
? "true"
|
||||
: "false",
|
||||
@@ -4105,7 +4176,8 @@ class RideLifecycleController extends GetxController {
|
||||
|
||||
if (response['status'] == 'success') {
|
||||
Log.print("✅ Search reset successfully.");
|
||||
if (!isPhase2) startSearchingTimer(); // Phase 1 يعيد العداد، Phase 2 يُدار من increasePriceAndRestartSearch
|
||||
if (!isPhase2)
|
||||
startSearchingTimer(); // Phase 1 يعيد العداد، Phase 2 يُدار من increasePriceAndRestartSearch
|
||||
} else {
|
||||
Log.print("❌ Failed to reset search: $response");
|
||||
handleNoDriverFound();
|
||||
@@ -4139,7 +4211,8 @@ class RideLifecycleController extends GetxController {
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
if (seconds == 20 && !_aiSilentRetryFired) {
|
||||
_aiSilentRetryFired = true;
|
||||
Log.print("🤖 [AI Negotiator Phase 1] 20s — silent retry with driver bonus...");
|
||||
Log.print(
|
||||
"🤖 [AI Negotiator Phase 1] 20s — silent retry with driver bonus...");
|
||||
retrySearchForDrivers();
|
||||
return;
|
||||
}
|
||||
@@ -4151,7 +4224,8 @@ class RideLifecycleController extends GetxController {
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
if (seconds >= 45) {
|
||||
timer.cancel();
|
||||
Log.print("🤖 [AI Negotiator Phase 2] 45s — showing AI price suggestion dialog...");
|
||||
Log.print(
|
||||
"🤖 [AI Negotiator Phase 2] 45s — showing AI price suggestion dialog...");
|
||||
_showAiNegotiatorDialog();
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user