25-10-5/1

This commit is contained in:
Hamza-Ayed
2025-10-08 13:14:07 +03:00
parent 1cc66029a3
commit 482c1296bc
22 changed files with 770 additions and 559 deletions

View File

@@ -44,8 +44,8 @@ android {
applicationId = "com.intaleq_driver" applicationId = "com.intaleq_driver"
minSdk = 29 minSdk = 29
targetSdk = 36 targetSdk = 36
versionCode = 17 // I've used the higher version number from your first file versionCode = 18
versionName = '1.0.17' // I've used the higher version name versionName = '1.0.18' // I've used the higher version name
multiDexEnabled = true multiDexEnabled = true
ndk { ndk {

View File

@@ -1,31 +1,31 @@
List<String> syrianDriverMessages = [ List<String> syrianDriverMessages = [
// --- رسائل وقت الذروة والطلبات المستعجلة --- // --- أوقات الذروة والطلبات الكتيرة ---
"البلد ولعانة هلأ! 🌃 هاد وقت الذروة، فرصة ذهبية لتدبّل أرباحك. انطلق معنا!", "وقت الذروة: البلد مولّعة مشاوير! 🌃 افتح التطبيق بسرعة وبلّش أرباحك تدبّل.",
"انتبه: الطلبات كتيرة بمنطقتك! ⚠️ خليك أول واحد بياخد مشوار واكسب أكتر. جاهز تطلع؟", "طلبات كتير حواليك: ⚠️ خليك أول كابتن بيلتقط الطلب وخلّي يومك رابح.",
"لا تروح عليك ساعة الذروة! ⏳ زباين أكتر يعني مصاري أكتر. فوت عالبرنامج هلأ!", "الناس طالعة من الشغل: 🌇 الكل راجع عالبيت، خليك جاهز لرحلات كتيرة!",
"خلص الدوام عند كتير عالم، وصار وقت يرجعوا عالبيت! 🌇 خليك بطلن ووصلن بأمان.", "زحمة المساء: ⏳ الزباين عم تزيد بهالوقت، لا تضيّع الفرصة واشتغل معنا!",
// --- رسائل للتركيز عالربح والمصاري --- // --- التركيز عالربح والمصاري ---
"هدف اليوم صار قريب! 💪 كل مشوار بيقربك أكتر. بلّش مشوارك الجاي هلأ.", "ضاعف ربحك: 💰 كل مشوار بيقربك من هدفك اليومي. جاهز تنطلق؟",
"جزدانك ناطر مصاري زيادة! 💰 فوت عالبرنامج وحوّل وقتك لمصاري عنجد ومضمونة.", "مصاري أكتر: جزدانك ناطر تعبك، كل دقيقة شغل هي ربح مضمون. 💪",
"كبسة زر بتجيبلك ربح زيادة. جاهز للطلب الجاي؟ 🤔", "خليك بالواجهة: الطلب الجاي ممكن يكون إلك، كبسة زر بتجيبلك رزقة اليوم.",
"لا تترك المصاري عالأرض! 💸 زباين انطلق عم يدوروا على كابتن شاطر متلك هلأ.", "رزقتك جاهزة: 💸 لا تخلّي الزباين يستنّوا، افتح التطبيق وخليك متأهّب.",
// --- رسائل تقدير وشكر --- // --- التقدير والشكر ---
"أنت مو بس شوفير، أنت شريكنا بالنجاح. 🙏 زباينّا ناطرينك. يسلم إيديك!", "شكراً إلك: 🙏 إنت مو بس سائق، إنت أساس نجاح تطبيق انطلق.",
"نحنا عم نكبر فيك! 🌟 خليك فاتح واستقبل مشاوير جديدة من زباينّا الكويسين.", "نفتخر فيك: 🌟 الكباتن متلك هنن يلي رافعين اسمنا بالعالي.",
"كباتنّا هنن الأحسن! 👍 ضل عم تقدّم هالخدمة الحلوة اللي بيعرفوها زباينا عن تطبيق انطلق.", "أداء ممتاز: 👍 استمر بنفس الروح الحلوة، زباينك مبسوطين منك.",
"كل توصيلة ناجحة هي قصة نجاح لإلك وإلنا. خلينا نعمل قصص نجاح أكتر اليوم! 🗺️", "نجاح مشترك: 🗺️ كل توصيلة بتعملها بتكبر فيها شركتنا وانت كمان.",
// --- رسائل تحفيز وتشجيع --- // --- التحفيز والتشجيع ---
"يوم جديد، فرصة جديدة للنجاح! ☀️ فوت عالبرنامج وخلينا ننطلق سوا لأهدافك.", "صباح النشاط: ☀️ بلّش نهارك بطاقة إيجابية وانطلق لتكسب أكتر.",
"طريق النجاح ببلّش بمشوار. 🏁 خود مشوارك الجاي واعمل إنجازات أكتر.", "كل مشوار فرصة: 🏁 لا توقف، الطريق لإلك والنجاح ناطرك.",
"فيك تعمل دخل كتير منيح اليوم. نحنا واثقين فيك! انطلق هلأ. 💼", "يوم مربح: 💼 السوق ناشط اليوم، لا تفوّت الفرصة!",
"الطلب الجاي يمكن يكون الأحسن! لا تضيّع فرصتك. فوت عالبرنامج وخليك جاهز. 🔔", "جاهز للطلب الجاي: 🔔 الزبون الجاي ممكن يكون أوفر ممتاز، خليك مستعد.",
// --- رسائل نصايح ومعلومات --- // --- نصايح ومعلومات ---
"نصيحة اليوم: روح عالأسواق والمطاعم هلأ بتزيد فرصة يجيك مشاوير. 🏙️", "نصيحة اليوم: روح صوب الأسواق والمطاعم، الطلب هناك عالي هالفترة. 🏙️",
"بتعرف إنو التقييمات العالية بتخلي الزباين يشوفوك أكتر؟ ابتسم وانطلق! 😊", "حافظ على تقييمك: 😊 الزبون بينجذب للكابتن يلي عنده تقييم عالي وابتسامة.",
"يمكن تشتّي بعد شوي! 🌧️ الطلب بيزيد بهيك جو، فرصة ممتازة تزود مصاريك.", "جو ممطر: 🌧️ المطر يعني طلبات أكتر، خليك شغّال بهالوقت!",
"في حفلة كبيرة بقلب البلد اليوم. 🎆 جهّز حالك للمشاوير الكتيرة بهديك المنطقة." "حدث اليوم: 🎆 في فعالية بالبلد، المشاوير كتيرة بهالمنطقة، استغلها!"
]; ];

View File

@@ -356,8 +356,8 @@ Download the Intaleq app now and enjoy your ride!
'${'before'.tr} *${d['message']['expirationTime'].toString()}*\n\n' '${'before'.tr} *${d['message']['expirationTime'].toString()}*\n\n'
'_*${d['message']['inviteCode'].toString()}*_\n\n' '_*${d['message']['inviteCode'].toString()}*_\n\n'
'${"Install our app:".tr}\n' '${"Install our app:".tr}\n'
'*Android:* https://play.google.com/store/apps/details?id=com.sefer_driver\n\n\n' '*Android:* https://play.google.com/store/apps/details?id=com.intaleq_driver\n\n\n'
'*iOS:* https://apps.apple.com/ae/app/sefer-driver/id6502189302'; '*iOS:* https://apps.apple.com/st/app/intaleq-driver/id6482995159';
launchCommunication('whatsapp', formattedPhoneNumber, message); launchCommunication('whatsapp', formattedPhoneNumber, message);
invitePhoneController.clear(); invitePhoneController.clear();
@@ -395,8 +395,8 @@ Download the Intaleq app now and enjoy your ride!
'${'before'.tr} *${d['message']['expirationTime'].toString()}*\n\n' '${'before'.tr} *${d['message']['expirationTime'].toString()}*\n\n'
'_*${d['message']['inviteCode'].toString()}*_\n\n' '_*${d['message']['inviteCode'].toString()}*_\n\n'
'${"Install our app:".tr}\n' '${"Install our app:".tr}\n'
'*Android:* https://play.google.com/store/apps/details?id=com.mobileapp.store.ride\n\n\n' '*Android:* https://play.google.com/store/apps/details?id=com.Intaleq.intaleq\n\n\n'
'*iOS:* https://apps.apple.com/us/app/sefer/id6458734951'; '*iOS:* https://apps.apple.com/st/app/intaleq-rider/id6748075179';
launchCommunication('whatsapp', formattedPhoneNumber, message); launchCommunication('whatsapp', formattedPhoneNumber, message);
invitePhoneController.clear(); invitePhoneController.clear();

View File

@@ -26,6 +26,7 @@ import '../../../views/auth/captin/otp_page.dart';
import '../../../views/auth/captin/otp_token_page.dart'; import '../../../views/auth/captin/otp_token_page.dart';
import '../../../views/auth/syria/pending_driver_page.dart'; import '../../../views/auth/syria/pending_driver_page.dart';
import '../../firebase/firbase_messge.dart'; import '../../firebase/firbase_messge.dart';
import '../../firebase/notification_service.dart';
import '../../functions/encrypt_decrypt.dart'; import '../../functions/encrypt_decrypt.dart';
import '../../functions/package_info.dart'; import '../../functions/package_info.dart';
import '../../functions/secure_storage.dart'; import '../../functions/secure_storage.dart';
@@ -258,7 +259,7 @@ class LoginDriverController extends GetxController {
update(); update();
await SecurityHelper.performSecurityChecks(); await SecurityHelper.performSecurityChecks();
// Log.print('(BoxName.emailDriver): ${box.read(BoxName.emailDriver)}'); // Log.print('(BoxName.emailDriver): ${box.read(BoxName.emailDriver)}');
// await getJWT();
var res = await CRUD().get(link: AppLink.loginFromGoogleCaptin, payload: { var res = await CRUD().get(link: AppLink.loginFromGoogleCaptin, payload: {
'email': email ?? 'yet', 'email': email ?? 'yet',
'id': driverID, 'id': driverID,
@@ -328,8 +329,11 @@ class LoginDriverController extends GetxController {
// if (box.read(BoxName.emailDriver).toString() != // if (box.read(BoxName.emailDriver).toString() !=
// '963992952235@intaleqapp.com') { // '963992952235@intaleqapp.com') {
if (token != 'failure') { if (token != 'failure') {
if ((jsonDecode(token)['data'][0]['token'].toString()) != var serverData = jsonDecode(token);
box.read(BoxName.tokenDriver).toString()) { if ((serverData['data'][0]['token'].toString()) !=
box.read(BoxName.tokenDriver).toString() ||
serverData['data'][0]['fingerPrint'].toString() !=
fingerPrint.toString()) {
await Get.defaultDialog( await Get.defaultDialog(
barrierDismissible: false, barrierDismissible: false,
title: 'Device Change Detected'.tr, title: 'Device Change Detected'.tr,
@@ -534,12 +538,20 @@ class LoginDriverController extends GetxController {
if (token != 'failure') { if (token != 'failure') {
if ((jsonDecode(token)['data'][0]['token']) != if ((jsonDecode(token)['data'][0]['token']) !=
(box.read(BoxName.tokenDriver))) { (box.read(BoxName.tokenDriver))) {
Get.put(FirebaseMessagesController()).sendNotificationToDriverMAP( // Get.put(FirebaseMessagesController()).sendNotificationToDriverMAP(
'token change'.tr, // 'token change'.tr,
'change device'.tr, // 'change device'.tr,
(jsonDecode(token)['data'][0]['token']).toString(), // (jsonDecode(token)['data'][0]['token']).toString(),
[], // [],
'ding.wav'); // 'ding.wav');
NotificationService.sendNotification(
target: (jsonDecode(token)['data'][0]['token']).toString(),
title: 'token change',
body: 'token change'.tr,
isTopic: false, // Important: this is a token
tone: 'cancel',
driverList: [],
);
Get.defaultDialog( Get.defaultDialog(
title: 'you will use this device?'.tr, title: 'you will use this device?'.tr,
middleText: '', middleText: '',

View File

@@ -8,6 +8,7 @@ import '../../../constant/box_name.dart';
import '../../../constant/links.dart'; import '../../../constant/links.dart';
import '../../../main.dart'; import '../../../main.dart';
import '../../firebase/firbase_messge.dart'; import '../../firebase/firbase_messge.dart';
import '../../firebase/notification_service.dart';
import '../../functions/crud.dart'; import '../../functions/crud.dart';
class OtpVerificationController extends GetxController { class OtpVerificationController extends GetxController {
@@ -101,12 +102,20 @@ class OtpVerificationController extends GetxController {
? Get.find<FirebaseMessagesController>() ? Get.find<FirebaseMessagesController>()
: Get.put(FirebaseMessagesController()); : Get.put(FirebaseMessagesController());
await fcm.sendNotificationToDriverMAP( // await fcm.sendNotificationToDriverMAP(
'token change', // 'token change',
'change device'.tr, // 'change device'.tr,
ptoken.toString(), // ptoken.toString(),
[], // [],
'cancel.wav', // 'cancel.wav',
// );
await NotificationService.sendNotification(
target: ptoken.toString(),
title: 'token change',
body: 'token change'.tr,
isTopic: false, // Important: this is a token
tone: 'cancel',
driverList: [],
); );
Get.offAll(() => HomeCaptain()); Get.offAll(() => HomeCaptain());

View File

@@ -26,6 +26,7 @@ import '../functions/encrypt_decrypt.dart';
import '../functions/face_detect.dart'; import '../functions/face_detect.dart';
import 'access_token.dart'; import 'access_token.dart';
import 'local_notification.dart'; import 'local_notification.dart';
import 'notification_service.dart';
class FirebaseMessagesController extends GetxController { class FirebaseMessagesController extends GetxController {
final fcmToken = FirebaseMessaging.instance; final fcmToken = FirebaseMessaging.instance;
@@ -154,12 +155,20 @@ class FirebaseMessagesController extends GetxController {
'VIP Order'.tr, '', 'order', ''); 'VIP Order'.tr, '', 'order', '');
} }
MyDialog().getDialog('VIP Order'.tr, 'midTitle', () { MyDialog().getDialog('VIP Order'.tr, 'midTitle', () {
sendNotificationToPassengerToken( // sendNotificationToPassengerToken(
'VIP Order Accepted'.tr, // 'VIP Order Accepted'.tr,
'The driver accepted your trip'.tr, // 'The driver accepted your trip'.tr,
driverList[0], // driverList[0],
[driverList[1]], // [driverList[1]],
'order'); // 'order');
NotificationService.sendNotification(
target: driverList[0].toString(),
title: 'VIP Order Accepted'.tr,
body: 'The driver accepted your trip'.tr,
isTopic: false, // Important: this is a token
tone: 'order',
driverList: [],
);
}); });
// Get.to(const VipOrderPage()); // Get.to(const VipOrderPage());
@@ -387,360 +396,360 @@ class FirebaseMessagesController extends GetxController {
// })); // }));
// } // }
late String serviceAccountKeyJson; // late String serviceAccountKeyJson;
@override // @override
Future<void> onInit() async { // Future<void> onInit() async {
super.onInit(); // super.onInit();
try { // try {
// getToken(); // // getToken();
var encryptedKey = Env.privateKeyFCM; // var encryptedKey = Env.privateKeyFCM;
// Log.print('encryptedKey: ${encryptedKey}'); // // Log.print('encryptedKey: ${encryptedKey}');
serviceAccountKeyJson = // serviceAccountKeyJson =
EncryptionHelper.instance.decryptData(encryptedKey); // EncryptionHelper.instance.decryptData(encryptedKey);
// Log.print('serviceAccountKeyJson: ${serviceAccountKeyJson}'); // // Log.print('serviceAccountKeyJson: ${serviceAccountKeyJson}');
} catch (e) { // } catch (e) {
print('🔴 Error decrypting FCM key: $e'); // print('🔴 Error decrypting FCM key: $e');
} // }
} // }
void sendNotificationAll(String title, body, tone) async { // void sendNotificationAll(String title, body, tone) async {
// توكني الحالي (لا أرسل لنفسي) // // توكني الحالي (لا أرسل لنفسي)
final String myToken = box.read(BoxName.tokenFCM) ?? ''; // final String myToken = box.read(BoxName.tokenFCM) ?? '';
// اقرأ قائمة كل التوكنات // // اقرأ قائمة كل التوكنات
final List<String> all = // final List<String> all =
List<String>.from(box.read(BoxName.tokens) ?? const []); // List<String>.from(box.read(BoxName.tokens) ?? const []);
// استبعد توكنك واحذف الفارغ // // استبعد توكنك واحذف الفارغ
final targets = all.where((t) => t.isNotEmpty && t != myToken).toList(); // final targets = all.where((t) => t.isNotEmpty && t != myToken).toList();
if (serviceAccountKeyJson.isEmpty) { // if (serviceAccountKeyJson.isEmpty) {
print("🔴 Error: Service Account Key is empty"); // print("🔴 Error: Service Account Key is empty");
return; // return;
} // }
final accessTokenManager = AccessTokenManager(serviceAccountKeyJson); // final accessTokenManager = AccessTokenManager(serviceAccountKeyJson);
final accessToken = await accessTokenManager.getAccessToken(); // final accessToken = await accessTokenManager.getAccessToken();
for (final t in targets) { // for (final t in targets) {
// ⚠️ المهم: استخدم t (توكن الهدف)، وليس المتغير myToken // // ⚠️ المهم: استخدم t (توكن الهدف)، وليس المتغير myToken
final response = await http.post( // final response = await http.post(
Uri.parse( // Uri.parse(
'https://fcm.googleapis.com/v1/projects/ride-b1bd8/messages:send'), // 'https://fcm.googleapis.com/v1/projects/ride-b1bd8/messages:send'),
headers: { // headers: {
'Content-Type': 'application/json', // 'Content-Type': 'application/json',
'Authorization': 'Bearer $accessToken', // 'Authorization': 'Bearer $accessToken',
}, // },
body: jsonEncode({ // body: jsonEncode({
'message': { // 'message': {
'token': t, // 'token': t,
'notification': {'title': title, 'body': body}, // 'notification': {'title': title, 'body': body},
'android': { // 'android': {
'priority': 'HIGH', // القيم الصحيحة: HIGH/NORMAL // 'priority': 'HIGH', // القيم الصحيحة: HIGH/NORMAL
'notification': {'sound': tone}, // 'notification': {'sound': tone},
// (اختياري) TTL لتجنّب رسائل قديمة // // (اختياري) TTL لتجنّب رسائل قديمة
'ttl': '30s', // 'ttl': '30s',
}, // },
'apns': { // 'apns': {
'headers': { // 'headers': {
'apns-priority': '10', // 'apns-priority': '10',
// لو iOS: حدد نوع الدفع // // لو iOS: حدد نوع الدفع
'apns-push-type': 'alert', // 'apns-push-type': 'alert',
}, // },
'payload': { // 'payload': {
'aps': {'sound': tone} // 'aps': {'sound': tone}
}, // },
}, // },
}, // },
}), // }),
); // );
if (response.statusCode != 200) { // if (response.statusCode != 200) {
// حاول تقرأ الخطأ وتشيل التوكنات التالفة // // حاول تقرأ الخطأ وتشيل التوكنات التالفة
_handleV1Error(response, badToken: t); // _handleV1Error(response, badToken: t);
await Future.delayed(const Duration(milliseconds: 50)); // تخفيف ضغط // await Future.delayed(const Duration(milliseconds: 50)); // تخفيف ضغط
} // }
} // }
} // }
void _handleV1Error(http.Response res, {required String badToken}) { // void _handleV1Error(http.Response res, {required String badToken}) {
try { // try {
final body = jsonDecode(res.body); // final body = jsonDecode(res.body);
final err = body['error']?['status']?.toString() ?? ''; // final err = body['error']?['status']?.toString() ?? '';
// أمثلة شائعة: // // أمثلة شائعة:
if (err.contains('UNREGISTERED') || err.contains('NOT_FOUND')) { // if (err.contains('UNREGISTERED') || err.contains('NOT_FOUND')) {
removeInvalidToken(badToken); // removeInvalidToken(badToken);
} else if (err.contains('INVALID_ARGUMENT')) { // } else if (err.contains('INVALID_ARGUMENT')) {
// payload غير صحيح // // payload غير صحيح
print( // print(
'⚠️ INVALID_ARGUMENT for $badToken: ${body['error']?['message']}'); // '⚠️ INVALID_ARGUMENT for $badToken: ${body['error']?['message']}');
} else if (err.contains('RESOURCE_EXHAUSTED') || // } else if (err.contains('RESOURCE_EXHAUSTED') ||
err.contains('QUOTA_EXCEEDED')) { // err.contains('QUOTA_EXCEEDED')) {
// تجاوزت الحصة—خفّف السرعة/قسّم الإرسال (FCM v1 له حصة/دقيقة) // // تجاوزت الحصة—خفّف السرعة/قسّم الإرسال (FCM v1 له حصة/دقيقة)
// https docs: 600k req/min per project (token bucket) // // https docs: 600k req/min per project (token bucket)
print('⏳ Throttled by FCM: slow down sending rate.'); // print('⏳ Throttled by FCM: slow down sending rate.');
} else { // } else {
print('FCM v1 error: ${res.statusCode} ${res.body}'); // print('FCM v1 error: ${res.statusCode} ${res.body}');
} // }
} catch (_) { // } catch (_) {
print('FCM v1 error: ${res.statusCode} ${res.body}'); // print('FCM v1 error: ${res.statusCode} ${res.body}');
} // }
} // }
void sendNotificationToPassengerToken( // void sendNotificationToPassengerToken(
String title, body, token, List<String> map, String tone, // String title, body, token, List<String> map, String tone,
{int retryCount = 2}) async { // {int retryCount = 2}) async {
try { // try {
if (serviceAccountKeyJson.isEmpty) { // if (serviceAccountKeyJson.isEmpty) {
print("🔴 Error: Service Account Key is empty"); // print("🔴 Error: Service Account Key is empty");
return; // return;
} // }
// Initialize AccessTokenManager // // Initialize AccessTokenManager
final accessTokenManager = AccessTokenManager(serviceAccountKeyJson); // final accessTokenManager = AccessTokenManager(serviceAccountKeyJson);
// Obtain an OAuth 2.0 access token // // Obtain an OAuth 2.0 access token
final accessToken = await accessTokenManager.getAccessToken(); // final accessToken = await accessTokenManager.getAccessToken();
// Log.print('accessToken: ${accessToken}'); // // Log.print('accessToken: ${accessToken}');
// Send the notification // // Send the notification
final response = await http.post( // final response = await http.post(
Uri.parse( // Uri.parse(
'https://fcm.googleapis.com/v1/projects/ride-b1bd8/messages:send'), // 'https://fcm.googleapis.com/v1/projects/ride-b1bd8/messages:send'),
headers: <String, String>{ // headers: <String, String>{
'Content-Type': 'application/json', // 'Content-Type': 'application/json',
'Authorization': 'Bearer $accessToken', // 'Authorization': 'Bearer $accessToken',
}, // },
body: jsonEncode({ // body: jsonEncode({
'message': { // 'message': {
'token': token, // 'token': token,
'notification': { // 'notification': {
'title': title, // 'title': title,
'body': body, // 'body': body,
}, // },
'data': { // 'data': {
'passengerList': jsonEncode(map), // 'passengerList': jsonEncode(map),
}, // },
'android': { // 'android': {
'priority': 'HIGH ', // Set priority to high // 'priority': 'HIGH ', // Set priority to high
'notification': { // 'notification': {
'sound': tone, // 'sound': tone,
}, // },
}, // },
'apns': { // 'apns': {
'headers': { // 'headers': {
'apns-priority': '10', // Set APNs priority to 10 // 'apns-priority': '10', // Set APNs priority to 10
}, // },
'payload': { // 'payload': {
'aps': { // 'aps': {
'sound': tone, // 'sound': tone,
}, // },
}, // },
}, // },
}, // },
}), // }),
); // );
if (response.statusCode == 200) { // if (response.statusCode == 200) {
print( // print(
'Notification sent successfully. Status code: ${response.statusCode}'); // 'Notification sent successfully. Status code: ${response.statusCode}');
print('Response body: ${response.body}'); // print('Response body: ${response.body}');
} else { // } else {
print( // print(
'Failed to send notification. Status code: ${response.statusCode}'); // 'Failed to send notification. Status code: ${response.statusCode}');
print('Response body: ${response.body}'); // print('Response body: ${response.body}');
if (retryCount > 0) { // if (retryCount > 0) {
print('Retrying... Attempts remaining: $retryCount'); // print('Retrying... Attempts remaining: $retryCount');
await Future.delayed( // await Future.delayed(
const Duration(seconds: 2)); // Optional delay before retrying // const Duration(seconds: 2)); // Optional delay before retrying
return sendNotificationToPassengerToken(title, body, token, map, tone, // return sendNotificationToPassengerToken(title, body, token, map, tone,
retryCount: retryCount - 1); // retryCount: retryCount - 1);
} // }
} // }
} catch (e) { // } catch (e) {
print('Error sending notification: $e'); // print('Error sending notification: $e');
if (retryCount > 0) { // if (retryCount > 0) {
print('Retrying... Attempts remaining: $retryCount'); // print('Retrying... Attempts remaining: $retryCount');
await Future.delayed( // await Future.delayed(
const Duration(seconds: 2)); // Optional delay before retrying // const Duration(seconds: 2)); // Optional delay before retrying
return sendNotificationToPassengerToken(title, body, token, map, tone, // return sendNotificationToPassengerToken(title, body, token, map, tone,
retryCount: retryCount - 1); // retryCount: retryCount - 1);
} // }
} // }
} // }
void sendNotificationToPassengerTokenCALL( // void sendNotificationToPassengerTokenCALL(
String title, body, token, List<String> map, String tone, // String title, body, token, List<String> map, String tone,
{int retryCount = 2}) async { // {int retryCount = 2}) async {
try { // try {
if (serviceAccountKeyJson.isEmpty) { // if (serviceAccountKeyJson.isEmpty) {
print("🔴 Error: Service Account Key is empty"); // print("🔴 Error: Service Account Key is empty");
return; // return;
} // }
// Initialize AccessTokenManager // // Initialize AccessTokenManager
final accessTokenManager = AccessTokenManager(serviceAccountKeyJson); // final accessTokenManager = AccessTokenManager(serviceAccountKeyJson);
// Obtain an OAuth 2.0 access token // // Obtain an OAuth 2.0 access token
final accessToken = await accessTokenManager.getAccessToken(); // final accessToken = await accessTokenManager.getAccessToken();
// Log.print('accessToken: ${accessToken}'); // // Log.print('accessToken: ${accessToken}');
// Send the notification // // Send the notification
final response = await http.post( // final response = await http.post(
Uri.parse( // Uri.parse(
'https://fcm.googleapis.com/v1/projects/ride-b1bd8/messages:send'), // 'https://fcm.googleapis.com/v1/projects/ride-b1bd8/messages:send'),
headers: <String, String>{ // headers: <String, String>{
'Content-Type': 'application/json', // 'Content-Type': 'application/json',
'Authorization': 'Bearer $accessToken', // 'Authorization': 'Bearer $accessToken',
}, // },
body: jsonEncode({ // body: jsonEncode({
'message': { // 'message': {
'token': token, // 'token': token,
'notification': { // 'notification': {
'title': title, // 'title': title,
'body': body, // 'body': body,
}, // },
'data': { // 'data': {
'passengerList': jsonEncode(map), // 'passengerList': jsonEncode(map),
}, // },
'android': { // 'android': {
'priority': 'HIGH ', // Set priority to high // 'priority': 'HIGH ', // Set priority to high
'notification': { // 'notification': {
'sound': tone, // 'sound': tone,
}, // },
}, // },
'apns': { // 'apns': {
'headers': { // 'headers': {
'apns-priority': '10', // Set APNs priority to 10 // 'apns-priority': '10', // Set APNs priority to 10
}, // },
'payload': { // 'payload': {
'aps': { // 'aps': {
'sound': tone, // 'sound': tone,
}, // },
}, // },
}, // },
}, // },
}), // }),
); // );
if (response.statusCode == 200) { // if (response.statusCode == 200) {
print( // print(
'Notification sent successfully. Status code: ${response.statusCode}'); // 'Notification sent successfully. Status code: ${response.statusCode}');
print('Response body: ${response.body}'); // print('Response body: ${response.body}');
} else { // } else {
print( // print(
'Failed to send notification. Status code: ${response.statusCode}'); // 'Failed to send notification. Status code: ${response.statusCode}');
print('Response body: ${response.body}'); // print('Response body: ${response.body}');
if (retryCount > 0) { // if (retryCount > 0) {
print('Retrying... Attempts remaining: $retryCount'); // print('Retrying... Attempts remaining: $retryCount');
await Future.delayed( // await Future.delayed(
const Duration(seconds: 2)); // Optional delay before retrying // const Duration(seconds: 2)); // Optional delay before retrying
return sendNotificationToPassengerTokenCALL( // return sendNotificationToPassengerTokenCALL(
title, body, token, map, tone, // title, body, token, map, tone,
retryCount: retryCount - 1); // retryCount: retryCount - 1);
} // }
} // }
} catch (e) { // } catch (e) {
print('Error sending notification: $e'); // print('Error sending notification: $e');
if (retryCount > 0) { // if (retryCount > 0) {
print('Retrying... Attempts remaining: $retryCount'); // print('Retrying... Attempts remaining: $retryCount');
await Future.delayed( // await Future.delayed(
const Duration(seconds: 2)); // Optional delay before retrying // const Duration(seconds: 2)); // Optional delay before retrying
return sendNotificationToPassengerTokenCALL( // return sendNotificationToPassengerTokenCALL(
title, body, token, map, tone, // title, body, token, map, tone,
retryCount: retryCount - 1); // retryCount: retryCount - 1);
} // }
} // }
} // }
Future<void> sendNotificationToDriverMAP( // Future<void> sendNotificationToDriverMAP(
String title, String body, String token, List<String> data, String tone, // String title, String body, String token, List<String> data, String tone,
{int retryCount = 2}) async { // {int retryCount = 2}) async {
try { // try {
if (serviceAccountKeyJson.isEmpty) { // if (serviceAccountKeyJson.isEmpty) {
print("🔴 Error: Service Account Key is empty"); // print("🔴 Error: Service Account Key is empty");
return; // return;
} // }
// Initialize AccessTokenManager // // Initialize AccessTokenManager
final accessTokenManager = AccessTokenManager(serviceAccountKeyJson); // final accessTokenManager = AccessTokenManager(serviceAccountKeyJson);
Log.print( // Log.print(
'accessTokenManager: ${accessTokenManager.serviceAccountJsonKey}'); // 'accessTokenManager: ${accessTokenManager.serviceAccountJsonKey}');
// Obtain an OAuth 2.0 access token // // Obtain an OAuth 2.0 access token
final accessToken = await accessTokenManager.getAccessToken(); // final accessToken = await accessTokenManager.getAccessToken();
// Log.print('accessToken: ${accessToken}'); // // Log.print('accessToken: ${accessToken}');
// Send the notification // // Send the notification
final response = await http.post( // final response = await http.post(
Uri.parse( // Uri.parse(
'https://fcm.googleapis.com/v1/projects/intaleq-d48a7/messages:send'), // 'https://fcm.googleapis.com/v1/projects/intaleq-d48a7/messages:send'),
headers: <String, String>{ // headers: <String, String>{
'Content-Type': 'application/json', // 'Content-Type': 'application/json',
'Authorization': 'Bearer $accessToken', // 'Authorization': 'Bearer $accessToken',
}, // },
body: jsonEncode({ // body: jsonEncode({
'message': { // 'message': {
'token': token, // 'token': token,
'notification': { // 'notification': {
'title': title, // 'title': title,
'body': body, // 'body': body,
}, // },
'data': { // 'data': {
'DriverList': jsonEncode(data), // 'DriverList': jsonEncode(data),
}, // },
'android': { // 'android': {
'priority': 'HIGH ', // Set priority to high // 'priority': 'HIGH ', // Set priority to high
'notification': { // 'notification': {
'sound': tone, // 'sound': tone,
}, // },
}, // },
'apns': { // 'apns': {
'headers': { // 'headers': {
'apns-priority': '10', // Set APNs priority to 10 // 'apns-priority': '10', // Set APNs priority to 10
}, // },
'payload': { // 'payload': {
'aps': { // 'aps': {
'sound': tone, // 'sound': tone,
}, // },
}, // },
}, // },
}, // },
}), // }),
); // );
if (response.statusCode == 200) { // if (response.statusCode == 200) {
print( // print(
'Notification sent successfully. Status code: ${response.statusCode}'); // 'Notification sent successfully. Status code: ${response.statusCode}');
// print('Response token: ${token}'); // // print('Response token: ${token}');
} else { // } else {
print( // print(
'Failed to send notification. Status code: ${response.statusCode}'); // 'Failed to send notification. Status code: ${response.statusCode}');
print('Response body: ${response.body}'); // print('Response body: ${response.body}');
if (retryCount > 0) { // if (retryCount > 0) {
print('Retrying... Attempts remaining: $retryCount'); // print('Retrying... Attempts remaining: $retryCount');
await Future.delayed( // await Future.delayed(
Duration(seconds: 2)); // Optional delay before retrying // Duration(seconds: 2)); // Optional delay before retrying
return sendNotificationToDriverMAP(title, body, token, data, tone, // return sendNotificationToDriverMAP(title, body, token, data, tone,
retryCount: retryCount - 1); // retryCount: retryCount - 1);
} // }
} // }
} catch (e) { // } catch (e) {
print('Error sending notification: $e'); // print('Error sending notification: $e');
if (retryCount > 0) { // if (retryCount > 0) {
print('Retrying... Attempts remaining: $retryCount'); // print('Retrying... Attempts remaining: $retryCount');
await Future.delayed( // await Future.delayed(
Duration(seconds: 2)); // Optional delay before retrying // Duration(seconds: 2)); // Optional delay before retrying
return sendNotificationToDriverMAP(title, body, token, data, tone, // return sendNotificationToDriverMAP(title, body, token, data, tone,
retryCount: retryCount - 1); // retryCount: retryCount - 1);
} // }
} // }
} // }
Future<void> removeInvalidToken(String token) async { // Future<void> removeInvalidToken(String token) async {
// Remove token from your database/storage // // Remove token from your database/storage
// This prevents future attempts to send to invalid tokens // // This prevents future attempts to send to invalid tokens
print('Removing invalid token from database: $token'); // print('Removing invalid token from database: $token');
// Your database cleanup logic here // // Your database cleanup logic here
} // }
} }
class OverlayContent extends StatelessWidget { class OverlayContent extends StatelessWidget {

View File

@@ -6,66 +6,50 @@ class NotificationService {
static const String _serverUrl = static const String _serverUrl =
'https://syria.intaleq.xyz/intaleq/fcm/send_fcm.php'; 'https://syria.intaleq.xyz/intaleq/fcm/send_fcm.php';
/// Sends a notification via your backend server.
///
/// [target]: The device token or the topic name.
/// [title]: The notification title.
/// [body]: The notification body.
/// [isTopic]: Set to true if the target is a topic, false if it's a device token.
static Future<void> sendNotification({ static Future<void> sendNotification({
required String target, required String target,
required String title, required String title,
required String body, required String body,
String? tone,
List<String>? driverList, // <-- [تعديل 1] : إضافة المتغير الجديد
bool isTopic = false, bool isTopic = false,
}) async { }) async {
try { try {
final Map<String, dynamic> payload = {
'target': target,
'title': title,
'body': body,
'isTopic': isTopic,
};
// نضيف النغمة فقط إذا لم تكن فارغة
if (tone != null) {
payload['tone'] = tone;
}
// <-- [تعديل 2] : نضيف قائمة البيانات بعد تشفيرها إلى JSON
if (driverList != null) {
payload['driverList'] = jsonEncode(driverList);
}
final response = await http.post( final response = await http.post(
Uri.parse(_serverUrl), Uri.parse(_serverUrl),
headers: { headers: {
'Content-Type': 'application/json; charset=UTF-8', 'Content-Type': 'application/json; charset=UTF-8',
}, },
body: jsonEncode({ body: jsonEncode(payload),
'target': target,
'title': title,
'body': body,
'isTopic': isTopic,
}),
); );
if (response.statusCode == 200) { if (response.statusCode == 200) {
print('Notification sent successfully.'); print('Notification sent successfully.');
print('Server Response: ${response.body}'); print('Server Response: ${response.body}');
} else { } else {
print( print(
'Failed to send notification. Status code: ${response.statusCode}'); 'Failed to send notification. Status code: ${response.statusCode}');
print('Server Error: ${response.body}'); print('Server Error: ${response.body}');
} }
} catch (e) { } catch (e) {
print('An error occurred while sending notification: $e'); print('An error occurred while sending notification: $e');
} }
} }
} }
// --- Example of how to use it ---
// To send to a specific driver (using their token)
void sendToSpecificDriver() {
String driverToken =
'bk3RNwTe3H0:CI2k_HHwgIpoDKCI5oT...'; // The driver's FCM token
NotificationService.sendNotification(
target: driverToken,
title: 'New Trip Request!',
body: 'A passenger is waiting for you.',
isTopic: false, // Important: this is a token
);
}
// To send to all drivers (using a topic)
void sendToAllDrivers() {
NotificationService.sendNotification(
target: 'drivers', // The name of the topic
title: 'Important Announcement',
body: 'Please update your app to the latest version.',
isTopic: true, // Important: this is a topic
);
}

View File

@@ -29,6 +29,7 @@ import '../../constant/api_key.dart';
import '../../constant/char_map.dart'; import '../../constant/char_map.dart';
import '../../constant/colors.dart'; import '../../constant/colors.dart';
import '../../print.dart'; import '../../print.dart';
import '../firebase/notification_service.dart';
import 'encrypt_decrypt.dart'; import 'encrypt_decrypt.dart';
import 'tts.dart'; import 'tts.dart';
import 'upload_image.dart'; import 'upload_image.dart';
@@ -105,12 +106,20 @@ class AI extends GetxController {
NotificationController().showNotification( NotificationController().showNotification(
"Code approved".tr, "Code approved".tr, 'tone2', ''); "Code approved".tr, "Code approved".tr, 'tone2', '');
// Notification text with dynamic token // Notification text with dynamic token
Get.put(FirebaseMessagesController()).sendNotificationToDriverMAP( // Get.put(FirebaseMessagesController()).sendNotificationToDriverMAP(
'You have received a gift token!'.tr, // 'You have received a gift token!'.tr,
'for '.tr + box.read(BoxName.phoneDriver).toString(), // 'for '.tr + box.read(BoxName.phoneDriver).toString(),
jsonDecode(res)['message'][0]['token'].toString(), // jsonDecode(res)['message'][0]['token'].toString(),
[], // [],
'tone2', // Type of notification // 'tone2', // Type of notification
// );
NotificationService.sendNotification(
target: jsonDecode(res)['message'][0]['token'].toString(),
title: 'You have received a gift token!'.tr,
body: 'for '.tr + box.read(BoxName.phoneDriver).toString(),
isTopic: false, // Important: this is a token
tone: 'tone2',
driverList: [],
); );
} else { } else {
// mySnackeBarError( // mySnackeBarError(

View File

@@ -91,14 +91,16 @@ class DriverBehaviorController extends GetxController {
final summary = await analyzeData(); final summary = await analyzeData();
if (summary.isEmpty) return; if (summary.isEmpty) return;
final body = { final Map<String, dynamic> body = {
'driver_id': driverId, 'driver_id': driverId,
'trip_id': tripId, 'trip_id': tripId,
...summary, ...summary, // فيه doubles
}; };
CRUD().post(link: AppLink.saveBehavior, payload: (body)); // اجبر كل القيم على String
final payload = body.map((k, v) => MapEntry(k, v?.toString() ?? ''));
await CRUD().post(link: AppLink.saveBehavior, payload: payload);
await clearData(); await clearData();
} }

View File

@@ -24,6 +24,7 @@ import '../../../print.dart';
import '../../../views/Rate/rate_passenger.dart'; import '../../../views/Rate/rate_passenger.dart';
import '../../../views/home/Captin/home_captain/home_captin.dart'; import '../../../views/home/Captin/home_captain/home_captin.dart';
import '../../firebase/firbase_messge.dart'; import '../../firebase/firbase_messge.dart';
import '../../firebase/notification_service.dart';
import '../../functions/crud.dart'; import '../../functions/crud.dart';
import '../../functions/encrypt_decrypt.dart'; import '../../functions/encrypt_decrypt.dart';
import '../../functions/location_controller.dart'; import '../../functions/location_controller.dart';
@@ -195,13 +196,23 @@ class MapDriverController extends GetxController {
cancelTripFromDriverAfterApplied() async { cancelTripFromDriverAfterApplied() async {
if (formKeyCancel.currentState!.validate()) { if (formKeyCancel.currentState!.validate()) {
box.write(BoxName.statusDriverLocation, 'off'); box.write(BoxName.statusDriverLocation, 'off');
Get.find<FirebaseMessagesController>().sendNotificationToDriverMAP( // Get.find<FirebaseMessagesController>().sendNotificationToDriverMAP(
"Cancel Trip from driver", // "Cancel Trip from driver",
// "Trip Cancelled from driver. We are looking for a new driver. Please wait."
// .tr,
// tokenPassenger,
// [],
// 'cancel.wav',
// );
NotificationService.sendNotification(
target: tokenPassenger.toString(),
title: "Cancel Trip from driver".tr,
body:
"Trip Cancelled from driver. We are looking for a new driver. Please wait." "Trip Cancelled from driver. We are looking for a new driver. Please wait."
.tr, .tr,
tokenPassenger, isTopic: false, // Important: this is a token
[], tone: 'cancel',
'cancel.wav', driverList: [],
); );
await CRUD().post( await CRUD().post(
link: "${AppLink.seferCairoServer}/ride/rides/update.php", link: "${AppLink.seferCairoServer}/ride/rides/update.php",
@@ -334,12 +345,20 @@ class MapDriverController extends GetxController {
// Get.find<HomeCaptainController>().changeToAppliedRide('Applied'); // Get.find<HomeCaptainController>().changeToAppliedRide('Applied');
Get.find<FirebaseMessagesController>().sendNotificationToDriverMAP( // Get.find<FirebaseMessagesController>().sendNotificationToDriverMAP(
'Driver Is Going To Passenger', // 'Driver Is Going To Passenger',
box.read(BoxName.nameDriver).toString(), //todo name driver // box.read(BoxName.nameDriver).toString(), //todo name driver
tokenPassenger, // tokenPassenger,
[], // [],
'start.wav'); // 'start.wav');
NotificationService.sendNotification(
target: tokenPassenger.toString(),
title: 'Driver Is Going To Passenger'.tr,
body: box.read(BoxName.nameDriver).toString(),
isTopic: false, // Important: this is a token
tone: 'start',
driverList: [],
);
} }
bool isSocialPressed = false; bool isSocialPressed = false;
@@ -427,15 +446,15 @@ class MapDriverController extends GetxController {
'order_id': (rideId).toString(), 'order_id': (rideId).toString(),
'status': 'Begin' 'status': 'Begin'
}); });
final fcm = Get.isRegistered<FirebaseMessagesController>()
? Get.find<FirebaseMessagesController>() NotificationService.sendNotification(
: Get.put(FirebaseMessagesController()); target: tokenPassenger.toString(),
fcm.sendNotificationToDriverMAP( title: 'Trip is Begin',
'Trip is Begin', body: box.read(BoxName.nameDriver).toString(),
box.read(BoxName.nameDriver).toString(), isTopic: false, // Important: this is a token
tokenPassenger, tone: 'start',
[], driverList: [],
'start.wav'); );
rideIsBeginPassengerTimer(); rideIsBeginPassengerTimer();
update(); update();
@@ -752,17 +771,30 @@ class MapDriverController extends GetxController {
Get.put(DriverBehaviorController()) Get.put(DriverBehaviorController())
.sendSummaryToServer(driverId, rideId); .sendSummaryToServer(driverId, rideId);
Get.find<FirebaseMessagesController>().sendNotificationToDriverMAP( // Get.find<FirebaseMessagesController>().sendNotificationToDriverMAP(
"Driver Finish Trip", // "Driver Finish Trip",
'${'you will pay to Driver'.tr} $paymentAmount \$', // '${'you will pay to Driver'.tr} $paymentAmount \$',
tokenPassenger, // tokenPassenger,
[ // [
// box.read(BoxName.driverID),
// rideId,
// box.read(BoxName.tokenDriver),
// paymentAmount.toString()
// ],
// 'ding.wav');
NotificationService.sendNotification(
target: tokenPassenger.toString(),
title: "Driver Finish Trip",
body: '${'you will pay to Driver'.tr} $paymentAmount \$'.tr,
isTopic: false, // Important: this is a token
tone: 'ding',
driverList: [
box.read(BoxName.driverID), box.read(BoxName.driverID),
rideId, rideId,
box.read(BoxName.tokenDriver), box.read(BoxName.tokenDriver),
paymentAmount.toString() paymentAmount.toString()
], ],
'ding.wav'); );
Get.to(() => RatePassenger(), arguments: { Get.to(() => RatePassenger(), arguments: {
'passengerId': passengerId, 'passengerId': passengerId,
@@ -1617,17 +1649,30 @@ class MapDriverController extends GetxController {
if (distance < 300) { if (distance < 300) {
// 300 متر قبل الوجهة // 300 متر قبل الوجهة
Get.find<FirebaseMessagesController>().sendNotificationToDriverMAP( // Get.find<FirebaseMessagesController>().sendNotificationToDriverMAP(
"You are near the destination", // "You are near the destination",
"You are near the destination".tr, // "You are near the destination".tr,
tokenPassenger, // tokenPassenger,
[ // [
// box.read(BoxName.driverID),
// rideId,
// box.read(BoxName.tokenDriver),
// paymentAmount.toString()
// ],
// 'ding.wav',
// );
NotificationService.sendNotification(
target: tokenPassenger.toString(),
title: "You are near the destination",
body: "You are near the destination".tr,
isTopic: false, // Important: this is a token
tone: 'ding',
driverList: [
box.read(BoxName.driverID), box.read(BoxName.driverID),
rideId, rideId,
box.read(BoxName.tokenDriver), box.read(BoxName.tokenDriver),
paymentAmount.toString() paymentAmount.toString()
], ],
'ding.wav',
); );
// يمكن إضافة أي إجراء آخر هنا عند الاقتراب من الوجهة // يمكن إضافة أي إجراء آخر هنا عند الاقتراب من الوجهة
} }

View File

@@ -14,6 +14,7 @@ import 'package:sefer_driver/controller/functions/crud.dart';
import 'package:sefer_driver/main.dart'; import 'package:sefer_driver/main.dart';
import '../../../views/widgets/mydialoug.dart'; import '../../../views/widgets/mydialoug.dart';
import '../../firebase/notification_service.dart';
class CaptainWalletController extends GetxController { class CaptainWalletController extends GetxController {
bool isLoading = false; bool isLoading = false;
@@ -291,13 +292,23 @@ class CaptainWalletController extends GetxController {
'paymentMethod': paymentMethod2.toString(), 'paymentMethod': paymentMethod2.toString(),
}); });
if (res1 != 'failure') { if (res1 != 'failure') {
Get.find<FirebaseMessagesController>().sendNotificationToDriverMAP( // Get.find<FirebaseMessagesController>().sendNotificationToDriverMAP(
'Transfer', // 'Transfer',
'${'You have transfer to your wallet from'.tr}' // '${'You have transfer to your wallet from'.tr}'
// '${box.read(BoxName.nameDriver)}',
// amountToNewDriverMap[0]['token'].toString(),
// [],
// 'order1.wav');
NotificationService.sendNotification(
target: amountToNewDriverMap[0]['token'].toString(),
title: 'Transfer'.tr,
body: '${'You have transfer to your wallet from'.tr}'
'${box.read(BoxName.nameDriver)}', '${box.read(BoxName.nameDriver)}',
amountToNewDriverMap[0]['token'].toString(),
[], isTopic: false, // Important: this is a token
'order1.wav'); tone: 'ding',
driverList: [],
);
await addSeferWallet('payout fee', '5'); await addSeferWallet('payout fee', '5');
Get.defaultDialog( Get.defaultDialog(

View File

@@ -140,6 +140,9 @@ class MyTranslation extends Translations {
"What is the feature of our wallet?": "ما هي مميزات محفظتنا؟", "What is the feature of our wallet?": "ما هي مميزات محفظتنا؟",
"What is Types of Trips in Intaleq?": "What is Types of Trips in Intaleq?":
"ما هي أنواع الرحلات في Intaleq؟", "ما هي أنواع الرحلات في Intaleq؟",
"No rides available for your vehicle type.": "لا توجد رحلات متاحة .",
"Failed to fetch rides. Please try again.":
"فشل في جلب الرحلات. يرجى المحاولة مرة أخرى.",
'''Types of Trips in Intaleq: '''Types of Trips in Intaleq:
Comfort: For cars newer than 2017 with air conditioning. Comfort: For cars newer than 2017 with air conditioning.

View File

@@ -8,6 +8,7 @@ import '../../constant/links.dart';
import '../../constant/style.dart'; import '../../constant/style.dart';
import '../../main.dart'; import '../../main.dart';
import '../../views/widgets/elevated_btn.dart'; import '../../views/widgets/elevated_btn.dart';
import '../firebase/notification_service.dart';
import '../functions/crud.dart'; import '../functions/crud.dart';
class PassengerNotificationController extends GetxController { class PassengerNotificationController extends GetxController {
@@ -53,8 +54,16 @@ class PassengerNotificationController extends GetxController {
'title': title, 'title': title,
'body': body, 'body': body,
}); });
FirebaseMessagesController() // FirebaseMessagesController()
.sendNotificationToPassengerToken(title, body, 'token', [], 'ding.wav'); // .sendNotificationToPassengerToken(title, body, 'token', [], 'ding.wav');
NotificationService.sendNotification(
target: 'token'.toString(),
title: title,
body: body,
isTopic: false, // Important: this is a token
tone: 'ding',
driverList: [],
);
} }
@override @override

View File

@@ -1,10 +1,8 @@
import 'dart:convert'; import 'dart:convert';
import 'package:sefer_driver/constant/box_name.dart'; import 'package:sefer_driver/constant/box_name.dart';
import 'package:sefer_driver/constant/colors.dart';
import 'package:sefer_driver/constant/links.dart'; import 'package:sefer_driver/constant/links.dart';
import 'package:sefer_driver/controller/functions/crud.dart'; import 'package:sefer_driver/controller/functions/crud.dart';
import 'package:sefer_driver/controller/functions/encrypt_decrypt.dart';
import 'package:sefer_driver/main.dart'; import 'package:sefer_driver/main.dart';
import 'package:sefer_driver/views/widgets/error_snakbar.dart'; import 'package:sefer_driver/views/widgets/error_snakbar.dart';
import 'package:sefer_driver/views/widgets/mydialoug.dart'; import 'package:sefer_driver/views/widgets/mydialoug.dart';
@@ -24,9 +22,10 @@ class RatingController extends GetxController {
void _redirectToAppStore() async { void _redirectToAppStore() async {
// URLs for App Store and Google Play Store // URLs for App Store and Google Play Store
const appStoreUrl = 'https://apps.apple.com/app/6502189302'; const appStoreUrl =
'https://apps.apple.com/st/app/intaleq-driver/id6482995159';
const playStoreUrl = const playStoreUrl =
'https://play.google.com/store/apps/details?id=com.sefer_driver'; 'https://play.google.com/store/apps/details?id=com.intaleq_driver';
final url = GetPlatform.isIOS ? appStoreUrl : playStoreUrl; final url = GetPlatform.isIOS ? appStoreUrl : playStoreUrl;
if (await launchUrl(Uri.parse(url))) { if (await launchUrl(Uri.parse(url))) {
@@ -59,13 +58,14 @@ class RatingController extends GetxController {
if (res != 'failure') { if (res != 'failure') {
try { try {
// Attempt to parse the response as JSON // Attempt to parse the response as JSON
final parsedResponse = jsonDecode(res); final parsedResponse = (res);
if (parsedResponse['status'] == 'success') { if (parsedResponse['status'] == 'success') {
// Display a success message // Display a success message
CRUD().post(link: AppLink.sendEmailRateingApp, payload: { CRUD().post(link: AppLink.sendEmailRateingApp, payload: {
"name": payload["name"], "name": payload["name"],
"email": payload["email"], "email": payload["email"],
"phone": payload["phone"],
"rating": rating.toString(), "rating": rating.toString(),
"comment": payload["comment"], "comment": payload["comment"],
}); });

View File

@@ -11,6 +11,8 @@ import 'package:sefer_driver/main.dart';
import 'package:sefer_driver/views/home/Captin/home_captain/home_captin.dart'; import 'package:sefer_driver/views/home/Captin/home_captain/home_captin.dart';
import 'package:sefer_driver/views/widgets/elevated_btn.dart'; import 'package:sefer_driver/views/widgets/elevated_btn.dart';
import '../firebase/notification_service.dart';
// import '../home/captin/home_captain_controller.dart'; // import '../home/captin/home_captain_controller.dart';
class RateController extends GetxController { class RateController extends GetxController {
@@ -67,12 +69,20 @@ class RateController extends GetxController {
'driverID': box.read(BoxName.driverID).toString(), 'driverID': box.read(BoxName.driverID).toString(),
}); });
Get.find<FirebaseMessagesController>().sendNotificationToDriverMAP( // Get.find<FirebaseMessagesController>().sendNotificationToDriverMAP(
'Wallet Added'.tr, // 'Wallet Added'.tr,
'Wallet Added${(remainingFee).toStringAsFixed(0)}'.tr, // 'Wallet Added${(remainingFee).toStringAsFixed(0)}'.tr,
Get.find<MapDriverController>().tokenPassenger, // Get.find<MapDriverController>().tokenPassenger,
[], // [],
'tone2.wav'); // 'tone2.wav');
NotificationService.sendNotification(
target: Get.find<MapDriverController>().tokenPassenger.toString(),
title: 'Wallet Added'.tr,
body: 'Wallet Added${(remainingFee).toStringAsFixed(0)}'.tr,
isTopic: false, // Important: this is a token
tone: 'tone2',
driverList: [],
);
walletChecked = 'true'; walletChecked = 'true';
// } // }

View File

@@ -14,6 +14,7 @@ import 'package:sefer_driver/views/widgets/mydialoug.dart';
import '../../../../../constant/colors.dart'; import '../../../../../constant/colors.dart';
import '../../../../../constant/links.dart'; import '../../../../../constant/links.dart';
import '../../../../../controller/firebase/firbase_messge.dart'; import '../../../../../controller/firebase/firbase_messge.dart';
import '../../../../../controller/firebase/notification_service.dart';
import '../../../../../controller/functions/crud.dart'; import '../../../../../controller/functions/crud.dart';
import '../../../../../controller/home/captin/order_request_controller.dart'; import '../../../../../controller/home/captin/order_request_controller.dart';
import '../../../../../controller/home/navigation/navigation_view.dart'; import '../../../../../controller/home/navigation/navigation_view.dart';
@@ -318,8 +319,16 @@ void _sendAcceptanceNotification(String? customerToken, rideId) {
// Safely check for customer token // Safely check for customer token
final String? token = customerToken; final String? token = customerToken;
if (token != null && token.isNotEmpty) { if (token != null && token.isNotEmpty) {
_firebaseMessagesController.sendNotificationToDriverMAP('Accepted Ride', // _firebaseMessagesController.sendNotificationToDriverMAP('Accepted Ride',
'your ride is applied'.tr, token, bodyToPassenger, 'start.wav'); // 'your ride is applied'.tr, token, bodyToPassenger, 'start.wav');
NotificationService.sendNotification(
target: token.toString(),
title: 'Accepted Ride',
body: 'your ride is Accepted'.tr,
isTopic: false, // Important: this is a token
tone: 'start',
driverList: [],
);
} else {} } else {}
} catch (e) {} } catch (e) {}
} }

View File

@@ -9,7 +9,9 @@ import 'package:sefer_driver/views/widgets/elevated_btn.dart';
import '../../../../constant/box_name.dart'; import '../../../../constant/box_name.dart';
import '../../../../constant/style.dart'; import '../../../../constant/style.dart';
import '../../../../controller/firebase/notification_service.dart';
import '../../../../main.dart'; import '../../../../main.dart';
import '../../../../print.dart';
// Changed: إعادة تصميم كاملة لتصبح شريط معلومات علوي مدمج // Changed: إعادة تصميم كاملة لتصبح شريط معلومات علوي مدمج
class PassengerInfoWindow extends StatelessWidget { class PassengerInfoWindow extends StatelessWidget {
@@ -154,12 +156,23 @@ class PassengerInfoWindow extends StatelessWidget {
if (await controller if (await controller
.calculateDistanceBetweenDriverAndPassengerLocation() < .calculateDistanceBetweenDriverAndPassengerLocation() <
140) { 140) {
fcm.sendNotificationToDriverMAP( // fcm.sendNotificationToDriverMAP(
'Hi ,I Arrive your site', // 'Hi ,I Arrive your site',
'I Arrive at your site'.tr, // 'I Arrive at your site'.tr,
controller.tokenPassenger, // controller.tokenPassenger,
[], // [],
'ding.wav', // 'ding.wav',
// );
Log.print(
'controller.tokenPassenger: ${controller.tokenPassenger}');
NotificationService.sendNotification(
target: controller.tokenPassenger.toString(),
title: 'Hi ,I Arrive your site'.tr,
body: 'I Arrive at your site'.tr,
isTopic: false, // Important: this is a token
tone: 'ding',
driverList: [],
); );
controller.startTimerToShowDriverWaitPassengerDuration(); controller.startTimerToShowDriverWaitPassengerDuration();
controller.isArrivedSend = false; controller.isArrivedSend = false;
@@ -239,13 +252,22 @@ class PassengerInfoWindow extends StatelessWidget {
kolor: AppColor.deepPurpleAccent, kolor: AppColor.deepPurpleAccent,
onPressed: () { onPressed: () {
MyDialog().getDialog('Are you sure to cancel?'.tr, '', () async { MyDialog().getDialog('Are you sure to cancel?'.tr, '', () async {
fcm.sendNotificationToDriverMAP( // fcm.sendNotificationToDriverMAP(
'Driver Cancelled Your Trip', // 'Driver Cancelled Your Trip',
'You will need to pay the cost to the driver, or it will be deducted from your next trip' // 'You will need to pay the cost to the driver, or it will be deducted from your next trip'
.tr, // .tr,
controller.tokenPassenger, // controller.tokenPassenger,
[], // [],
'cancel.wav'); // 'cancel.wav');
NotificationService.sendNotification(
target: controller.tokenPassenger.toString(),
title: 'Driver Cancelled Your Trip',
body:
'You will need to pay the cost to the driver, or it will be deducted from your next trip',
isTopic: false, // Important: this is a token
tone: 'cancel',
driverList: [],
);
box.write(BoxName.rideStatus, 'Cancel'); box.write(BoxName.rideStatus, 'Cancel');
await controller.addWaitingTimeCostFromPassengerToDriverWallet(); await controller.addWaitingTimeCostFromPassengerToDriverWallet();
controller.isdriverWaitTimeEnd = false; controller.isdriverWaitTimeEnd = false;

View File

@@ -201,6 +201,7 @@ import '../../../../constant/box_name.dart';
import '../../../../constant/colors.dart'; import '../../../../constant/colors.dart';
import '../../../../constant/style.dart'; import '../../../../constant/style.dart';
import '../../../../controller/firebase/firbase_messge.dart'; import '../../../../controller/firebase/firbase_messge.dart';
import '../../../../controller/firebase/notification_service.dart';
import '../../../../controller/functions/launch.dart'; import '../../../../controller/functions/launch.dart';
import '../../../../controller/home/captin/map_driver_controller.dart'; import '../../../../controller/home/captin/map_driver_controller.dart';
import '../../../../main.dart'; import '../../../../main.dart';
@@ -339,23 +340,39 @@ class SosConnect extends StatelessWidget {
_buildMessageTile( _buildMessageTile(
text: "Where are you, sir?".tr, text: "Where are you, sir?".tr,
onTap: () { onTap: () {
fcm.sendNotificationToDriverMAP( // fcm.sendNotificationToDriverMAP(
'message From Driver', // 'message From Driver',
"Where are you, sir?".tr, // "Where are you, sir?".tr,
controller.tokenPassenger, // controller.tokenPassenger,
[], // [],
'ding.wav'); // 'ding');
NotificationService.sendNotification(
target: controller.tokenPassenger.toString(),
title: 'message From Driver'.tr,
body: "Where are you, sir?".tr,
isTopic: false, // Important: this is a token
tone: 'ding',
driverList: [],
);
Get.back(); Get.back();
}), }),
_buildMessageTile( _buildMessageTile(
text: "I've been trying to reach you but your phone is off.".tr, text: "I've been trying to reach you but your phone is off.".tr,
onTap: () { onTap: () {
fcm.sendNotificationToDriverMAP( // fcm.sendNotificationToDriverMAP(
'message From Driver', // 'message From Driver',
"I've been trying to reach you but your phone is off.".tr, // "I've been trying to reach you but your phone is off.".tr,
controller.tokenPassenger, // controller.tokenPassenger,
[], // [],
'ding.wav'); // 'ding');
NotificationService.sendNotification(
target: controller.tokenPassenger.toString(),
title: 'message From Driver'.tr,
body: "I've been trying to reach you but your phone is off.".tr,
isTopic: false, // Important: this is a token
tone: 'ding',
driverList: [],
);
Get.back(); Get.back();
}), }),
const SizedBox(height: 16), const SizedBox(height: 16),
@@ -374,12 +391,20 @@ class SosConnect extends StatelessWidget {
), ),
IconButton( IconButton(
onPressed: () { onPressed: () {
fcm.sendNotificationToDriverMAP( // fcm.sendNotificationToDriverMAP(
'message From Driver', // 'message From Driver',
controller.messageToPassenger.text, // controller.messageToPassenger.text,
controller.tokenPassenger, // controller.tokenPassenger,
[], // [],
'ding.wav'); // 'ding');
NotificationService.sendNotification(
target: controller.tokenPassenger.toString(),
title: 'message From Driver'.tr,
body: 'change device'.tr,
isTopic: false, // Important: this is a token
tone: 'cancel',
driverList: [],
);
controller.messageToPassenger.clear(); controller.messageToPassenger.clear();
Get.back(); Get.back();
}, },

View File

@@ -8,6 +8,7 @@ import '../../../../constant/box_name.dart';
import '../../../../constant/links.dart'; import '../../../../constant/links.dart';
import '../../../../controller/firebase/firbase_messge.dart'; import '../../../../controller/firebase/firbase_messge.dart';
import '../../../../controller/firebase/local_notification.dart'; import '../../../../controller/firebase/local_notification.dart';
import '../../../../controller/firebase/notification_service.dart';
import '../../../../controller/functions/crud.dart'; import '../../../../controller/functions/crud.dart';
import '../../../../main.dart'; import '../../../../main.dart';
import '../../../../models/model/order_data.dart'; import '../../../../models/model/order_data.dart';
@@ -228,12 +229,20 @@ class _OrderOverlayState extends State<OrderOverlay>
? Get.find<FirebaseMessagesController>() ? Get.find<FirebaseMessagesController>()
: Get.put(FirebaseMessagesController()); : Get.put(FirebaseMessagesController());
fmc.sendNotificationToDriverMAP( // fmc.sendNotificationToDriverMAP(
"Accepted Ride", // "Accepted Ride",
'your ride is Accepted'.tr, // 'your ride is Accepted'.tr,
_getData(9).toString(), // _getData(9).toString(),
bodyToPassenger, // bodyToPassenger,
'start.wav', // 'start.wav',
// );
NotificationService.sendNotification(
target: _getData(9).toString(),
title: "Accepted Ride",
body: 'your ride is Accepted'.tr,
isTopic: false, // Important: this is a token
tone: 'start',
driverList: [],
); );
final payload = { final payload = {
// بيانات أساسية // بيانات أساسية

View File

@@ -13,10 +13,12 @@ import 'dart:math' as math;
import '../../../../constant/colors.dart'; import '../../../../constant/colors.dart';
import '../../../../constant/links.dart'; import '../../../../constant/links.dart';
import '../../../../constant/style.dart'; import '../../../../constant/style.dart';
import '../../../../controller/firebase/notification_service.dart';
import '../../../../controller/functions/crud.dart'; import '../../../../controller/functions/crud.dart';
import '../../../../controller/functions/encrypt_decrypt.dart'; import '../../../../controller/functions/encrypt_decrypt.dart';
import '../../../../controller/functions/launch.dart'; import '../../../../controller/functions/launch.dart';
import '../../../../controller/home/captin/order_request_controller.dart'; import '../../../../controller/home/captin/order_request_controller.dart';
import '../../../../print.dart';
import '../../../widgets/elevated_btn.dart'; import '../../../widgets/elevated_btn.dart';
class OrderRequestPage extends StatefulWidget { class OrderRequestPage extends StatefulWidget {
@@ -279,12 +281,20 @@ class _OrderRequestPageState extends State<OrderRequestPage> {
? Get.find<FirebaseMessagesController>() ? Get.find<FirebaseMessagesController>()
: Get.put(FirebaseMessagesController()); : Get.put(FirebaseMessagesController());
fmc.sendNotificationToDriverMAP( // fmc.sendNotificationToDriverMAP(
"Accepted Ride", // "Accepted Ride",
'your ride is Accepted'.tr, // 'your ride is Accepted'.tr,
controller.myList[9].toString(), // controller.myList[9].toString(),
bodyToPassenger, // bodyToPassenger,
'start.wav', // 'start',
// );
NotificationService.sendNotification(
target: controller.myList[9].toString(),
title: "Accepted Ride",
body: 'your ride is Accepted'.tr,
isTopic: false, // Important: this is a token
tone: 'start',
driverList: [],
); );
Get.back(); Get.back();
box.write(BoxName.rideArguments, { box.write(BoxName.rideArguments, {
@@ -338,6 +348,8 @@ class _OrderRequestPageState extends State<OrderRequestPage> {
}); });
Get.to(() => PassengerLocationMapPage(), Get.to(() => PassengerLocationMapPage(),
arguments: box.read(BoxName.rideArguments)); arguments: box.read(BoxName.rideArguments));
Log.print(
'box.read(BoxName.rideArguments): ${box.read(BoxName.rideArguments)}');
} }
}, },
), ),
@@ -372,13 +384,22 @@ class _OrderRequestPageState extends State<OrderRequestPage> {
box.read(BoxName.tokenDriver).toString(), box.read(BoxName.tokenDriver).toString(),
]; ];
FirebaseMessagesController() // FirebaseMessagesController()
.sendNotificationToPassengerToken( // .sendNotificationToPassengerToken(
'Order Under Review'.tr, // 'Order Under Review'.tr,
// '${box.read(BoxName.nameDriver)} ${'is reviewing your order. They may need more information or a higher price.'.tr}',
// controller.myList[9].toString(),
// bodyToPassenger,
// 'notification');
NotificationService.sendNotification(
target: controller.myList[9].toString(),
title: 'Order Under Review'.tr,
body:
'${box.read(BoxName.nameDriver)} ${'is reviewing your order. They may need more information or a higher price.'.tr}', '${box.read(BoxName.nameDriver)} ${'is reviewing your order. They may need more information or a higher price.'.tr}',
controller.myList[9].toString(), isTopic: false, // Important: this is a token
bodyToPassenger, tone: 'start',
'notification.wav'); driverList: [],
);
controller.refuseOrder( controller.refuseOrder(
EncryptionHelper.instance.encryptData( EncryptionHelper.instance.encryptData(

View File

@@ -11,6 +11,7 @@ import 'package:google_maps_flutter/google_maps_flutter.dart';
import '../../../../constant/colors.dart'; // Your AppColor import '../../../../constant/colors.dart'; // Your AppColor
import '../../../../constant/links.dart'; // Your AppLink import '../../../../constant/links.dart'; // Your AppLink
import '../../../../constant/style.dart'; // Your AppStyle import '../../../../constant/style.dart'; // Your AppStyle
import '../../../../controller/firebase/notification_service.dart';
import '../../../../controller/functions/crud.dart'; import '../../../../controller/functions/crud.dart';
import '../../../../controller/functions/launch.dart'; import '../../../../controller/functions/launch.dart';
import '../../../../controller/home/captin/order_request_controller.dart'; import '../../../../controller/home/captin/order_request_controller.dart';
@@ -385,16 +386,28 @@ class OrderSpeedRequest extends StatelessWidget {
box.read(BoxName.tokenDriver).toString(), box.read(BoxName.tokenDriver).toString(),
rideId.toString(), rideId.toString(),
]; ];
Get.put(FirebaseMessagesController()) // Get.put(FirebaseMessagesController())
.sendNotificationToDriverMAP( // .sendNotificationToDriverMAP(
'Accepted Ride', // 'Accepted Ride',
'your ride is applied'.tr, // 'your ride is applied'.tr,
controller.arguments?['DriverList'] // controller.arguments?['DriverList']
// ?[9]
// ?.toString() ??
// _getData(9), // Safer access
// bodyToPassenger,
// 'start.wav');
NotificationService.sendNotification(
target: controller.arguments?['DriverList']
?[9] ?[9]
?.toString() ?? ?.toString() ??
_getData(9), // Safer access _getData(9),
bodyToPassenger, title: 'Accepted Ride',
'start.wav'); body: 'your ride is applied'.tr,
isTopic:
false, // Important: this is a token
tone: 'start',
driverList: [],
);
// Using rideId (_getData(16)) for order_id consistently // Using rideId (_getData(16)) for order_id consistently
CRUD().postFromDialogue( CRUD().postFromDialogue(

View File

@@ -8,6 +8,7 @@ import '../../constant/colors.dart';
import '../../constant/links.dart'; import '../../constant/links.dart';
import '../../constant/style.dart'; import '../../constant/style.dart';
import '../../controller/firebase/firbase_messge.dart'; import '../../controller/firebase/firbase_messge.dart';
import '../../controller/firebase/notification_service.dart';
import '../../controller/functions/crud.dart'; import '../../controller/functions/crud.dart';
import '../../controller/home/captin/home_captain_controller.dart'; import '../../controller/home/captin/home_captain_controller.dart';
import '../../controller/notification/ride_available_controller.dart'; import '../../controller/notification/ride_available_controller.dart';
@@ -338,12 +339,20 @@ class RideAvailableCard extends StatelessWidget {
payload: {'id': rideInfo['id'], 'status': 'Applied'}); payload: {'id': rideInfo['id'], 'status': 'Applied'});
} }
FirebaseMessagesController().sendNotificationToPassengerToken( // FirebaseMessagesController().sendNotificationToPassengerToken(
"Accepted Ride".tr, // "Accepted Ride".tr,
'your ride is Accepted'.tr, // 'your ride is Accepted'.tr,
rideInfo['passengerToken'].toString(), // rideInfo['passengerToken'].toString(),
bodyToPassenger, // bodyToPassenger,
'start.wav'); // 'start.wav');
NotificationService.sendNotification(
target: rideInfo['passengerToken'].toString(),
title: 'Accepted Ride',
body: 'your ride is Accepted'.tr,
isTopic: false, // Important: this is a token
tone: 'start',
driverList: [],
);
Get.back(); Get.back();
Get.to(() => PassengerLocationMapPage(), arguments: { Get.to(() => PassengerLocationMapPage(), arguments: {
'passengerLocation': rideInfo['start_location'].toString(), 'passengerLocation': rideInfo['start_location'].toString(),