first commit

This commit is contained in:
Hamza-Ayed
2026-06-09 08:40:31 +03:00
commit d8901e1a87
3161 changed files with 536187 additions and 0 deletions

View File

@@ -0,0 +1,32 @@
import '../../constant/box_name.dart';
import '../../constant/links.dart';
import '../../main.dart';
import 'crud.dart';
addError1(String error, String details, String where) async {
try {
// Get user information for the error log
final userId = box.read(BoxName.driverID) ?? box.read(BoxName.passengerID);
final userType =
box.read(BoxName.driverID) != null ? 'Driver' : 'passenger';
final phone = box.read(BoxName.phone) ?? box.read(BoxName.phoneDriver);
// Send the error data to the server
// Note: This is a fire-and-forget call. We don't await it or handle its response
// to prevent an infinite loop if the addError endpoint itself is failing.
CRUD().post(
link: AppLink.addError,
payload: {
'error': error.toString(),
'userId': userId.toString(),
'userType': userType,
'phone': phone.toString(),
'device': where, // The location of the error
'details': details, // The detailed stack trace or context
},
);
} catch (e) {
// If logging the error itself fails, print to the console to avoid infinite loops.
print("Failed to log error to server: $e");
}
}

View File

@@ -0,0 +1,92 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:url_launcher/url_launcher.dart';
import 'dart:io';
import 'dart:convert';
import '../../constant/info.dart';
import '../../constant/links.dart';
import '../../constant/colors.dart';
import '../../print.dart';
import 'crud.dart';
class AppUpdateController extends GetxController {
@override
void onInit() {
super.onInit();
// الفحص التلقائي عند التشغيل لتحديثات المتجر
checkSmartUpdate();
}
// ======================================================================
// الدالة الذكية المدمجة (الآن تفحص المتجر فقط لأن Shorebird يعمل تلقائياً بالخلفية)
// ======================================================================
Future<void> checkSmartUpdate() async {
Log.print("🔄 بدء فحص تحديثات المتجر...");
// 1. فحص تحديث المتجر (Native Update)
await _checkStoreUpdate();
}
// ======================================================================
// 1. تحديث المتجر الأساسي
// ======================================================================
Future<bool> _checkStoreUpdate() async {
try {
final packageInfo = await PackageInfo.fromPlatform();
final currentBuildNumber = packageInfo.buildNumber;
// استخدام نفس الـ Endpoint والمعايير الموجودة في التطبيق
var response = await CRUD().get(link: AppLink.packageInfo, payload: {
"platform": Platform.isAndroid ? 'android' : 'ios',
"appName": AppInformation.appVersion,
});
if (response != 'failure') {
var decoded = jsonDecode(response);
if (decoded['status'] == 'success' && decoded['message'] != null && decoded['message'].isNotEmpty) {
String latestBuildNumber = decoded['message'][0]['version'].toString();
// مقارنة الـ Build Number
if (latestBuildNumber != currentBuildNumber) {
_showStoreUpdateDialog();
return true;
}
}
}
} catch (e) {
Log.print("❌ Store update check error: $e");
}
return false;
}
// ======================================================================
// دوال مساعدة
// ======================================================================
void _showStoreUpdateDialog() {
final String storeUrl = Platform.isAndroid
? 'https://play.google.com/store/apps/details?id=com.intaleq_driver'
: 'https://apps.apple.com/jo/app/intaleq-driver/id6482995159';
Get.defaultDialog(
title: "تحديث جديد متوفر".tr,
middleText: "يوجد إصدار جديد من التطبيق في المتجر، يرجى التحديث للحصول على الميزات الجديدة.".tr,
barrierDismissible: false,
onWillPop: () async => false,
confirm: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: AppColor.primaryColor,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10))
),
onPressed: () async {
if (await canLaunchUrl(Uri.parse(storeUrl))) {
await launchUrl(Uri.parse(storeUrl), mode: LaunchMode.externalApplication);
}
},
child: Text("تحديث الآن".tr, style: const TextStyle(color: Colors.white)),
),
);
}
}

View File

@@ -0,0 +1,44 @@
import 'package:flutter/material.dart';
import 'package:just_audio/just_audio.dart';
import 'package:get/get.dart';
class AudioController extends GetxController {
final AudioPlayer _audioPlayer = AudioPlayer();
Future<void> playAudio() async {
// Check if the platform is Android
if (Theme.of(Get.context!).platform == TargetPlatform.android) {
try {
// Load the audio file from the raw resources
await _audioPlayer.setAsset(
'assets/order1.wav'); // Adjust the path based on your project structure
_audioPlayer.play();
} catch (e) {
// Handle errors, such as file not found
print('Error playing audio: $e');
}
}
}
Future<void> playAudio1(String path) async {
// Check if the platform is Android
// if (Theme.of(Get.context!).platform == TargetPlatform.android) {
try {
// Load the audio file from the raw resources
await _audioPlayer
.setAsset(path); // Adjust the path based on your project structure
_audioPlayer.play();
} catch (e) {
// Handle errors, such as file not found
print('Error playing audio: $e');
}
// }
}
@override
void onClose() {
// Release resources when done
_audioPlayer.dispose();
super.onClose();
}
}

View File

@@ -0,0 +1,80 @@
import 'dart:io';
import 'package:path_provider/path_provider.dart';
import 'package:get/get.dart';
import 'package:just_audio/just_audio.dart';
import 'package:record/record.dart';
class AudioRecorderController extends GetxController {
AudioPlayer audioPlayer = AudioPlayer();
AudioRecorder recorder = AudioRecorder();
bool isRecording = false;
bool isPlaying = false;
bool isPaused = false;
String filePath = '';
String? selectedFilePath;
double currentPosition = 0;
double totalDuration = 0;
// Start recording
Future<void> startRecording({String? rideId}) async {
final bool isPermissionGranted = await recorder.hasPermission();
if (!isPermissionGranted) {
return;
}
final directory = await getApplicationDocumentsDirectory();
final String dateStr =
'${DateTime.now().year}-${DateTime.now().month.toString().padLeft(2, '0')}-${DateTime.now().day.toString().padLeft(2, '0')}';
// Generate a unique file name
String fileName = (rideId != null && rideId.isNotEmpty && rideId != 'yet' && rideId != 'null')
? '${dateStr}_$rideId.m4a'
: '$dateStr.m4a';
filePath = '${directory.path}/$fileName';
const config = RecordConfig(
encoder: AudioEncoder.aacLc,
sampleRate: 44100,
bitRate: 128000,
);
await recorder.start(config, path: filePath);
isRecording = true;
update();
}
// Stop recording
Future<void> stopRecording() async {
await recorder.stop();
isRecording = false;
isPaused = false;
update();
}
// Get a list of recorded files
Future<List<String>> getRecordedFiles() async {
final directory = await getApplicationDocumentsDirectory();
final files = await directory.list().toList();
return files
.map((file) => file.path)
.where((path) => path.endsWith('.m4a'))
.toList();
}
// Delete a specific recorded file
Future<void> deleteRecordedFile(String filePath) async {
final file = File(filePath);
if (await file.exists()) {
await file.delete();
update();
}
}
@override
void onClose() {
audioPlayer.dispose();
recorder.dispose();
super.onClose();
}
}

View File

@@ -0,0 +1,225 @@
import 'dart:async';
import 'dart:convert';
import 'dart:ui';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_background_service/flutter_background_service.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:flutter_overlay_window/flutter_overlay_window.dart';
import 'package:socket_io_client/socket_io_client.dart' as IO;
import 'package:flutter_overlay_window/flutter_overlay_window.dart' as Overlay;
import 'package:get_storage/get_storage.dart';
import 'package:geolocator/geolocator.dart' as geo;
import '../../constant/box_name.dart';
import '../firebase/local_notification.dart';
const String notificationChannelId = 'driver_service_channel';
const int notificationId = 888;
const String notificationIcon = '@mipmap/launcher_icon';
@pragma('vm:entry-point')
Future<bool> onStart(ServiceInstance service) async {
DartPluginRegistrant.ensureInitialized();
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
await GetStorage.init();
final box = GetStorage();
IO.Socket? socket;
String driverId = box.read(BoxName.driverID) ?? '';
String token = box.read(BoxName.tokenDriver) ?? '';
if (driverId.isNotEmpty) {
socket = IO.io(
'https://location.intaleq.xyz',
IO.OptionBuilder()
.setTransports(['websocket'])
.disableAutoConnect()
.setQuery({
'driver_id': driverId,
'token': token,
'EIO': '3', // توافقية مع Workerman
})
.setReconnectionAttempts(double.infinity)
.build());
socket.connect();
socket.onConnect((_) {
print("✅ Background Service: Socket Connected! ID: ${socket?.id}");
if (service is AndroidServiceInstance) {
flutterLocalNotificationsPlugin.show(
id: notificationId,
title: 'أنت متصل الآن',
body: 'بانتظار الطلبات...',
notificationDetails: const NotificationDetails(
android: AndroidNotificationDetails(
notificationChannelId,
'خدمة السائق',
icon: notificationIcon,
ongoing: true,
importance: Importance.low,
priority: Priority.low,
),
),
);
}
});
socket.on('new_ride_request', (data) async {
print("🔔 Background Service: Received new_ride_request");
// 🔥 قراءة حالة التطبيق مباشرة قبل العرض
await GetStorage.init(); // تأكد من تحديث البيانات
final box = GetStorage();
bool isAppInForeground = box.read(BoxName.isAppInForeground) ?? false;
// 🔥 Check إضافي: هل الـ Overlay مفتوح بالفعل؟ (للأندرويد فقط)
bool overlayActive = false;
if (Platform.isAndroid) {
overlayActive = await Overlay.FlutterOverlayWindow.isActive();
}
if (isAppInForeground || overlayActive) {
print("🛑 App is FOREGROUND or Overlay already shown. Skipping.");
return;
}
// عرض الـ Overlay (للأندرويد فقط)
if (Platform.isAndroid) {
print("🚀 App is BACKGROUND. Showing Overlay...");
try {
await Overlay.FlutterOverlayWindow.showOverlay(
enableDrag: true,
overlayTitle: "طلب جديد",
overlayContent: "لديك طلب جديد وصل للتو!",
flag: OverlayFlag.focusPointer,
positionGravity: PositionGravity.auto,
height: WindowSize.matchParent,
width: WindowSize.matchParent,
startPosition: const OverlayPosition(0, -30),
);
await Overlay.FlutterOverlayWindow.shareData(data);
} catch (e) {
print("Overlay Error: $e");
}
} else if (Platform.isIOS) {
// على iOS، نظهر إشعاراً عادياً لأن الـ Overlay غير موجود
flutterLocalNotificationsPlugin.show(
id: 1002,
title: "طلب رحلة جديد 🚖",
body: "لديك طلب رحلة جديد، افتح التطبيق للموافقة عليه",
notificationDetails: const NotificationDetails(
iOS: DarwinNotificationDetails(
presentAlert: true,
presentBadge: true,
presentSound: true,
),
),
payload: jsonEncode(data));
}
});
}
service.on('stopService').listen((event) {
socket?.clearListeners();
socket?.dispose();
service.stopSelf();
});
// 🔥 Location management in background isolate (Using Geolocator)
geo.Position? latestPos;
// Listen to location changes continuously in the background
geo.Geolocator.getPositionStream(
locationSettings: geo.AndroidSettings(
accuracy: geo.LocationAccuracy.high,
distanceFilter: 10,
intervalDuration: const Duration(seconds: 10),
),
).listen((pos) {
latestPos = pos;
});
// 🔥 MERCY HEARTBEAT: Send location every 2 minutes to keep driver active in 'raids'
Timer.periodic(const Duration(minutes: 2), (timer) async {
if (socket != null && socket.connected && latestPos != null) {
try {
socket.emit('update_location', {
'driver_id': driverId,
'lat': latestPos!.latitude,
'lng': latestPos!.longitude,
'heading': latestPos!.heading,
'speed': latestPos!.speed * 3.6,
'status': box.read(BoxName.statusDriverLocation) ?? 'on',
'source': 'background_heartbeat'
});
print(
"💓 Background Mercy Heartbeat Sent: ${latestPos!.latitude}, ${latestPos!.longitude}");
} catch (e) {
print("❌ Background Heartbeat Error: $e");
}
}
});
Timer.periodic(const Duration(seconds: 30), (timer) async {
if (service is AndroidServiceInstance) {
if (await service.isForegroundService()) {
flutterLocalNotificationsPlugin.show(
id: notificationId,
title: 'خدمة السائق نشطة',
body: 'بانتظار الطلبات...',
notificationDetails: const NotificationDetails(
android: AndroidNotificationDetails(
notificationChannelId,
'خدمة السائق',
icon: notificationIcon,
ongoing: true,
importance: Importance.low,
priority: Priority.low,
),
),
);
}
}
});
return true;
}
class BackgroundServiceHelper {
static Future<void> initialize() async {
final service = FlutterBackgroundService();
await service.configure(
androidConfiguration: AndroidConfiguration(
onStart: onStart,
autoStart: false,
isForegroundMode: true,
notificationChannelId: notificationChannelId,
initialNotificationTitle: 'تطبيق السائق',
initialNotificationContent: 'تجهيز الخدمة...',
foregroundServiceNotificationId: notificationId,
),
iosConfiguration: IosConfiguration(
autoStart: false,
onForeground: onStart,
onBackground: onStart,
),
);
}
static Future<void> startService() async {
final service = FlutterBackgroundService();
if (!await service.isRunning()) {
await service.startService();
}
}
static Future<void> stopService() async {
final service = FlutterBackgroundService();
service.invoke("stopService");
}
}

View File

@@ -0,0 +1,39 @@
import 'package:battery_plus/battery_plus.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class BatteryNotifier {
static final Battery _battery = Battery();
static int? _lastNotifiedLevel;
static Future<void> checkBatteryAndNotify() async {
try {
final int batteryLevel = await _battery.batteryLevel;
// ✅ لا تكرر الإشعار إذا الفرق قليل
if (_lastNotifiedLevel != null &&
(batteryLevel >= _lastNotifiedLevel! - 2)) return;
if (batteryLevel <= 30) {
Color backgroundColor = Colors.yellow;
if (batteryLevel <= 20) {
backgroundColor = Colors.red;
}
Get.snackbar(
"⚠️ تنبيه البطارية", // العنوان
"مستوى البطارية: $batteryLevel٪", // النص
snackPosition: SnackPosition.TOP,
backgroundColor: backgroundColor,
colorText: Colors.white,
duration: const Duration(seconds: 10), // مدة الظهور
margin: const EdgeInsets.all(10),
);
_lastNotifiedLevel = batteryLevel;
}
} catch (e) {
print('Battery check error: $e');
}
}
}

View File

@@ -0,0 +1,129 @@
// import 'package:SEFER/constant/api_key.dart';
// import 'package:SEFER/controller/functions/crud.dart';
// // import 'package:agora_rtc_engine/agora_rtc_engine.dart';
// import 'package:get/get.dart';
// import 'package:permission_handler/permission_handler.dart';
// import '../../constant/box_name.dart';
// import '../firebase/firbase_messge.dart';
// import '../home/captin/map_driver_controller.dart';
// import '../../main.dart';
// class CallController extends GetxController {
// String channelName = ''; // Get.find<MapDriverController>().rideId;
// String token = '';
// // int uid = int.parse(box.read(BoxName.phoneDriver)); // uid of the local user
// int uid = 0;
// int? remoteUid; // uid of the remote user
// bool _isJoined = false; // Indicates if the local user has joined the channel
// String status = '';
// // late RtcEngine agoraEngine; // Agora engine instance
// @override
// void onInit() {
// super.onInit();
// channelName = Get.find<MapDriverController>().rideId; // 'sefer300'; //
// remoteUid = int.parse(Get.find<MapDriverController>().passengerPhone);
// uid = int.parse(box.read(BoxName.phoneDriver));
// initAgoraFull();
// }
// initAgoraFull() async {
// await fetchToken();
// // Set up an instance of Agora engine
// setupVoiceSDKEngine();
// // join();
// FirebaseMessagesController().sendNotificationToPassengerTokenCALL(
// 'Call Income',
// '${'You have call from driver'.tr} ${box.read(BoxName.nameDriver)}',
// Get.find<MapDriverController>().tokenPassenger,
// [
// token,
// channelName,
// uid.toString(),
// remoteUid.toString(),
// ],
// );
// join();
// }
// @override
// void onClose() {
// // agoraEngine.leaveChannel();
// super.onClose();
// }
// // Future<void> setupVoiceSDKEngine() async {
// // // retrieve or request microphone permission
// // await [Permission.microphone].request();
// // //create an instance of the Agora engine
// // agoraEngine = createAgoraRtcEngine();
// // await agoraEngine.initialize(RtcEngineContext(appId: AK.agoraAppId));
// // // Register the event handler
// // agoraEngine.registerEventHandler(
// // RtcEngineEventHandler(
// // onJoinChannelSuccess: (RtcConnection connection, int elapsed) {
// // // Get.snackbar(
// // // "Local user uid:${connection.localUid} joined the channel", '');
// // status = 'joined'.tr;
// // _isJoined = true;
// // update();
// // },
// // onUserJoined: (RtcConnection connection, int remoteUid, int elapsed) {
// // // Get.snackbar("Remote user uid:$remoteUid joined the channel", '');
// // status = '${Get.find<MapDriverController>().passengerName} '
// // 'joined'
// // .tr;
// // remoteUid = remoteUid;
// // update();
// // },
// // onUserOffline: (RtcConnection connection, int? remoteUid,
// // UserOfflineReasonType reason) {
// // // Get.snackbar("Remote user uid:$remoteUid left the channel", '');
// // status = 'Call Left'.tr;
// // remoteUid = null;
// // update();
// // },
// // ),
// // );
// // }
// // void join() async {
// // // Set channel options including the client role and channel profile
// // ChannelMediaOptions options = const ChannelMediaOptions(
// // clientRoleType: ClientRoleType.clientRoleBroadcaster,
// // channelProfile: ChannelProfileType.channelProfileCommunication,
// // );
// // await agoraEngine.joinChannel(
// // token: token,
// // channelId: channelName,
// // options: options,
// // uid: uid,
// // );
// // }
// // void leave() {
// // _isJoined = false;
// // remoteUid = null;
// // update();
// // agoraEngine.leaveChannel();
// // }
// // // Clean up the resources when you leave
// // @override
// // void dispose() async {
// // await agoraEngine.leaveChannel();
// // super.dispose();
// // }
// fetchToken() async {
// var res = await CRUD()
// .getAgoraToken(channelName: channelName, uid: uid.toString());
// token = res;
// update();
// }
// }

View File

@@ -0,0 +1,238 @@
/*
import 'dart:convert';
import 'dart:io';
import 'package:camera/camera.dart';
import 'package:get/get.dart';
// import 'package:google_mlkit_text_recognition/google_mlkit_text_recognition.dart';
import 'package:siro_driver/constant/box_name.dart';
import 'package:siro_driver/constant/links.dart';
import 'package:siro_driver/views/widgets/elevated_btn.dart';
import 'package:path_provider/path_provider.dart' as path_provider;
import 'package:path/path.dart' as path;
import 'package:http/http.dart' as http;
import '../../main.dart';
class CameraClassController extends GetxController {
late CameraController cameraController;
late List<CameraDescription> cameras;
bool isCameraInitialized = false;
// final TextRecognizer _textRecognizer = TextRecognizer();
String? scannedText;
bool isloading = false;
@override
void onInit() {
super.onInit();
initializeCamera();
}
Future<void> initializeCamera() async {
try {
cameras = await availableCameras();
//update();
cameraController = CameraController(
cameras[0],
ResolutionPreset.medium,
enableAudio: false,
);
await cameraController.initialize();
isCameraInitialized = true;
update();
} catch (e) {
if (e is CameraException) {
switch (e.code) {
case 'CameraAccessDenied':
Get.defaultDialog(
title: 'Camera Access Denied.'.tr,
middleText: '',
confirm:
MyElevatedButton(title: 'Open Settings'.tr, onPressed: () {}),
);
break;
default:
// Handle other errors here.
break;
}
}
}
}
var imgUrl = '';
Future extractCardId() async {
// Construct the path for the image file
final directory = await path_provider.getTemporaryDirectory();
final imagePath =
path.join(directory.path, '${box.read(BoxName.driverID)}.png');
// Capture the image and save it to the specified path
final XFile capturedImage = await cameraController.takePicture();
// Move the captured image to the desired path
await capturedImage.saveTo(imagePath);
await uploadImage(File(capturedImage.path));
extractByAPI('${AppLink.server}/card_image/' + box.read(BoxName.driverID));
}
Future extractByAPI(String imgUrl) async {
var headers = {'apikey': 'K89368168788957'};
var request = http.MultipartRequest(
'POST', Uri.parse('https://api.ocr.space/parse/image'));
request.fields.addAll({
'language': 'ara',
'isOverlayRequired': 'false',
'url': imgUrl,
'iscreatesearchablepdf': 'false',
'issearchablepdfhidetextlayer': 'false'
});
request.headers.addAll(headers);
http.StreamedResponse response = await request.send();
if (response.statusCode == 200) {
} else {}
}
Future<String> uploadImage(File imageFile) async {
String? basicAuthCredentials =
await storage.read(key: BoxName.basicAuthCredentials);
var request = http.MultipartRequest(
'POST',
Uri.parse(AppLink.uploadImage),
);
// Attach the image file to the request
request.files.add(
await http.MultipartFile.fromPath('image', imageFile.path),
); // Add the headers to the request
request.headers.addAll({
"Content-Type": "application/x-www-form-urlencoded",
'Authorization':
'Basic ${base64Encode(utf8.encode(basicAuthCredentials.toString()))}',
});
// Add the driverID to the request
request.fields['driverID'] = box.read(BoxName.driverID);
// Send the request
var response = await request.send();
// Read the response
var responseData = await response.stream.toBytes();
var responseString = String.fromCharCodes(responseData);
scannedText = responseString;
update();
// Return the link received from the server
return responseString;
}
// Future<void> takePictureAndMLGoogleScan() async {
// try {
// // Construct the path for the image file
// final directory = await path_provider.getTemporaryDirectory();
// final imagePath =
// path.join(directory.path, '${box.read(BoxName.driverID)}.png');
// // Capture the image and save it to the specified path
// final XFile capturedImage = await cameraController.takePicture();
// // Move the captured image to the desired path
// await capturedImage.saveTo(imagePath);
// // Recognize the text in the image
// final InputImage inputImage =
// InputImage.fromFile(File(capturedImage.path));
// final RecognizedText recognizedText =
// await _textRecognizer.processImage(inputImage);
// scannedText = recognizedText.text;
// // Extract the scanned text line by line
// final List<Map<String, dynamic>> lines = [];
// for (var i = 0; i < recognizedText.blocks.length; i++) {
// lines.add({
// 'line_number': i,
// 'text': recognizedText.blocks[i].text,
// });
// }
// // Convert the list of lines to a JSON string
// final String jsonOutput = jsonEncode(lines);
// update();
// // Print the JSON output
// // Get.back();
// } catch (e) {}
// }
String getTextAsJSON(String text) {
final lines = text.split('\n');
final jsonList = lines.map((line) {
return {
'line_text': line,
'num_words': line.trim().split(' ').length,
};
}).toList();
final json = {
'lines': jsonList,
'num_lines': lines.length,
};
return jsonEncode(json);
}
List<String> getTextBlocks(String text) {
return text.split('\n');
}
// Future<void> takePictureAndTesseractScan() async {
// try {
// // Construct the path for the image file
// final directory = await path_provider.getTemporaryDirectory();
// final imagePath =
// path.join(directory.path, '${box.read(BoxName.driverID)}.png');
// // Capture the image and save it to the specified path
// final XFile capturedImage = await cameraController.takePicture();
// // Move the captured image to the desired path
// await capturedImage.saveTo(imagePath);
// // Recognize the text in the image
// final languages = [
// 'eng',
// 'ara'
// ]; // Specify the languages you want to use for text extraction
// final text = await FlutterTesseractOcr.extractText(imagePath,
// language: languages.join('+'), // Combine multiple languages with '+'
// args: {
// "psm": "4",
// "preserve_interword_spaces": "1",
// // "rectangle": const Rect.fromLTWH(100, 100, 200, 200),
// } // Additional options if needed
// );
// isloading = false;
// final jsonText = getTextAsJSON(text);
// final textBlocks = getTextBlocks(text);
// update();
// scannedText =
// textBlocks.toString(); // Convert the extracted text to JSON.
// // Print the JSON to the console.
// update();
// } catch (e) {
// scannedText = '';
// }
// }
@override
void onClose() {
cameraController.dispose();
super.onClose();
}
}
*/

View File

@@ -0,0 +1,736 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:siro_driver/controller/functions/encrypt_decrypt.dart';
import 'package:siro_driver/controller/functions/network/net_guard.dart';
import 'package:siro_driver/constant/box_name.dart';
import 'package:siro_driver/constant/links.dart';
import 'package:siro_driver/controller/auth/captin/login_captin_controller.dart';
import 'package:siro_driver/main.dart';
import 'package:get/get.dart';
import 'package:http/http.dart' as http;
import 'package:siro_driver/env/env.dart';
import 'package:siro_driver/print.dart';
import '../../constant/api_key.dart';
import '../../views/widgets/error_snakbar.dart';
import 'gemeni.dart';
import 'upload_image.dart';
class CRUD {
final NetGuard _netGuard = NetGuard();
static bool _isRefreshingJWT = false;
static String _lastErrorSignature = '';
static DateTime _lastErrorTimestamp = DateTime(2000);
static const Duration _errorLogDebounceDuration = Duration(minutes: 1);
// ── فحص صلاحية JWT بدون مكتبات خارجية ──────────────────────
static bool _isJwtValid(String? token) {
if (token == null || token.isEmpty) return false;
try {
final parts = token.split('.');
if (parts.length != 3) return false;
// فك تشفير الـ payload (الجزء الثاني)
String payload = parts[1];
// إضافة padding للـ base64
switch (payload.length % 4) {
case 2: payload += '=='; break;
case 3: payload += '='; break;
}
final decoded = jsonDecode(utf8.decode(base64Url.decode(payload)));
final exp = decoded['exp'];
if (exp == null) return false;
// نعتبر التوكن منتهي قبل 30 ثانية من انتهاء الصلاحية (buffer)
return DateTime.now().millisecondsSinceEpoch < (exp * 1000 - 30000);
} catch (_) {
return false;
}
}
static Future<void> addError(
String error, String details, String where) async {
try {
final currentErrorSignature = '$where-$error';
final now = DateTime.now();
if (currentErrorSignature == _lastErrorSignature &&
now.difference(_lastErrorTimestamp) < _errorLogDebounceDuration) {
return;
}
_lastErrorSignature = currentErrorSignature;
_lastErrorTimestamp = now;
final userId =
box.read(BoxName.driverID) ?? box.read(BoxName.passengerID);
final userType =
box.read(BoxName.driverID) != null ? 'Driver' : 'Passenger';
final phone = box.read(BoxName.phone) ?? box.read(BoxName.phoneDriver);
CRUD().post(
link: AppLink.addError,
payload: {
'error': error.toString(),
'userId': userId.toString(),
'userType': userType,
'phone': phone.toString(),
'device': where,
'details': details,
},
);
} catch (e) {}
}
// ─────────────────────────────────────────────────────────────
// دالة مساعدة: يجيب البصمة المشفرة من GetStorage
// نفس القيمة المرسلة عند login وعُملها hash في JWT
// السيرفر يتحقق: sha256(X-Device-FP + FP_PEPPER) == JWT.fingerPrint
// ─────────────────────────────────────────────────────────────
String _getFpHeader() {
return box.read(BoxName.deviceFingerprint)?.toString() ?? '';
}
// ═══════════════════════════════════════════════════════════════
// _makeRequest — دالة مركزية لكل الطلبات
// ───────────────────────────────────────────────────────────────
// Retry logic للشبكات الضعيفة (سوريا):
// • 3 محاولات لأخطاء الشبكة (SocketException / TimeoutException)
// • انتظار 1 ثانية بين المحاولات لأخطاء SocketException
// • بدون انتظار لأخطاء Timeout (نعيد فوراً)
// ═══════════════════════════════════════════════════════════════
Future<dynamic> _makeRequest({
required String link,
Map<String, dynamic>? payload,
required Map<String, String> headers,
}) async {
// timeouts مرتفعة مناسبة للإنترنت الضعيف في سوريا
const totalTimeout = Duration(seconds: 60);
Future<http.Response> doPost() {
final url = Uri.parse(link);
return http
.post(url, body: payload, headers: headers)
.timeout(totalTimeout);
}
http.Response? response;
int attempts = 0;
final requestId = DateTime.now().millisecondsSinceEpoch.toString().substring(7);
Log.print('🚀 [REQ-$requestId] $link');
if (payload != null) Log.print('📦 [PAYLOAD-$requestId] $payload');
while (attempts < 3) {
try {
attempts++;
response = await doPost();
break; // نجح الاتصال — نخرج
} on SocketException catch (_) {
Log.print('⚠️ SocketException attempt $attempts$link');
if (attempts >= 3) {
_netGuard.notifyOnce((title, msg) => mySnackeBarError(msg));
return 'no_internet';
}
// انتظار قبل إعادة المحاولة — مهم للشبكات المتقطعة
await Future.delayed(const Duration(seconds: 1));
} on TimeoutException catch (_) {
Log.print('⚠️ TimeoutException attempt $attempts$link');
if (attempts >= 3) return 'failure';
// لا انتظار — نعيد فوراً
} catch (e) {
// errno = 9 (Bad file descriptor) — إعادة المحاولة
if (e.toString().contains('errno = 9') && attempts < 3) {
await Future.delayed(const Duration(milliseconds: 500));
continue;
}
addError(
'HTTP Exception: $e', 'Try: $attempts', 'CRUD._makeRequest $link');
return 'failure';
}
}
// لو كل المحاولات فشلت بدون response
if (response == null) return 'failure';
final sc = response.statusCode;
final body = response.body;
Log.print('📥 [RES-$requestId] [$sc] $link');
Log.print('📄 [BODY-$requestId] $body');
// 2xx
if (sc >= 200 && sc < 300) {
try {
return jsonDecode(body);
} catch (e, st) {
addError(
'JSON Decode Error', 'Body: $body\n$st', 'CRUD._makeRequest $link');
return 'failure';
}
}
// 401 → تجديد التوكن (مع حماية من الحلقة اللانهائية)
if (sc == 401) {
// تخطي تجديد التوكن لـ endpoints غير حرجة (مثل تسجيل الأخطاء)
final isNonCritical = link.contains('errorApp.php');
if (!_isRefreshingJWT && !isNonCritical) {
_isRefreshingJWT = true;
try {
await Get.put(LoginDriverController()).getJWT();
} finally {
_isRefreshingJWT = false;
}
}
return 'token_expired';
}
// 5xx
if (sc >= 500) {
addError('Server 5xx', 'SC: $sc\nBody: $body', 'CRUD._makeRequest $link');
return 'failure';
}
return 'failure';
}
// ═══════════════════════════════════════════════════════════════
// post — طلب POST للسائق
// التغيير: إضافة X-Device-FP header
// ═══════════════════════════════════════════════════════════════
Future<dynamic> post({
required String link,
Map<String, dynamic>? payload,
}) async {
String token = r(box.read(BoxName.jwt)).toString().split(Env.addd)[0];
// فحص صلاحية التوكن قبل الإرسال — تجنب طلب مضمون الرفض
if (!_isJwtValid(token) && !_isRefreshingJWT) {
_isRefreshingJWT = true;
try {
await Get.put(LoginDriverController()).getJWT();
token = r(box.read(BoxName.jwt)).toString().split(Env.addd)[0];
} finally {
_isRefreshingJWT = false;
}
}
final headers = {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Bearer $token',
'X-Device-FP': _getFpHeader(), // ← إثبات الجهاز
};
return await _makeRequest(link: link, payload: payload, headers: headers);
}
// ═══════════════════════════════════════════════════════════════
// get — طلب GET للسائق (يستخدم POST method)
// التغيير: إضافة X-Device-FP header + timeout مناسب لسوريا
// ═══════════════════════════════════════════════════════════════
Future<dynamic> get({
required String link,
Map<String, dynamic>? payload,
}) async {
try {
// فحص صلاحية التوكن قبل الإرسال
String token = r(box.read(BoxName.jwt)).toString().split(Env.addd)[0];
if (!_isJwtValid(token) && !_isRefreshingJWT) {
_isRefreshingJWT = true;
try {
await Get.put(LoginDriverController()).getJWT();
token = r(box.read(BoxName.jwt)).toString().split(Env.addd)[0];
} finally {
_isRefreshingJWT = false;
}
}
var url = Uri.parse(link);
var response = await http.post(
url,
body: payload,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Bearer $token',
'X-Device-FP': _getFpHeader(),
},
).timeout(const Duration(seconds: 60));
Log.print('get [$link]: ${response.statusCode}');
Log.print('get body: ${response.body}');
if (response.statusCode == 200) {
var jsonData = jsonDecode(response.body);
if (jsonData['status'] == 'success') return response.body;
return jsonData['status'];
} else if (response.statusCode == 401) {
if (!_isRefreshingJWT) {
_isRefreshingJWT = true;
try {
await Get.put(LoginDriverController()).getJWT();
} finally {
_isRefreshingJWT = false;
}
}
return 'token_expired';
} else {
addError('Non-200: ${response.statusCode}', 'crud().get - Other',
url.toString());
return 'failure';
}
} on TimeoutException {
return 'failure';
} on SocketException {
return 'no_internet';
} catch (e) {
addError('GET Exception: $e', '', link);
return 'failure';
}
}
// ═══════════════════════════════════════════════════════════════
// postWallet — طلب POST للمحفظة
// التغيير: إضافة X-Device-FP header
// 3 headers: JWT + HMAC + FP
// ═══════════════════════════════════════════════════════════════
Future<dynamic> postWallet({
required String link,
Map<String, dynamic>? payload,
}) async {
var jwt = await LoginDriverController().getJwtWallet();
final hmac = box.read(BoxName.hmac);
final headers = {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Bearer $jwt',
'X-HMAC-Auth': hmac.toString(),
'X-Device-FP': _getFpHeader(), // ← إثبات الجهاز
};
return await _makeRequest(link: link, payload: payload, headers: headers);
}
// ═══════════════════════════════════════════════════════════════
// getWallet — طلب GET للمحفظة (يستخدم POST method)
// التغيير: إضافة X-Device-FP header
// ═══════════════════════════════════════════════════════════════
Future<dynamic> getWallet({
required String link,
Map<String, dynamic>? payload,
}) async {
var s = await LoginDriverController().getJwtWallet();
final hmac = box.read(BoxName.hmac);
var url = Uri.parse(link);
var response = await http.post(
url,
body: payload,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Bearer $s',
'X-HMAC-Auth': hmac.toString(),
'X-Device-FP': _getFpHeader(), // ← إثبات الجهاز
},
).timeout(const Duration(seconds: 60));
if (response.statusCode == 200) {
var jsonData = jsonDecode(response.body);
if (jsonData['status'] == 'success') return response.body;
return jsonData['status'];
} else if (response.statusCode == 401) {
var jsonData = jsonDecode(response.body);
if (jsonData['error'] == 'Token expired') {
await Get.put(LoginDriverController()).getJwtWallet();
return 'token_expired';
}
addError('Unauthorized: ${jsonData['error']}', 'crud().getWallet - 401',
url.toString());
return 'failure';
} else {
addError('Non-200: ${response.statusCode}', 'crud().getWallet - Other',
url.toString());
return 'failure';
}
}
// ═══════════════════════════════════════════════════════════════
// postWalletMtn — طلب MTN للمحفظة
// التغيير: إضافة X-Device-FP header
// ═══════════════════════════════════════════════════════════════
Future<dynamic> postWalletMtn({
required String link,
Map<String, dynamic>? payload,
}) async {
final s = await LoginDriverController().getJwtWallet();
final hmac = box.read(BoxName.hmac);
final url = Uri.parse(link);
try {
final response = await http.post(
url,
body: payload,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Bearer $s',
'X-HMAC-Auth': hmac.toString(),
'X-Device-FP': _getFpHeader(), // ← إثبات الجهاز
},
).timeout(const Duration(seconds: 60));
Map<String, dynamic> wrap(String status, {Object? message, int? code}) {
return {
'status': status,
'message': message,
'code': code ?? response.statusCode
};
}
if (response.statusCode == 200) {
try {
return jsonDecode(response.body);
} catch (e) {
return wrap('failure',
message: 'JSON decode error', code: response.statusCode);
}
} else if (response.statusCode == 401) {
try {
final jsonData = jsonDecode(response.body);
if (jsonData is Map && jsonData['error'] == 'Token expired') {
await Get.put(LoginDriverController()).getJWT();
return {
'status': 'failure',
'message': 'token_expired',
'code': 401
};
}
return wrap('failure', message: jsonData);
} catch (_) {
return wrap('failure', message: response.body);
}
} else {
try {
return wrap('failure', message: jsonDecode(response.body));
} catch (_) {
return wrap('failure', message: response.body);
}
}
} catch (e) {
return {
'status': 'failure',
'message': 'HTTP request error: $e',
'code': -1
};
}
}
// =======================================================================
// باقي الدوال الخارجية — لا تحتاج X-Device-FP (APIs خارجية)
// =======================================================================
Future<dynamic> getAgoraToken({
required String channelName,
required String uid,
}) async {
var uid = box.read(BoxName.phone) ?? box.read(BoxName.phoneDriver);
var res = await http.get(
Uri.parse(
'https://orca-app-b2i85.ondigitalocean.app/token?channelName=$channelName'),
headers: {'Authorization': 'Bearer ${AK.agoraAppCertificate}'},
);
if (res.statusCode == 200) {
return jsonDecode(res.body)['token'];
}
}
Future<dynamic> getLlama({
required String link,
required String payload,
required String prompt,
}) async {
var url = Uri.parse(link);
var headers = {
'Content-Type': 'application/json',
'Authorization':
'Bearer LL-X5lJ0Px9CzKK0HTuVZ3u2u4v3tGWkImLTG7okGRk4t25zrsLqJ0qNoUzZ2x4ciPy',
};
var data = json.encode({
'model': 'Llama-3-70b-Inst-FW',
'messages': [
{
'role': 'user',
'content':
'Extract the desired information from the following passage as json decoded like $prompt just in this:\n\n$payload',
}
],
'temperature': 0.9,
});
var response = await http.post(url, body: data, headers: headers);
if (response.statusCode == 200) return response.body;
return response.statusCode;
}
Future allMethodForAI(String prompt, linkPHP, imagePath) async {
await ImageController().choosImage(linkPHP, imagePath);
Future.delayed(const Duration(seconds: 2));
var extractedString =
await arabicTextExtractByVisionAndAI(imagePath: imagePath);
var json = jsonDecode(extractedString);
var textValues = extractTextFromLines(json);
await Get.put(AI()).anthropicAI(textValues, prompt, imagePath);
}
String extractTextFromLines(Map<String, dynamic> jsonData) {
final readResult = jsonData['readResult'];
final blocks = readResult['blocks'];
final buffer = StringBuffer();
for (final block in blocks) {
for (final line in block['lines']) {
buffer.write(line['text']);
buffer.write('\n');
}
}
return buffer.toString().trim();
}
Future<dynamic> arabicTextExtractByVisionAndAI(
{required String imagePath}) async {
var headers = {
'Content-Type': 'application/json',
'Ocp-Apim-Subscription-Key': AK.ocpApimSubscriptionKey,
};
String imagePathFull =
'${AppLink.server}/card_image/$imagePath-${box.read(BoxName.driverID)}.jpg';
var request = http.Request(
'POST',
Uri.parse(
'https://eastus.api.cognitive.microsoft.com/computervision/imageanalysis:analyze?features=caption,read&model-version=latest&language=en&api-version=2024-02-01'),
);
request.body = json.encode({'url': imagePathFull});
request.headers.addAll(headers);
http.StreamedResponse response = await request.send();
if (response.statusCode == 200)
return await response.stream.bytesToString();
}
Future<dynamic> getChatGPT(
{required String link, required String payload}) async {
var url = Uri.parse(link);
var headers = {
'Content-Type': 'application/json',
'Authorization': 'Bearer ${Env.chatGPTkeySeferNew}',
};
var data = json.encode({
'model': 'gpt-3.5-turbo',
'messages': [
{
'role': 'user',
'content':
'Extract the desired information from the following passage as json decoded like vin,make,made,year,expiration_date,color,owner,registration_date just in this:\n\n$payload',
}
],
'temperature': 0.9,
});
var response = await http.post(url, body: data, headers: headers);
if (response.statusCode == 200) return response.body;
return response.statusCode;
}
Future<dynamic> postPayMob(
{required String link, Map<String, dynamic>? payload}) async {
var url = Uri.parse(link);
var response = await http.post(url,
body: payload, headers: {'Content-Type': 'application/json'});
var jsonData = jsonDecode(response.body);
if (response.statusCode == 200) {
if (jsonData['status'] == 'success') return response.body;
return jsonData['status'];
}
return response.statusCode;
}
// ── sendEmail — إصلاح: استخدام r() بدل X.r() القديم ─────────
Future<void> sendEmail(String link, Map<String, String>? payload) async {
// r() هي نفس دالة فك التشفير الثلاثي المختصرة
String token = r(box.read(BoxName.jwt)).toString().split(Env.addd)[0];
if (!_isJwtValid(token)) {
await LoginDriverController().getJWT();
token = r(box.read(BoxName.jwt)).toString().split(Env.addd)[0];
}
final headers = {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Bearer $token',
'X-Device-FP': _getFpHeader(), // ← إثبات الجهاز
};
final request = http.Request('POST', Uri.parse(link));
request.bodyFields = payload ?? {};
request.headers.addAll(headers);
final response = await request.send();
if (response.statusCode != 200) {
final responseBody = await response.stream.bytesToString();
addError('sendEmail failed: ${response.statusCode}', responseBody,
'CRUD.sendEmail');
}
}
Future<dynamic> postFromDialogue(
{required String link, Map<String, dynamic>? payload}) async {
var url = Uri.parse(link);
var response = await http.post(
url,
body: payload,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization':
'Basic ${base64Encode(utf8.encode(AK.basicAuthCredentials))}',
},
);
if (response.body.isNotEmpty) {
var jsonData = jsonDecode(response.body);
if (response.statusCode == 200 && jsonData['status'] == 'success') {
Get.back();
return response.body;
}
return jsonData['status'];
}
}
Future<void> sendVerificationRequest(String phoneNumber) async {
final accountSid = AK.accountSIDTwillo;
final authToken = AK.authTokenTwillo;
final verifySid = AK.twilloRecoveryCode;
await http.post(
Uri.parse(
'https://verify.twilio.com/v2/Services/$verifySid/Verifications'),
headers: {
'Authorization':
'Basic ' + base64Encode(utf8.encode('$accountSid:$authToken')),
'Content-Type': 'application/x-www-form-urlencoded',
},
body: {'To': phoneNumber, 'Channel': 'sms'},
);
}
Future<dynamic> getGoogleApi(
{required String link, Map<String, dynamic>? payload}) async {
var url = Uri.parse(link);
var response = await http.post(url, body: payload);
var jsonData = jsonDecode(response.body);
if (jsonData['status'] == 'OK') return jsonData;
return jsonData['status'];
}
Future<dynamic> update({
required String endpoint,
required Map<String, dynamic> data,
required String id,
}) async {
var url = Uri.parse('$endpoint/$id');
var response = await http.put(
url,
body: json.encode(data),
headers: {
'Authorization':
'Basic ${base64Encode(utf8.encode(AK.basicAuthCredentials))}'
},
);
return json.decode(response.body);
}
Future<dynamic> delete({required String endpoint, required String id}) async {
var url = Uri.parse('$endpoint/$id');
var response = await http.delete(
url,
headers: {
'Authorization':
'Basic ${base64Encode(utf8.encode(AK.basicAuthCredentials))}'
},
);
return json.decode(response.body);
}
Future<dynamic> getMapSaas({
required String link,
}) async {
var url = Uri.parse(link);
try {
var response = await http.get(
url,
headers: {
'Content-Type': 'application/json',
'x-api-key': Env.mapSaasKey,
},
);
Log.print('link -MapSaas: $link');
Log.print('response -MapSaas: ${response.body}');
if (response.statusCode == 200) {
return jsonDecode(response.body);
}
Log.print('MapSaas Error: ${response.statusCode} - ${response.body}');
return null;
} catch (e) {
Log.print('MapSaas Exception: $e');
return null;
}
}
Future<dynamic> postMapSaas({
required String link,
required Map<String, dynamic> payload,
}) async {
var url = Uri.parse(link);
try {
var response = await http.post(
url,
body: jsonEncode(payload),
headers: {
'Content-Type': 'application/json',
'x-api-key': Env.mapSaasKey,
},
);
Log.print('post -MapSaas link: $link');
Log.print('post -MapSaas payload: $payload');
Log.print('post -MapSaas response: ${response.body}');
if (response.statusCode == 200 || response.statusCode == 201) {
return jsonDecode(response.body);
}
Log.print('MapSaas Post Error: ${response.statusCode} - ${response.body}');
return null;
} catch (e) {
Log.print('MapSaas Post Exception: $e');
return null;
}
}
}
class NoInternetException implements Exception {
final String message;
NoInternetException(
[this.message =
'No internet connection. Please check your network and try again.']);
@override
String toString() => message;
}
class WeakNetworkException implements Exception {
final String message;
WeakNetworkException(
[this.message =
'Your network connection is too slow. Please try again later.']);
@override
String toString() => message;
}
class ApiException implements Exception {
final String message;
final int? statusCode;
ApiException(this.message, [this.statusCode]);
@override
String toString() =>
'ApiException: $message (Status Code: ${statusCode ?? 'N/A'})';
}

View File

@@ -0,0 +1,26 @@
import 'package:flutter/material.dart';
class LineChartPainter extends CustomPainter {
final List<double> data;
LineChartPainter(this.data);
@override
void paint(Canvas canvas, Size size) {
// Calculate the scale factor.
final scaleFactor = size.height / 240;
// Draw the line chart.
for (var i = 0; i < data.length - 1; i++) {
final x1 = i * size.width / data.length;
final y1 = data[i] * scaleFactor;
final x2 = (i + 1) * size.width / data.length;
final y2 = data[i + 1] * scaleFactor;
canvas.drawLine(Offset(x1, y1), Offset(x2, y2), Paint());
}
}
@override
bool shouldRepaint(LineChartPainter oldDelegate) => false;
}

View File

@@ -0,0 +1,205 @@
import 'dart:async';
import 'dart:io';
import 'package:device_info_plus/device_info_plus.dart';
import 'performance_test.dart'; // Make sure this path is correct
/// Analyzes various device hardware and software aspects to generate a compatibility score.
/// This class provides a standardized output for the UI to consume easily.
class DeviceAnalyzer {
final DeviceInfoPlugin _deviceInfo = DeviceInfoPlugin();
/// Reads the total RAM from the system's meminfo file.
/// Returns the value in Megabytes (MB).
Future<double> _readTotalRamMB() async {
try {
final file = File('/proc/meminfo');
if (!await file.exists()) return 0.0;
final lines = await file.readAsLines();
for (var line in lines) {
if (line.startsWith('MemTotal')) {
// Extracts the numeric value from the line.
final kb = int.tryParse(RegExp(r'\d+').stringMatch(line) ?? '0') ?? 0;
return kb / 1024.0; // Convert from Kilobytes to Megabytes
}
}
} catch (e) {
print('❌ Error reading total RAM: $e');
}
return 0.0;
}
/// Reads the current RAM usage percentage from the system's meminfo file.
Future<double> _readUsedRamPercent() async {
try {
final file = File('/proc/meminfo');
if (!await file.exists()) return 0.0;
final lines = await file.readAsLines();
int? total, available;
for (var line in lines) {
if (line.startsWith('MemTotal')) {
total = int.tryParse(RegExp(r'\d+').stringMatch(line) ?? '');
} else if (line.startsWith('MemAvailable')) {
available = int.tryParse(RegExp(r'\d+').stringMatch(line) ?? '');
}
}
if (total != null && available != null && total > 0) {
final used = total - available;
return (used / total) * 100.0;
}
} catch (e) {
print('❌ Error reading used RAM: $e');
}
return 0.0;
}
/// The main analysis function that runs all checks.
Future<Map<String, dynamic>> analyzeDevice() async {
List<Map<String, dynamic>> details = [];
if (!Platform.isAndroid) {
return {
'score': 0,
'details': [
{
'label': 'النظام غير مدعوم',
'status': false,
'achieved_score': 0,
'max_score': 100
}
]
};
}
final info = await _deviceInfo.androidInfo;
final data = info.data;
final features = List<String>.from(data['systemFeatures'] ?? []);
// 1. Android Version (Max: 10 points)
final version =
int.tryParse(info.version.release?.split('.').first ?? '0') ?? 0;
final int androidScore = version >= 9 ? 10 : 0;
details.add({
'label': 'إصدار أندرويد ${info.version.release}',
'status': androidScore > 0,
'achieved_score': androidScore,
'max_score': 10,
});
// 2. Total RAM (Max: 10 points)
final totalRam = await _readTotalRamMB();
int ramScore;
if (totalRam >= 8000) {
ramScore = 10;
} else if (totalRam >= 4000) {
ramScore = 5;
} else if (totalRam >= 3000) {
ramScore = 3;
} else {
ramScore = 0;
}
details.add({
'label': 'إجمالي الرام ${totalRam.toStringAsFixed(0)} ميجابايت',
'status': ramScore >= 5,
'achieved_score': ramScore,
'max_score': 10,
});
// 3. CPU Cores (Max: 10 points)
final cores = Platform.numberOfProcessors;
int coreScore = cores >= 6 ? 10 : (cores >= 4 ? 5 : 0);
details.add({
'label': 'أنوية المعالج ($cores)',
'status': coreScore >= 5,
'achieved_score': coreScore,
'max_score': 10,
});
// 4. Free Storage (Max: 5 points)
final freeBytes = data['freeDiskSize'] ?? 0;
final freeGB = freeBytes / (1024 * 1024 * 1024);
int storeScore = freeGB >= 5 ? 5 : (freeGB >= 2 ? 3 : 0);
details.add({
'label': 'المساحة الحرة ${freeGB.toStringAsFixed(1)} جيجابايت',
'status': storeScore >= 3,
'achieved_score': storeScore,
'max_score': 5,
});
// 5. GPS + Gyroscope Sensors (Max: 10 points)
bool okSensors = features.contains('android.hardware.location.gps') &&
features.contains('android.hardware.sensor.gyroscope');
final int sensorScore = okSensors ? 10 : 0;
details.add({
'label': 'حساسات GPS و Gyroscope',
'status': okSensors,
'achieved_score': sensorScore,
'max_score': 10,
});
// 6. Storage Write Speed (Max: 20 points)
final writeSpeed = await PerformanceTester.testStorageWriteSpeed();
int writeScore;
if (writeSpeed >= 30) {
writeScore = 20;
} else if (writeSpeed >= 15) {
writeScore = 15;
} else if (writeSpeed >= 5) {
writeScore = 10;
} else {
writeScore = 5;
}
details.add({
'label': 'سرعة الكتابة (${writeSpeed.toStringAsFixed(1)} MB/s)',
'status': writeScore >= 10,
'achieved_score': writeScore,
'max_score': 20,
});
// 7. CPU Compute Speed (Max: 20 points)
final cpuTime = await PerformanceTester.testCPUSpeed();
int cpuScore;
if (cpuTime <= 1.0) {
cpuScore = 20;
} else if (cpuTime <= 2.5) {
cpuScore = 15;
} else if (cpuTime <= 4.0) {
cpuScore = 10;
} else {
cpuScore = 5;
}
details.add({
'label': 'سرعة المعالجة (${cpuTime.toStringAsFixed(2)} ثانية)',
'status': cpuScore >= 10,
'achieved_score': cpuScore,
'max_score': 20,
});
// 8. Memory Pressure (Max: 15 points)
final usedPercent = await _readUsedRamPercent();
int memScore;
if (usedPercent <= 60) {
memScore = 15;
} else if (usedPercent <= 80) {
memScore = 10;
} else if (usedPercent <= 90) {
memScore = 5;
} else {
memScore = 0;
}
details.add({
'label': 'استخدام الرام الحالي (${usedPercent.toStringAsFixed(0)}%)',
'status': memScore >= 10,
'achieved_score': memScore,
'max_score': 15,
});
// Calculate the final total score by summing up the achieved scores.
final totalScore = details.fold<int>(
0, (sum, item) => sum + (item['achieved_score'] as int));
return {
'score': totalScore.clamp(0, 100),
'details': details,
};
}
}

View File

@@ -0,0 +1,80 @@
import 'dart:io';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:get_storage/get_storage.dart';
import '../../constant/box_name.dart';
class DeviceInfo {
final String? manufacturer;
final String? model;
final String? deviceId;
final String? osVersion;
final String? platform;
final String? deviceName;
final bool? isPhysicalDevice;
DeviceInfo({
this.manufacturer,
this.model,
this.deviceId,
this.osVersion,
this.platform,
this.deviceName,
this.isPhysicalDevice,
});
Map<String, dynamic> toJson() => {
'manufacturer': manufacturer,
'model': model,
'deviceId': deviceId,
'osVersion': osVersion,
'platform': platform,
'deviceName': deviceName,
'isPhysicalDevice': isPhysicalDevice,
};
}
class DeviceController {
final box = GetStorage();
final _deviceInfo = DeviceInfoPlugin();
Future<DeviceInfo> getDeviceInfo() async {
if (Platform.isAndroid) {
return await _getAndroidDeviceInfo();
} else if (Platform.isIOS) {
return await _getIosDeviceInfo();
}
throw UnsupportedError('Unsupported platform');
}
Future<DeviceInfo> _getAndroidDeviceInfo() async {
final androidInfo = await _deviceInfo.androidInfo;
final deviceInfo = DeviceInfo(
manufacturer: androidInfo.manufacturer,
model: androidInfo.model,
deviceId: androidInfo.id,
osVersion: androidInfo.version.release,
platform: 'Android',
deviceName: androidInfo.device,
isPhysicalDevice: androidInfo.isPhysicalDevice,
);
box.write(BoxName.deviceInfo, deviceInfo.toJson());
return deviceInfo;
}
Future<DeviceInfo> _getIosDeviceInfo() async {
final iosInfo = await _deviceInfo.iosInfo;
final deviceInfo = DeviceInfo(
manufacturer: 'Apple',
model: iosInfo.model,
deviceId: iosInfo.identifierForVendor,
osVersion: iosInfo.systemVersion,
platform: 'iOS',
deviceName: iosInfo.name,
isPhysicalDevice: iosInfo.isPhysicalDevice,
);
box.write(BoxName.deviceInfo, deviceInfo.toJson());
return deviceInfo;
}
}

View File

@@ -0,0 +1,42 @@
import 'package:flutter/services.dart';
class DigitObscuringFormatter extends TextInputFormatter {
@override
TextEditingValue formatEditUpdate(
TextEditingValue oldValue, TextEditingValue newValue) {
final maskedText = maskDigits(newValue.text);
return newValue.copyWith(
text: maskedText,
selection: updateCursorPosition(maskedText, newValue.selection));
}
String maskDigits(String text) {
final totalDigits = text.length;
final visibleDigits = 4;
final hiddenDigits = totalDigits - visibleDigits * 2;
final firstVisibleDigits = text.substring(0, visibleDigits);
final lastVisibleDigits = text.substring(totalDigits - visibleDigits);
final maskedDigits = List.filled(hiddenDigits, '*').join();
return '$firstVisibleDigits$maskedDigits$lastVisibleDigits';
}
TextSelection updateCursorPosition(
String maskedText, TextSelection currentSelection) {
final cursorPosition = currentSelection.baseOffset;
final cursorOffset =
currentSelection.extentOffset - currentSelection.baseOffset;
final totalDigits = maskedText.length;
const visibleDigits = 4;
final hiddenDigits = totalDigits - visibleDigits * 2;
final updatedPosition = cursorPosition <= visibleDigits
? cursorPosition
: hiddenDigits + visibleDigits + (cursorPosition - visibleDigits);
return TextSelection.collapsed(
offset: updatedPosition, affinity: currentSelection.affinity);
}
}

View File

@@ -0,0 +1,41 @@
// import 'dart:io';
//
// import 'package:get/get.dart';
// import 'package:image_picker/image_picker.dart';
// import 'package:google_ml_kit/google_ml_kit.dart';
//
// class ImagePickerController extends GetxController {
// RxBool textScanning = false.obs;
// RxString scannedText = ''.obs;
//
// Future<void> getImage(ImageSource source) async {
// try {
// final pickedImage = await ImagePicker().pickImage(source: source);
// if (pickedImage != null) {
// textScanning.value = true;
// final imageFile = File(pickedImage.path);
// getRecognisedText(imageFile);
// }
// } catch (e) {
// textScanning.value = false;
// scannedText.value = "Error occurred while scanning";
// }
// }
//
// Future<void> getRecognisedText(File image) async {
// final inputImage = InputImage.fromFilePath(image.path);
// final textDetector = GoogleMlKit.vision.textRecognizer();
// final RecognizedText recognisedText =
// await textDetector.processImage(inputImage);
// await textDetector.close();
//
// scannedText.value = '';
// for (TextBlock block in recognisedText.blocks) {
// for (TextLine line in block.lines) {
// scannedText.value += line.text + '\n';
// }
// }
//
// textScanning.value = false;
// }
// }

View File

@@ -0,0 +1,32 @@
import 'dart:convert';
import 'package:encrypt/encrypt.dart' as encrypt;
import '../../constant/api_key.dart';
class KeyEncryption {
// استخدم مفتاح بطول 32 حرفًا
static final _key = encrypt.Key.fromUtf8(AK.keyOfApp);
static final _iv =
encrypt.IV.fromLength(16); // توليد تهيئة عشوائية بطول 16 بايت
static String encryptKey(String key) {
final encrypter =
encrypt.Encrypter(encrypt.AES(_key, mode: encrypt.AESMode.cbc));
final encrypted = encrypter.encrypt(key, iv: _iv);
final result = _iv.bytes + encrypted.bytes; // تضمين التهيئة مع النص المشفر
return base64Encode(result);
}
static String decryptKey(String encryptedKey) {
print('encryptedKey: ${AK.keyOfApp}');
final decoded = base64Decode(encryptedKey);
print('encryptedKey: $encryptedKey');
final iv = encrypt.IV(decoded.sublist(0, 16)); // استخراج التهيئة
final encrypted =
encrypt.Encrypted(decoded.sublist(16)); // استخراج النص المشفر
final encrypter =
encrypt.Encrypter(encrypt.AES(_key, mode: encrypt.AESMode.cbc));
return encrypter.decrypt(encrypted, iv: iv);
}
}

View File

@@ -0,0 +1,77 @@
import 'package:encrypt/encrypt.dart' as encrypt;
import 'package:flutter/foundation.dart';
import 'package:secure_string_operations/secure_string_operations.dart';
import '../../constant/char_map.dart';
import '../../env/env.dart';
import '../../main.dart';
import '../../print.dart';
class EncryptionHelper {
static EncryptionHelper? _instance;
late final encrypt.Key key;
late final encrypt.IV iv;
EncryptionHelper._(this.key, this.iv);
static EncryptionHelper get instance {
if (_instance == null) {
throw Exception(
"EncryptionHelper is not initialized. Call `await EncryptionHelper.initialize()` in main.");
}
return _instance!;
}
/// Initializes and stores the instance globally
static Future<void> initialize() async {
if (_instance != null) {
debugPrint("EncryptionHelper is already initialized.");
return; // Prevent re-initialization
}
debugPrint("Initializing EncryptionHelper...");
var keyOfApp = r(Env.keyOfApp).toString().split(Env.addd)[0];
var initializationVector =
r(Env.initializationVector).toString().split(Env.addd)[0];
// Set the global instance
_instance = EncryptionHelper._(
encrypt.Key.fromUtf8(keyOfApp!),
encrypt.IV.fromUtf8(initializationVector!),
);
debugPrint("EncryptionHelper initialized successfully.");
}
/// Encrypts a string
String encryptData(String plainText) {
try {
final encrypter =
encrypt.Encrypter(encrypt.AES(key, mode: encrypt.AESMode.cbc));
final encrypted = encrypter.encrypt(plainText, iv: iv);
return encrypted.base64;
} catch (e) {
debugPrint('Encryption Error: $e');
return '';
}
}
/// Decrypts a string
String decryptData(String encryptedText) {
try {
final encrypter =
encrypt.Encrypter(encrypt.AES(key, mode: encrypt.AESMode.cbc));
final encrypted = encrypt.Encrypted.fromBase64(encryptedText);
return encrypter.decrypt(encrypted, iv: iv);
} catch (e) {
debugPrint('Decryption Error: $e');
return '';
}
}
}
r(String string) {
return X.r(X.r(X.r(string, cn), cC), cs).toString();
}
c(String string) {
return X.c(X.c(X.c(string, cn), cC), cs).toString();
}

View File

@@ -0,0 +1,95 @@
import 'dart:convert';
import 'dart:io';
import 'package:siro_driver/constant/api_key.dart';
import 'package:siro_driver/constant/box_name.dart';
import 'package:siro_driver/main.dart';
import 'package:http/http.dart' as http;
import 'package:http/io_client.dart';
import '../../constant/links.dart';
import 'encrypt_decrypt.dart';
import 'upload_image.dart';
Future<String> faceDetector() async {
await ImageController().choosFace(AppLink.uploadEgypt, 'face_detect');
await Future.delayed(const Duration(seconds: 2));
var headers = {
// 'Authorization': 'Basic ${AK.basicCompareFaces}',
'Authorization': 'Basic hamza:12345678',
'Content-Type': 'application/json'
};
// var request = http.Request('POST', Uri.parse(//Todo
// 'https://face-detect-f6924392c4c7.herokuapp.com/compare_faces'));
var request = http.Request(
'POST', Uri.parse('https://mohkh.online:5000/compare_faces'));
request.body = json.encode({
"url1":
"${AppLink.seferCairoServer}/card_image/id_front-${(box.read(BoxName.driverID))}.jpg",
"url2":
"https://api.sefer.live/sefer/card_image/face_detect-${(box.read(BoxName.driverID))}.jpg"
});
print('request.body: ${request.body}');
request.headers.addAll(headers);
try {
http.Client client = await createHttpClient();
http.StreamedResponse response = await client.send(request);
// http.StreamedResponse response = await request.send();
if (response.statusCode == 200) {
String result = await response.stream.bytesToString();
print('result: ${result}');
return result;
} else {
print('Error: ${response.reasonPhrase}');
return 'Error: ${response.reasonPhrase}';
}
} catch (e) {
print('Exception occurred: $e');
return 'Error: $e';
}
}
Future<http.Client> createHttpClient() async {
final SecurityContext securityContext = SecurityContext();
HttpClient httpClient = HttpClient(context: securityContext);
httpClient.badCertificateCallback =
(X509Certificate cert, String host, int port) => true; // Bypass SSL
return IOClient(httpClient);
}
Future<String> faceDetector2(String url1, String url2) async {
var headers = {
'Authorization': 'Basic hamza:12345678',
'Content-Type': 'application/json'
};
var request = http.Request(
'POST', Uri.parse('https://mohkh.online:5000/compare_faces'));
request.body = json.encode({"url1": url1, "url2": url2});
request.headers.addAll(headers);
try {
http.Client client = await createHttpClient(); // Use custom client
DateTime startTime = DateTime.now();
http.StreamedResponse response = await client.send(request);
DateTime endTime = DateTime.now();
Duration duration = endTime.difference(startTime);
if (response.statusCode == 200) {
print(await response.stream.bytesToString());
print(duration.inSeconds);
return await response.stream.bytesToString();
} else {
print(await response.stream.bytesToString());
return 'Error: ${response.reasonPhrase}';
}
} catch (e) {
return 'Exception: $e';
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,34 @@
import 'package:geolocator/geolocator.dart';
class GeoLocation {
Future<Position> getCurrentLocation() async {
bool serviceEnabled;
LocationPermission permission;
// Check if location services are enabled.
serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled) {
// Location services are not enabled, so we request the user to enable it.
return Future.error('Location services are disabled.');
}
permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
if (permission == LocationPermission.denied) {
// Permissions are denied, we cannot fetch the location.
return Future.error('Location permissions are denied');
}
}
if (permission == LocationPermission.deniedForever) {
// Permissions are denied forever, we cannot request permissions.
return Future.error(
'Location permissions are permanently denied, we cannot request permissions.');
}
// When we reach here, permissions are granted and we can fetch the location.
return await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.high);
}
}

View File

@@ -0,0 +1,123 @@
import 'package:url_launcher/url_launcher.dart';
import 'dart:io';
import 'package:get/get.dart';
import 'package:siro_driver/views/widgets/error_snakbar.dart';
void showInBrowser(String url) async {
if (await canLaunchUrl(Uri.parse(url))) {
launchUrl(Uri.parse(url));
} else {}
}
String cleanAndFormatPhoneNumber(String phoneNumber) {
// 1. Clean the number
String formattedNumber = phoneNumber.replaceAll(RegExp(r'\s+'), '');
// 2. Format logic (Syria/Egypt/International)
if (formattedNumber.length > 6) {
if (formattedNumber.startsWith('09')) {
formattedNumber = '+963${formattedNumber.substring(1)}';
} else if (formattedNumber.startsWith('01') && formattedNumber.length == 11) {
formattedNumber = '+20${formattedNumber.substring(1)}';
} else if (formattedNumber.startsWith('00')) {
formattedNumber = '+${formattedNumber.substring(2)}';
} else if (!formattedNumber.startsWith('+')) {
formattedNumber = '+$formattedNumber';
}
}
return formattedNumber;
}
Future<void> makePhoneCall(String phoneNumber) async {
String formattedNumber = cleanAndFormatPhoneNumber(phoneNumber);
if (!formattedNumber.startsWith('+963')) {
mySnackeBarError("Calling non-Syrian numbers is not supported".tr);
return;
}
// Create URI directly from String to avoid double encoding '+' as '%2B'
final Uri launchUri = Uri.parse('tel:$formattedNumber');
// 4. Execute with externalApplication mode
try {
if (!await launchUrl(launchUri, mode: LaunchMode.externalApplication)) {
throw 'Could not launch $launchUri';
}
} catch (e) {
if (await canLaunchUrl(launchUri)) {
await launchUrl(launchUri);
} else {
print("Cannot launch url: $launchUri");
}
}
}
void launchCommunication(
String method, String contactInfo, String message) async {
String formattedContact = cleanAndFormatPhoneNumber(contactInfo);
// WhatsApp prefers the phone number without the '+' prefix
String whatsappContact = formattedContact.replaceAll('+', '');
String url;
if (Platform.isIOS) {
switch (method) {
case 'phone':
if (!formattedContact.startsWith('+963')) {
mySnackeBarError("Calling non-Syrian numbers is not supported".tr);
return;
}
url = 'tel:$formattedContact';
break;
case 'sms':
url = 'sms:$formattedContact?body=${Uri.encodeComponent(message)}';
break;
case 'whatsapp':
url =
'https://api.whatsapp.com/send?phone=$whatsappContact&text=${Uri.encodeComponent(message)}';
break;
case 'email':
url =
'mailto:$formattedContact?subject=Subject&body=${Uri.encodeComponent(message)}';
break;
default:
return;
}
} else if (Platform.isAndroid) {
switch (method) {
case 'phone':
if (!formattedContact.startsWith('+963')) {
mySnackeBarError("Calling non-Syrian numbers is not supported".tr);
return;
}
url = 'tel:$formattedContact';
break;
case 'sms':
url = 'sms:$formattedContact?body=${Uri.encodeComponent(message)}';
break;
case 'whatsapp':
final bool whatsappInstalled =
await canLaunchUrl(Uri.parse('whatsapp://'));
if (whatsappInstalled) {
url =
'whatsapp://send?phone=$whatsappContact&text=${Uri.encodeComponent(message)}';
} else {
url =
'https://api.whatsapp.com/send?phone=$whatsappContact&text=${Uri.encodeComponent(message)}';
}
break;
case 'email':
url =
'mailto:$formattedContact?subject=Subject&body=${Uri.encodeComponent(message)}';
break;
default:
return;
}
} else {
return;
}
if (await canLaunchUrl(Uri.parse(url))) {
await launchUrl(Uri.parse(url));
} else {}
}

View File

@@ -0,0 +1,37 @@
import 'dart:convert';
import 'package:siro_driver/constant/links.dart';
import 'package:siro_driver/controller/functions/crud.dart';
import 'package:siro_driver/controller/functions/gemeni.dart';
class LlamaAi {
Future<Map> getCarRegistrationData(String input, prompt) async {
Map exrtatDataFinal = {};
String oneLine = input.replaceAll('\n', ' ');
// var res = await CRUD().getLlama(link: AppLink.gemini, payload: oneLine);
var res = await CRUD()
.getLlama(link: AppLink.llama, payload: oneLine, prompt: prompt);
var decod = jsonDecode(res.toString());
// exrtatDataFinal = jsonDecode(extractDataFromJsonString(decod['choices']));
extractDataFromJsonString(decod['choices'][0]['message']['content']);
return exrtatDataFinal;
}
String extractDataFromJsonString(String jsonString) {
// Remove any leading or trailing whitespace from the string
jsonString = jsonString.trim();
// Extract the JSON substring from the given string
final startIndex = jsonString.indexOf('{');
final endIndex = jsonString.lastIndexOf('}');
final jsonSubstring = jsonString.substring(startIndex, endIndex + 1);
// Parse the JSON substring into a Map
final jsonData = jsonDecode(jsonSubstring);
// Return the extracted data
return jsonEncode(jsonData);
}
}

View File

@@ -0,0 +1,85 @@
import 'dart:io';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:get/get_core/src/get_main.dart';
import 'package:get/get_navigation/src/extension_navigation.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:siro_driver/views/widgets/error_snakbar.dart';
import 'package:siro_driver/views/widgets/mydialoug.dart';
import 'background_service.dart';
Future<void> requestNotificationPermission() async {
if (Platform.isAndroid) {
final androidInfo = await DeviceInfoPlugin().androidInfo;
if (androidInfo.version.sdkInt >= 33) {
// Android 13+
final status = await Permission.notification.request();
if (!status.isGranted) {
print('⚠️ إذن الإشعارات مرفوض');
return;
}
}
}
// بعد الحصول على الإذن، ابدأ الخدمة
await BackgroundServiceHelper.startService();
}
class PermissionsHelper {
/// طلب إذن الإشعارات على Android 13+
static Future<bool> requestNotificationPermission() async {
if (Platform.isAndroid) {
final androidInfo = await DeviceInfoPlugin().androidInfo;
if (androidInfo.version.sdkInt >= 33) {
final status = await Permission.notification.request();
if (status.isDenied) {
print('⚠️ إذن الإشعارات مرفوض');
mySnackbarWarning(
"يرجى منح صلاحية الإشعارات لضمان وصول الطلبات إليك");
return false;
}
if (status.isPermanentlyDenied) {
print('⚠️ إذن الإشعارات مرفوض بشكل دائم - افتح الإعدادات');
mySnackbarWarning('يرجى فتح الإعدادات وتفعيل صلاحية الإشعارات');
return false;
}
}
}
return true;
}
/// طلب جميع الإذونات المطلوبة
static Future<bool> requestAllPermissions() async {
// إذن الإشعارات (اختياري)
await requestNotificationPermission();
// 1. طلب إذن الموقع الأساسي فقط إذا كان مرفوضاً
var status = await Permission.location.status;
if (status.isDenied) {
status = await Permission.location.request();
}
if (status.isPermanentlyDenied) {
_showSettingsDialog('الموقع');
return false;
}
return status.isGranted || status.isLimited;
}
static void _showSettingsDialog(String permissionName) {
MyDialog().getDialog(
'صلاحية $permissionName مطلوبة',
'لقد قمت برفض صلاحية $permissionName سابقاً. يرجى تفعيلها من الإعدادات لتمكين التطبيق من العمل.',
() async {
await openAppSettings();
Get.back();
},
);
}
}

View File

@@ -0,0 +1,820 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:geolocator/geolocator.dart' as geo;
import 'package:intaleq_maps/intaleq_maps.dart';
import 'package:location/location.dart';
import 'package:battery_plus/battery_plus.dart';
import 'package:permission_handler/permission_handler.dart' as ph;
import 'package:socket_io_client/socket_io_client.dart' as IO;
import 'package:siro_driver/constant/table_names.dart';
import 'package:trip_overlay_plugin/trip_overlay_plugin.dart';
import '../../constant/box_name.dart';
import '../../constant/links.dart';
import '../../main.dart';
import '../../print.dart';
import '../firebase/local_notification.dart';
import '../home/captin/home_captain_controller.dart';
import '../home/captin/map_driver_controller.dart';
import '../home/payment/captain_wallet_controller.dart';
import 'background_service.dart';
import 'crud.dart';
class LocationController extends GetxController with WidgetsBindingObserver {
// ===================================================================
// ====== Tunables ======
// ===================================================================
static const Duration recordIntervalNormal = Duration(seconds: 3);
static const Duration uploadBatchIntervalNormal = Duration(minutes: 2);
static const Duration recordIntervalPowerSave = Duration(seconds: 10);
static const Duration uploadBatchIntervalPowerSave = Duration(minutes: 5);
static const double lowWalletThreshold = -200;
static const int powerSaveTriggerLevel = 20;
static const int powerSaveExitLevel = 25;
// ===================================================================
// ====== Services & Variables ======
// ===================================================================
late final Location location = Location();
final Battery _battery = Battery();
IO.Socket? socket;
bool isSocketConnected = false;
Timer? _socketHeartbeat;
StreamSubscription<LocationData>? _locSub;
StreamSubscription<BatteryState>? _batterySub;
Timer? _recordTimer;
Timer? _uploadBatchTimer;
late final HomeCaptainController _homeCtrl;
late final CaptainWalletController _walletCtrl;
LatLng myLocation = LatLng(
box.read('last_lat') ?? 0.0,
box.read('last_lng') ?? 0.0,
);
double heading = box.read('last_heading') ?? 0.0;
double speed = 0.0;
double totalDistance = 0.0;
bool _isReady = false;
bool _isPowerSavingMode = false;
final List<Map<String, dynamic>> _trackBuffer = [];
final List<Map<String, dynamic>> _behaviorBuffer = [];
LatLng? _lastPosForDistance;
LatLng? _lastRecordedRealLoc;
DateTime? _lastRecordedTime;
LatLng? _lastSqlLoc;
double? _lastSpeed;
DateTime? _lastSpeedAt;
@override
Future<void> onInit() async {
super.onInit();
Log.print('🚀 LocationController Starting...');
// 1. Register Lifecycle Observer
WidgetsBinding.instance.addObserver(this);
box.write(BoxName.isAppInForeground, true);
// مراقب الحالة (Status Watcher)
box.listenKey(BoxName.statusDriverLocation, (value) {
if (value == 'blocked') {
Log.print("⛔ Driver is Blocked: Force Stopping Location Updates.");
stopLocationUpdates();
if (socket != null && socket!.connected) {
socket!.emit('update_location', {
'driver_id': box.read(BoxName.driverID),
'status': 'blocked',
'lat': myLocation.latitude,
'lng': myLocation.longitude,
'heading': heading,
'speed': speed * 3.6,
'distance': totalDistance
});
socket!.disconnect();
}
}
});
bool deps = await _awaitDependencies();
if (!deps) return;
_isReady = true;
initSocket();
await _initLocationSettings();
_listenToBatteryChanges();
if (box.read(BoxName.statusDriverLocation) != 'blocked') {
await startLocationUpdates();
}
Log.print('✅ LocationController Initialized.');
}
@override
void onClose() {
WidgetsBinding.instance.removeObserver(this);
box.write(BoxName.isAppInForeground, false);
stopLocationUpdates();
_batterySub?.cancel();
_stopHeartbeat();
socket?.dispose();
super.onClose();
}
// ===================================================================
// 🔥 Lifecycle Manager (Fixes Freeze & Background issues)
// ===================================================================
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
Log.print("📱 Lifecycle: App is in FOREGROUND");
box.write(BoxName.isAppInForeground, true);
// إيقاف خدمة الخلفية
BackgroundServiceHelper.stopService();
if (socket == null || (!socket!.connected && !_isInitializingSocket)) {
Log.print("🔄 Initializing Socket on resume...");
initSocket();
}
} else if (state == AppLifecycleState.paused ||
state == AppLifecycleState.detached) {
Log.print("📱 Lifecycle: App is in BACKGROUND");
box.write(BoxName.isAppInForeground, false);
// تشغيل خدمة الخلفية للأندرويد لضمان بقاء التطبيق حياً
if (!Platform.isIOS) {
BackgroundServiceHelper.startService();
}
}
}
Future<bool> _awaitDependencies() async {
int attempts = 0;
while (attempts < 10) {
if (Get.isRegistered<HomeCaptainController>() &&
Get.isRegistered<CaptainWalletController>()) {
_homeCtrl = Get.find<HomeCaptainController>();
_walletCtrl = Get.find<CaptainWalletController>();
return true;
}
await Future.delayed(const Duration(milliseconds: 500));
attempts++;
}
return false;
}
// ===================================================================
// ====== Socket Logic (Improved) ======
// ===================================================================
bool _isInitializingSocket = false;
void initSocket() {
// منع الاستدعاءات المتداخلة التي تسبب قتل الاتصال قبل اكتماله
if (_isInitializingSocket) {
Log.print("⏳ Socket is already initializing. Skipping redundant call.");
return;
}
if (socket != null && socket!.connected) {
Log.print("✅ Socket is already connected. No need to re-init.");
return;
}
String driverId = box.read(BoxName.driverID).toString();
String token = box.read(BoxName.tokenDriver).toString();
String platform = Platform.isIOS ? 'ios' : 'android';
_isInitializingSocket = true;
// تنظيف السوكيت القديم فقط إذا كان موجوداً وغير متصل
if (socket != null) {
Log.print("🧹 Cleaning up old socket instance...");
socket!.clearListeners();
socket!.dispose();
socket = null;
}
Log.print(
"🟡 [LocationController] Initializing NEW Socket for Driver: $driverId");
try {
// العودة للـ Websocket حصراً لأنه الوحيد الذي ينجح في فتح القناة
socket = IO.io(
'https://location.intaleq.xyz',
IO.OptionBuilder()
.setTransports(['websocket'])
.setQuery({'driver_id': driverId, 'token': token, 'EIO': '3'})
.enableForceNew()
.build());
_setupSocketListeners();
socket!.connect();
} catch (e) {
_isInitializingSocket = false;
Log.print("❌ Socket Initialization Exception: $e");
}
}
void _setupSocketListeners() {
if (socket == null) return;
socket!.off('connect');
socket!.off('disconnect');
socket!.off('connect_error');
socket!.off('error');
socket!.onConnect((_) {
_isInitializingSocket = false;
// ننتظر قليلاً للتأكد من تعبئة الـ IDs
Future.delayed(const Duration(milliseconds: 1000), () {
String? sid = socket?.id;
String? eid = socket?.io.engine?.id;
Log.print(
'✅ Socket Connected! ID: ${sid ?? eid ?? 'N/A'} (SID: $sid, EID: $eid)');
if (sid != null || eid != null) {
isSocketConnected = true;
_startHeartbeat();
}
});
});
socket!.onDisconnect((data) {
_isInitializingSocket = false;
Log.print('❌ Socket Disconnected: $data');
isSocketConnected = false;
_stopHeartbeat();
});
socket!.onConnectError((err) {
_isInitializingSocket = false;
Log.print('❌ Socket Connect Error: $err');
});
socket!.onConnectTimeout((data) {
_isInitializingSocket = false;
Log.print('❌ Socket Connect Timeout: $data');
});
socket!.onError((err) {
_isInitializingSocket = false;
Log.print('❌ Socket General Error: $err');
});
socket!.on('reconnect_attempt', (attempt) {
Log.print('🔄 Socket Reconnecting... Attempt: $attempt');
});
// 🔥 الاستماع للطلبات الجديدة
socket!.on('new_ride_request', (data) {
Log.print("🔔 Socket: New Ride Request Arrived!");
// نستخدم Future.microtask لضمان عدم حظر الـ UI Thread
Future.microtask(() {
if (data != null) {
try {
List<dynamic> rawList = [];
if (data is String) {
var decoded = jsonDecode(data);
if (decoded is List) rawList = decoded;
} else if (data is List) {
if (data.isNotEmpty) {
rawList = (data[0] is List) ? data[0] : data;
}
}
if (rawList.isNotEmpty) {
Map<String, dynamic> convertedMap = {};
for (int i = 0; i < rawList.length; i++) {
convertedMap[i.toString()] = rawList[i];
}
handleIncomingOrder(convertedMap, "Socket");
}
} catch (e) {
Log.print("❌ Error processing socket data: $e");
}
}
});
});
// 🔥 الاستماع للإلغاء
socket!.on('cancel_ride', (data) {
Log.print("🚫 Socket: Ride Cancelled Event Received");
String reason = data['reason'] ?? 'No reason provided';
if (Get.isRegistered<MapDriverController>()) {
Get.find<MapDriverController>()
.processRideCancelledByPassenger(reason, source: "Socket");
}
});
}
// داخل LocationController
Future<void> handleIncomingOrder(
Map<String, dynamic> rideData, String source) async {
Log.print("📦 Socket Order Received from ($source)");
// 🔴 1. التحقق من حالة التطبيق قبل أي شيء 🔴
bool isAppInForeground = box.read(BoxName.isAppInForeground) ?? false;
if (!isAppInForeground) {
Log.print(
"📱 [LocationController] Order received in background (iOS/Android). Source: $source");
if (Platform.isIOS) {
// على iOS، نقوم بإظهار إشعار محلي لأن الـ Overlay غير مدعوم
NotificationController().showNotification(
"طلب رحلة جديد 🚖",
"لديك طلب رحلة جديد، افتح التطبيق للموافقة عليه",
jsonEncode(rideData),
'ding.wav');
}
return;
}
try {
// 2. التحقق من صحة البيانات
if (rideData.isEmpty || !rideData.containsKey('16')) {
Log.print("❌ Socket Error: Invalid Ride Data.");
return;
}
// 3. تجهيز البيانات (DriverList)
List<dynamic> driverList = [];
if (rideData.isNotEmpty) {
var sortedKeys = rideData.keys
.where((e) => int.tryParse(e) != null)
.map((e) => int.parse(e))
.toList()..sort();
for (var key in sortedKeys) {
driverList.add(rideData[key.toString()]);
}
}
// الحماية ضد البنية غير المكتملة
if (driverList.length <= 16) {
Log.print("❌ Socket Error: Parsed driver list is incomplete.");
return;
}
// 4. إغلاق النافذة (إن وجدت بالخطأ) والتنقل
try {
if (await TripOverlayPlugin.isOverlayActive()) {
Log.print("📲 Closing Overlay because App took control via Socket");
await TripOverlayPlugin.hideOverlay();
}
} catch (e) {
Log.print("Overlay check error: $e");
}
// 🔥 [Fix Active-Ride Guard] منع فتح صفحة الطلبات أثناء وجود السائق في رحلة نشطة
// هذا يمنع socket event جديد من تعطيل رحلة جارية
String? currentRideStatus = box.read(BoxName.rideStatus);
bool hasActiveRide = (currentRideStatus == 'Begin' ||
currentRideStatus == 'Apply' ||
currentRideStatus == 'Arrived');
String currentRoute = Get.currentRoute;
bool isOnMapPage = currentRoute.contains('MapPage') ||
currentRoute.contains('PassengerLocation');
if (hasActiveRide || isOnMapPage) {
Log.print(
"⛔ [LocationController] Ignoring new ride request — driver has active ride ($currentRideStatus) or is on map page ($currentRoute).");
return;
}
if (currentRoute != '/OrderRequestPage') {
Log.print("🚀 Socket: Navigating to OrderRequestPage...");
Get.toNamed('/OrderRequestPage', arguments: {
'myListString': jsonEncode(driverList),
'DriverList': driverList,
'body': 'New Trip Request via Socket ⚡'
});
} else {
Log.print(
"⚠️ User is already on OrderRequestPage. Skipping navigation.");
}
} catch (e) {
Log.print("❌ Socket Navigation Error: $e");
}
}
void _startHeartbeat() {
_socketHeartbeat?.cancel();
_socketHeartbeat = Timer.periodic(const Duration(seconds: 25), (timer) {
// [Fix 6] تخطي الإرسال إذا كان stream الموقع نشطاً.
// الـ _locSub يرسل update_location عند كل تحرك (كل 5-10 ثوانٍ) تلقائياً.
// الـ heartbeat يكون مفيداً فقط عندما يتوقف الـ stream (الجهاز ثابت أو أوقف الخدمة).
if (_locSub != null) return;
if (socket != null && isSocketConnected && myLocation.latitude != 0) {
emitLocationToSocket(myLocation, heading, speed);
}
});
}
void _stopHeartbeat() {
_socketHeartbeat?.cancel();
}
// In LocationController.dart
void emitLocationToSocket(LatLng pos, double head, double spd) {
String status = box.read(BoxName.statusDriverLocation) ?? 'on';
String? currentRideStatus = box.read(BoxName.rideStatus);
String? storedPassengerId = box.read(BoxName.passengerID);
String? storedRideId = box.read(BoxName.rideId);
// Basic payload
var payload = {
'driver_id': box.read(BoxName.driverID),
'lat': pos.latitude,
'lng': pos.longitude,
'heading': head,
'speed': spd * 3.6,
'status': status,
'distance': totalDistance,
};
// 🔥 القرار الذكي: حقن بيانات الراكب إذا كان هناك رحلة نشطة في الـ Box 🔥
bool hasActiveRide = (currentRideStatus == 'Begin' ||
currentRideStatus == 'Apply' ||
currentRideStatus == 'Arrived');
if (hasActiveRide && storedPassengerId != null) {
payload['passenger_id'] = storedPassengerId;
payload['ride_id'] = storedRideId;
}
// DebugLog.print to verify
//Log.print('🚀 Emitting Location: $payload');
if (socket != null && socket!.connected) {
socket!.emit('update_location', payload);
}
}
// ===================================================================
// ====== Tracking Logic ======
// ===================================================================
Future<void> startLocationUpdates() async {
_isReady = true;
String currentStatus = box.read(BoxName.statusDriverLocation) ?? 'off';
if (currentStatus == 'blocked') {
stopLocationUpdates();
return;
}
// Start background service
await BackgroundServiceHelper.startService();
if (socket == null || !socket!.connected) {
initSocket();
}
if (_locSub != null) return;
if (await _ensureServiceAndPermission()) {
_subscribeLocationStream();
_startBatchTimers();
}
}
Future<void> _subscribeLocationStream() async {
_locSub?.cancel();
int interval = _isPowerSavingMode ? 10000 : 5000;
await location.enableBackgroundMode(enable: true);
location.changeSettings(
accuracy: LocationAccuracy.navigation,
interval: interval,
distanceFilter: _isPowerSavingMode ? 20 : 10,
);
_locSub = location.onLocationChanged.listen((LocationData loc) async {
if (loc.latitude == null || loc.longitude == null) return;
final now = DateTime.now();
final pos = LatLng(loc.latitude!, loc.longitude!);
myLocation = pos;
speed = loc.speed ?? 0.0;
heading = loc.heading ?? 0.0;
box.write('last_lat', pos.latitude);
box.write('last_lng', pos.longitude);
box.write('last_heading', heading);
if (_lastPosForDistance != null) {
final d = _calculateDistance(_lastPosForDistance!, pos);
if (d > 5.0) totalDistance += d;
}
_lastPosForDistance = pos;
update();
emitLocationToSocket(pos, heading, speed);
if (Get.isRegistered<HomeCaptainController>()) {
final homeCtrl = Get.find<HomeCaptainController>();
if (homeCtrl.isActive &&
homeCtrl.mapHomeCaptainController != null &&
homeCtrl.isHomeMapActive &&
homeCtrl.isMapReadyForCommands) {
homeCtrl.mapHomeCaptainController?.animateCamera(
CameraUpdate.newLatLngZoom(pos, 17.5),
);
}
}
await _saveBehaviorIfMoved(pos, now, currentSpeed: speed);
}, onError: (e) => Log.print('❌ Location Stream Error: $e'));
}
Timer? _socketWatchdogTimer;
Future<void> stopLocationUpdates() async {
Log.print("🛑 Stopping Location Updates...");
_locSub?.cancel();
_locSub = null;
_recordTimer?.cancel();
_uploadBatchTimer?.cancel();
_socketHeartbeat?.cancel();
_socketWatchdogTimer?.cancel();
if (socket != null) {
socket!.clearListeners();
socket!.dispose();
}
if (!Platform.isIOS) {
await BackgroundServiceHelper.stopService();
}
socket = null;
isSocketConnected = false;
_isReady = false;
}
// ===================================================================
// ====== Batch Logic & Helpers ======
// ===================================================================
void _startBatchTimers() {
_recordTimer?.cancel();
_uploadBatchTimer?.cancel();
_socketWatchdogTimer?.cancel();
final recDur =
_isPowerSavingMode ? recordIntervalPowerSave : recordIntervalNormal;
final upDur = _isPowerSavingMode
? uploadBatchIntervalPowerSave
: uploadBatchIntervalNormal;
_recordTimer =
Timer.periodic(recDur, (_) => _recordCurrentLocationToBuffer());
_uploadBatchTimer = Timer.periodic(upDur, (_) => _flushBufferToServer());
// محاولة إعادة الاتصال بالسوكيت إذا انقطع كل 3 ثواني
_socketWatchdogTimer = Timer.periodic(const Duration(seconds: 3), (_) {
if (!isSocketConnected && !_isInitializingSocket) {
Log.print("🔄 Socket Watchdog: Attempting to reconnect socket...");
initSocket();
}
});
}
void _recordCurrentLocationToBuffer() {
if (myLocation.latitude == 0) return;
final now = DateTime.now();
double distFromLast = 0.0;
if (_lastRecordedRealLoc != null) {
distFromLast = _calculateDistance(_lastRecordedRealLoc!, myLocation);
}
bool moved = distFromLast > 10.0;
bool timeForced = _lastRecordedTime == null ||
now.difference(_lastRecordedTime!).inSeconds >= 60;
if ((moved && speed > 0.5) || timeForced) {
_lastRecordedRealLoc = myLocation;
_lastRecordedTime = now;
final point = {
'lat': double.parse(myLocation.latitude.toStringAsFixed(6)),
'lng': double.parse(myLocation.longitude.toStringAsFixed(6)),
'spd': double.parse((speed * 3.6).toStringAsFixed(1)),
'head': int.parse(heading.toStringAsFixed(0)),
'st': box.read(BoxName.statusDriverLocation) ?? 'off',
'ts': now.toIso8601String(),
};
_trackBuffer.add(point);
}
}
Future<void> _flushBufferToServer() async {
if (_trackBuffer.isEmpty) return;
int itemsToTake = _trackBuffer.length > 100 ? 100 : _trackBuffer.length;
List<Map<String, dynamic>> batch = _trackBuffer.sublist(0, itemsToTake);
final String driverId = (box.read(BoxName.driverID) ?? '').toString();
try {
var res = await CRUD().post(
link: '${AppLink.locationServer}/add_batch.php',
payload: {'driver_id': driverId, 'batch_data': jsonEncode(batch)},
);
if (res != 'failure') {
_trackBuffer.removeRange(0, itemsToTake);
} else {
_enforceBufferLimit();
}
} catch (e) {
Log.print('❌ Failed to upload batch: $e');
_enforceBufferLimit();
}
}
void _enforceBufferLimit() {
if (_trackBuffer.length > 500) {
_trackBuffer.removeRange(0, _trackBuffer.length - 500);
Log.print("⚠️ Buffer limit enforced. Removed oldest entries.");
}
}
void _listenToBatteryChanges() async {
_battery.onBatteryStateChanged.listen((state) async {
int level = await _battery.batteryLevel;
bool previousMode = _isPowerSavingMode;
if (level <= powerSaveTriggerLevel) _isPowerSavingMode = true;
if (level >= powerSaveExitLevel) _isPowerSavingMode = false;
if (previousMode != _isPowerSavingMode) {
_startBatchTimers();
_updateLocationSettings();
}
});
}
Future<void> _updateLocationSettings() async {
if (_locSub == null) return;
int interval = _isPowerSavingMode ? 10000 : 5000;
try {
await location.changeSettings(
accuracy: LocationAccuracy.navigation,
interval: interval,
distanceFilter: _isPowerSavingMode ? 20 : 10,
);
Log.print("🔋 Location settings updated. Power Save: $_isPowerSavingMode");
} catch (e) {
Log.print("❌ Failed to update location settings: $e");
}
}
Future<void> _saveBehaviorIfMoved(LatLng pos, DateTime now,
{required double currentSpeed}) async {
final dist =
(_lastSqlLoc == null) ? 999.0 : _calculateDistance(_lastSqlLoc!, pos);
if (dist < 15.0) return;
final accel = _calcAcceleration(currentSpeed, now) ?? 0.0;
_lastSqlLoc = pos;
_behaviorBuffer.add({
'driver_id': (box.read(BoxName.driverID) ?? '').toString(),
'latitude': pos.latitude,
'longitude': pos.longitude,
'acceleration': accel,
'created_at': now.toIso8601String(),
'updated_at': now.toIso8601String(),
});
if (_behaviorBuffer.length >= 10) {
_flushBehaviorBuffer();
}
}
void _flushBehaviorBuffer() {
if (_behaviorBuffer.isEmpty) return;
List<Map<String, dynamic>> batch = List.from(_behaviorBuffer);
_behaviorBuffer.clear();
Future.microtask(() async {
try {
for (var data in batch) {
await sql.insertData(data, TableName.behavior);
}
} catch (e) {
Log.print('SQLite Batch Insert Error: $e');
}
});
}
// استبدال دالة Haversine اليدوية بـ Geolocator في باقي الكود أيضاً
// لأنها تعتمد على C++ في الأندرويد و Obj-C في الآيفون (Native Speed)
double _calculateDistance(LatLng a, LatLng b) {
return geo.Geolocator.distanceBetween(
a.latitude, a.longitude, b.latitude, b.longitude);
}
double? _calcAcceleration(double currentSpeed, DateTime now) {
if (_lastSpeed != null && _lastSpeedAt != null) {
final dt = now.difference(_lastSpeedAt!).inMilliseconds / 1000.0;
if (dt > 0.5) {
final a = (currentSpeed - _lastSpeed!) / dt;
_lastSpeed = currentSpeed;
_lastSpeedAt = now;
return a;
}
}
_lastSpeed = currentSpeed;
_lastSpeedAt = now;
return null;
}
Future<void> _initLocationSettings() async {
if (await _ensureServiceAndPermission()) {
try {
await location.enableBackgroundMode(enable: true);
location.changeSettings(
accuracy: LocationAccuracy.navigation,
interval: 1000,
distanceFilter: 10);
} catch (e) {
Log.print("Warning: $e");
}
}
}
// 🔥🔥 هذه هي الدالة المعدلة التي تستخدم ph.Permission 🔥🔥
Future<bool> _ensureServiceAndPermission() async {
// 1. طلب إذن الإشعارات أولاً باستخدام permission_handler
if (Platform.isAndroid) {
var notificationStatus = await ph.Permission.notification.status;
if (!notificationStatus.isGranted) {
await ph.Permission.notification.request();
}
}
// 2. طلب تفعيل خدمة الموقع (GPS) من بكج location
bool serviceEnabled = await location.serviceEnabled();
if (!serviceEnabled) {
serviceEnabled = await location.requestService();
if (!serviceEnabled) return false;
}
// 3. طلب إذن الموقع الأساسي من بكج location
PermissionStatus permissionGranted = await location.hasPermission();
if (permissionGranted == PermissionStatus.denied) {
permissionGranted = await location.requestPermission();
if (permissionGranted != PermissionStatus.granted) return false;
}
return true;
}
// ... (باقي الكود)
Future<LocationData?> getLocation() async {
try {
if (await _ensureServiceAndPermission()) {
final locData = await location.getLocation();
if (locData != null && locData.latitude != null && locData.longitude != null) {
myLocation = LatLng(locData.latitude!, locData.longitude!);
heading = locData.heading ?? 0.0;
speed = locData.speed ?? 0.0;
box.write('last_lat', myLocation.latitude);
box.write('last_lng', myLocation.longitude);
box.write('last_heading', heading);
update();
if (Get.isRegistered<HomeCaptainController>()) {
final homeCtrl = Get.find<HomeCaptainController>();
if (homeCtrl.mapHomeCaptainController != null &&
homeCtrl.isMapReadyForCommands) {
Log.print("📍 [LocationController] Animating camera to single location update");
homeCtrl.mapHomeCaptainController?.animateCamera(
CameraUpdate.newLatLngZoom(myLocation, 17.5),
);
}
}
}
return locData;
}
} catch (e) {
Log.print('❌ FAILED to get single location: $e');
}
return null;
}
}

View File

@@ -0,0 +1,60 @@
import 'package:get/get.dart';
import 'package:permission_handler/permission_handler.dart';
import '../../constant/box_name.dart';
import '../../main.dart';
import '../../print.dart';
import '../../views/widgets/mydialoug.dart';
import '../auth/captin/login_captin_controller.dart';
class LocationPermissions {
// late Location location;
// Future locationPermissions() async {
// location = Location();
// var permissionStatus = await location.requestPermission();
// if (permissionStatus == PermissionStatus.denied) {
// // The user denied the location permission.
// Get.defaultDialog(title: 'GPS Required Allow !.'.tr, middleText: '');
// return null;
// }
// }
}
Future<void> getPermissionLocation() async {
final PermissionStatus status = await Permission.locationAlways.status;
if (!await Permission.locationAlways.serviceStatus.isEnabled) {
Log.print('status.isGranted: ${status.isGranted}');
// box.write(BoxName.locationPermission, 'true');
await Permission.locationAlways.request();
Get.put(LoginDriverController()).update();
MyDialog().getDialog(
'Enable Location Permission'.tr, // {en:ar}
'Allowing location access will help us display orders near you. Please enable it now.'
.tr, // {en:ar}
() async {
Get.back();
box.write(BoxName.locationPermission, 'true');
await Permission.locationAlways.request();
},
);
}
}
Future<void> getPermissionLocation1() async {
PermissionStatus status = await Permission.locationWhenInUse.request();
if (status.isGranted) {
// After granting when in use, request "always" location permission
status = await Permission.locationAlways.request();
if (status.isGranted) {
print("Background location permission granted");
} else {
print("Background location permission denied");
}
} else {
print("Location permission denied");
await openAppSettings();
}
}

View File

@@ -0,0 +1,157 @@
import 'package:siro_driver/views/home/on_boarding_page.dart';
import 'package:siro_driver/views/widgets/error_snakbar.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:siro_driver/constant/box_name.dart';
import 'package:siro_driver/constant/colors.dart';
import 'package:siro_driver/constant/links.dart';
import 'package:siro_driver/controller/functions/crud.dart';
import 'package:siro_driver/main.dart';
import 'package:siro_driver/views/widgets/elevated_btn.dart';
import 'package:siro_driver/views/widgets/my_textField.dart';
import '../../constant/style.dart';
class LogOutController extends GetxController {
TextEditingController checkTxtController = TextEditingController();
final formKey = GlobalKey<FormState>();
final formKey1 = GlobalKey<FormState>();
final emailTextController = TextEditingController();
Future deleteMyAccountDriver(String id) async {
await CRUD().post(link: AppLink.removeUser, payload: {'id': id}).then(
(value) => Get.snackbar('Deleted'.tr, 'Your Account is Deleted',
backgroundColor: AppColor.redColor));
}
checkBeforeDelete() async {
var res = await CRUD().post(
link: AppLink.deletecaptainAccounr,
payload: {'id': box.read(BoxName.driverID)});
return res['message'][0]['id'];
}
deletecaptainAccount() {
Get.defaultDialog(
backgroundColor: AppColor.yellowColor,
title: 'Are you sure to delete your account?'.tr,
middleText:
'Your data will be erased after 2 weeks\nAnd you will can\'t return to use app after 1 month ',
titleStyle: AppStyle.title,
content: Column(
children: [
Container(
width: Get.width,
decoration: AppStyle.boxDecoration,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'Your data will be erased after 2 weeks\nAnd you will can\'t return to use app after 1 month'
.tr,
style: AppStyle.title.copyWith(color: AppColor.redColor),
),
),
),
const SizedBox(
height: 20,
),
Form(
key: formKey,
child: SizedBox(
width: Get.width,
child: MyTextForm(
controller: checkTxtController,
label: 'Enter Your First Name'.tr,
hint: 'Enter Your First Name'.tr,
type: TextInputType.name,
),
))
],
),
confirm: MyElevatedButton(
title: 'Delete'.tr,
onPressed: () async {
if (checkTxtController.text == (box.read(BoxName.nameDriver))) {
// deletecaptainAccount();
var id = await checkBeforeDelete();
deleteMyAccountDriver(id);
} else {
mySnackeBarError('Your Name is Wrong'.tr);
}
}));
}
Future logOutPassenger() async {
Get.defaultDialog(
title: 'Are you Sure to LogOut?'.tr,
content: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
MyElevatedButton(
title: 'Cancel'.tr,
onPressed: () => Get.back(),
),
ElevatedButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(AppColor.redColor),
),
onPressed: () async {
// box.remove(BoxName.agreeTerms);
await box.erase();
await storage.deleteAll();
Get.offAll(OnBoardingPage());
},
child: Text(
'Sign Out'.tr,
style:
AppStyle.title.copyWith(color: AppColor.secondaryColor),
))
],
));
}
Future logOutCaptain() async {
Get.defaultDialog(
title: 'Are you Sure to LogOut?'.tr,
titleStyle: AppStyle.title,
content: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
MyElevatedButton(
title: 'Cancel'.tr,
onPressed: () => Get.back(),
),
ElevatedButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(AppColor.redColor),
),
onPressed: () async {
// box.remove(BoxName.agreeTerms);
await box.erase();
await storage.deleteAll();
Get.offAll(OnBoardingPage());
},
child: Text(
'Sign Out'.tr,
style:
AppStyle.title.copyWith(color: AppColor.secondaryColor),
))
],
));
}
deletePassengerAccount() async {
if (formKey1.currentState!.validate()) {
if (box.read(BoxName.email).toString() == emailTextController.text) {
await CRUD().post(link: AppLink.passengerRemovedAccountEmail, payload: {
'email': box.read(BoxName.email),
});
} else {
mySnackeBarError(
'Email you inserted is Wrong.'.tr,
);
}
}
}
}

View File

@@ -0,0 +1,48 @@
import 'dart:async';
import 'dart:io';
import 'package:http/http.dart' as http;
import 'net_guard.dart';
typedef BodyEncoder = Future<http.Response> Function();
class HttpRetry {
/// ريتراي لـ network/transient errors فقط.
static Future<http.Response> sendWithRetry(
BodyEncoder send, {
int maxRetries = 3,
Duration baseDelay = const Duration(milliseconds: 400),
Duration timeout = const Duration(seconds: 12),
}) async {
// ✅ Pre-flight check for internet connection
if (!await NetGuard().hasInternet()) {
// Immediately throw a specific exception if there's no internet.
// This avoids pointless retries.
throw const SocketException("No internet connection");
}
int attempt = 0;
while (true) {
attempt++;
try {
final res = await send().timeout(timeout);
return res;
} on TimeoutException catch (_) {
if (attempt >= maxRetries) rethrow;
} on SocketException catch (_) {
if (attempt >= maxRetries) rethrow;
} on HandshakeException catch (_) {
if (attempt >= maxRetries) rethrow;
} on http.ClientException catch (e) {
// مثال: Connection reset by peer
final msg = e.message.toLowerCase();
final transient = msg.contains('connection reset') ||
msg.contains('broken pipe') ||
msg.contains('timed out');
if (!transient || attempt >= maxRetries) rethrow;
}
// backoff: 0.4s, 0.8s, 1.6s
final delay = baseDelay * (1 << (attempt - 1));
await Future.delayed(delay);
}
}
}

View File

@@ -0,0 +1,48 @@
import 'dart:async';
import 'dart:io';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:internet_connection_checker/internet_connection_checker.dart';
class NetGuard {
static final NetGuard _i = NetGuard._();
NetGuard._();
factory NetGuard() => _i;
bool _notified = false;
/// فحص: (أ) فيه شبكة؟ (ب) فيه انترنت؟ (ج) السيرفر نفسه reachable؟
Future<bool> hasInternet({Uri? mustReach}) async {
final connectivity = await Connectivity().checkConnectivity();
if (connectivity == ConnectivityResult.none) return false;
final hasNet =
await InternetConnectionChecker.createInstance().hasConnection;
if (!hasNet) return false;
if (mustReach != null) {
try {
final host = mustReach.host;
final result = await InternetAddress.lookup(host);
if (result.isEmpty || result.first.rawAddress.isEmpty) return false;
// اختباري خفيف عبر TCP (80/443) — 400ms timeout
final port = mustReach.scheme == 'http' ? 80 : 443;
final socket = await Socket.connect(host, port,
timeout: const Duration(seconds: 1));
socket.destroy();
} catch (_) {
return false;
}
}
return true;
}
/// إظهار إشعار مرة واحدة ثم إسكات التكرارات
void notifyOnce(void Function(String title, String msg) show) {
if (_notified) return;
_notified = true;
show('لا يوجد اتصال بالإنترنت', 'تحقق من الشبكة ثم حاول مجددًا.');
// إعادة السماح بعد 15 ثانية
Future.delayed(const Duration(seconds: 15), () => _notified = false);
}
}

View File

@@ -0,0 +1,649 @@
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:image_picker/image_picker.dart';
import 'package:http/http.dart' as http;
import 'package:siro_driver/constant/api_key.dart';
import 'package:siro_driver/constant/colors.dart';
import 'package:siro_driver/constant/info.dart';
import 'package:siro_driver/constant/style.dart';
import 'package:siro_driver/constant/table_names.dart';
import 'package:siro_driver/main.dart';
import 'package:siro_driver/views/widgets/elevated_btn.dart';
import '../../constant/box_name.dart';
import '../../constant/links.dart';
import '../auth/captin/register_captin_controller.dart';
import 'launch.dart';
//
// class TextExtractionController extends GetxController {
// String extractedText = '';
// bool isloading = false;
// File? _scannedImage;
// // Convert the extracted text to JSON
// // Convert the extracted text to JSON
// String getTextAsJSON(String text) {
// final lines = text.split('\n');
// final jsonList = lines.map((line) {
// return {
// 'line_text': line,
// 'num_words': line.trim().split(' ').length,
// };
// }).toList();
//
// final json = {
// 'lines': jsonList,
// 'num_lines': lines.length,
// };
//
// return jsonEncode(json);
// }
//
// // Convert the extracted text to blocks by line
// List<String> getTextBlocks(String text) {
// return text.split('\n');
// }
//
// // Future<void> pickAndExtractText() async {
// // final pickedImage = await ImagePicker().pickImage(
// // source: ImageSource.camera,
// // preferredCameraDevice: CameraDevice.rear,
// // maxHeight: Get.height * .3,
// // maxWidth: Get.width * .8,
// // imageQuality: 99,
// // );
// // if (pickedImage != null) {
// // isloading = true;
// // update();
// // final imagePath = pickedImage.path;
// // final languages = [
// // 'eng',
// // 'ara'
// // ]; // Specify the languages you want to use for text extraction
//
// // try {
// // final text = await FlutterTesseractOcr.extractText(imagePath,
// // language:
// // languages.join('+'), // Combine multiple languages with '+'
// // args: {
// // "psm": "4",
// // "preserve_interword_spaces": "1",
// // // "rectangle": const Rect.fromLTWH(100, 100, 200, 200),
// // } // Additional options if needed
// // );
// // isloading = false;
// // final jsonText = getTextAsJSON(text);
// // final textBlocks = getTextBlocks(text);
// // update();
// // extractedText =
// // textBlocks.toString(); // Convert the extracted text to JSON.
//
// // // Print the JSON to the console.
// // update();
// // } catch (e) {
// // extractedText = '';
// // }
// // }
// // }
// }
// class TextMLGoogleRecognizerController extends GetxController {
// @override
// void onInit() {
// scanText();
// super.onInit();
// }
//
// // The ImagePicker instance
// final ImagePicker _imagePicker = ImagePicker();
//
// // The GoogleMlKit TextRecognizer instance
// final TextRecognizer _textRecognizer = TextRecognizer();
//
// // The scanned text
// String? scannedText;
// String? jsonOutput;
// final List<Map<String, dynamic>> lines = [];
//
// Map decode = {};
//
// Future<void> scanText() async {
// // Pick an image from the camera or gallery
// final XFile? image =
// await _imagePicker.pickImage(source: ImageSource.gallery);
//
// // If no image was picked, return
// if (image == null) {
// return;
// }
//
// // Convert the XFile object to an InputImage object
// final InputImage inputImage = InputImage.fromFile(File(image.path));
//
// // Recognize the text in the image
// final RecognizedText recognizedText =
// await _textRecognizer.processImage(inputImage);
// scannedText = recognizedText.text;
// Map extractedData = {};
// // Extract the scanned text line by line
// for (var i = 0; i < recognizedText.blocks.length; i++) {
// final block = recognizedText.blocks[i];
// for (final line in block.lines) {
// final lineText = line.text;
//
// if (lineText.contains('DL')) {
// final dlNumber = lineText.split('DL')[1].trim();
// extractedData['dl_number'] = dlNumber;
// }
// if (lineText.contains('USA')) {
// final usa = lineText.split('USA')[1].trim();
// extractedData['USA'] = usa;
// }
// if (lineText.contains('DRIVER LICENSE')) {
// final driverl = lineText;
// extractedData['DRIVER_LICENSE'] = driverl;
// }
//
// if (lineText.contains('EXP')) {
// final expiryDate = lineText.split('EXP')[1].trim();
// extractedData['expiry_date'] = expiryDate;
// }
//
// if (lineText.contains('DOB')) {
// final dob = lineText.split('DOB')[1].trim();
// extractedData['dob'] = dob;
// }
//
// if (lineText.contains("LN")) {
// if ((lineText.indexOf("LN") == 0)) {
// final lastName = lineText.split('LN')[1].trim();
// extractedData['lastName'] = lastName;
// }
// }
// if (lineText.contains("FN")) {
// final firstName = lineText.split('FN')[1].trim();
// extractedData['firstName'] = firstName;
// }
// if (lineText.contains("RSTR")) {
// final rstr = lineText.split('RSTR')[1].trim();
// extractedData['rstr'] = rstr;
// }
// if (lineText.contains("CLASS")) {
// final class1 = lineText.split('CLASS')[1].trim();
// extractedData['class'] = class1;
// }
// if (lineText.contains("END")) {
// final end = lineText.split('END')[1].trim();
// extractedData['end'] = end;
// }
// if (lineText.contains("DD")) {
// final dd = lineText.split('DD')[1].trim();
// extractedData['dd'] = dd;
// }
// if (lineText.contains("EYES")) {
// final eyes = lineText.split('EYES')[1].trim();
// extractedData['eyes'] = eyes;
// }
// if (lineText.contains("SEX")) {
// final parts = lineText.split("SEX ")[1];
// extractedData['sex'] = parts[0];
// }
// if (lineText.contains("HAIR")) {
// final hair = lineText.split('HAIR')[1].trim();
// extractedData['hair'] = hair;
// }
//
// if (lineText.contains('STREET') || lineText.contains(',')) {
// final address = lineText;
// extractedData['address'] = address;
// }
//
// // Repeat this process for other relevant data fields
// }
// }
//
// // Convert the list of lines to a JSON string
// jsonOutput = jsonEncode(extractedData);
// decode = jsonDecode(jsonOutput!);
//
// update();
// }
// }
class ScanDocumentsByApi extends GetxController {
bool isLoading = false;
Map<String, dynamic> responseMap = {};
final ImagePicker imagePicker = ImagePicker();
late Uint8List imagePortrait;
late Uint8List imageSignature;
late Uint8List imageDocumentFrontSide;
XFile? image;
XFile? imagePortraitFile;
XFile? imageFace;
late File tempFile;
late String imagePath;
DateTime now = DateTime.now();
late String name;
late String licenseClass;
late String documentNo;
late String address;
late String stateCode;
late String height;
late String sex;
late String postalCode;
late String dob;
late String expireDate;
// ///////////////////////
// late CameraController cameraController;
// late List<CameraDescription> cameras;
// bool isCameraInitialized = false;
// // final TextRecognizer _textRecognizer = TextRecognizer();
// String? scannedText;
// Future<void> initializeCamera(int cameraID) async {
// try {
// cameras = await availableCameras();
// //update();
// cameraController = CameraController(
// cameras[cameraID],
// ResolutionPreset.medium,
// enableAudio: false,
// );
// await cameraController.initialize();
// isCameraInitialized = true;
// update();
// } catch (e) {
// if (e is CameraException) {
// switch (e.code) {
// case 'CameraAccessDenied':
// Get.defaultDialog(
// title: 'Camera Access Denied.'.tr,
// middleText: '',
// confirm:
// MyElevatedButton(title: 'Open Settings', onPressed: () {}),
// );
// break;
// default:
// // Handle other errors here.
// break;
// }
// }
// }
// }
///
Future<void> scanDocumentsByApi() async {
// String? visionApi = await storage.read(key: BoxName.visionApi);
// String? visionApi = AK.visionApi;
// Pick an image from the camera or gallery
image = await imagePicker.pickImage(source: ImageSource.camera); //
// If no image was picked, return
if (image == null) {
return;
}
isLoading = true;
update();
var headers = {'X-BLOBR-KEY': AK.visionApi};
var request = http.MultipartRequest('POST',
Uri.parse('https://api.faceonlive.com/j2y3q25y1b6maif1/api/iddoc'));
request.files.add(await http.MultipartFile.fromPath('image', image!.path));
request.headers.addAll(headers);
http.StreamedResponse response = await request.send();
if (response.statusCode == 200) {
String responseString = await response.stream.bytesToString();
responseMap = jsonDecode(responseString);
var ocrData = responseMap['data']['ocr'];
name = ocrData['name'].toString();
licenseClass = ocrData['dlClass'].toString();
documentNo = ocrData['documentNumber'].toString();
address = ocrData['address'].toString();
height = ocrData['height'].toString();
postalCode = ocrData['addressPostalCode'].toString();
sex = ocrData['sex'].toString();
stateCode = ocrData['addressJurisdictionCode'].toString();
expireDate = ocrData['dateOfExpiry'].toString();
dob = ocrData['dateOfBirth'].toString();
if (responseMap['data'] != null &&
responseMap['data']['image'] != null &&
responseMap['data']['image']['portrait'] != null) {
imagePortrait = base64Decode(responseMap['data']['image']['portrait']);
String tempPath = Directory.systemTemp.path;
tempFile = File('$tempPath/image.jpg');
await tempFile.writeAsBytes(imagePortrait);
imagePath = tempFile.path;
// imagePortraitFile=File(imagePath) ;
update();
} else {
// Handle error or provide a default value
}
if (responseMap['data']['image']['signature'] != null) {
imageSignature =
base64Decode(responseMap['data']['image']['signature']);
} else {
imageSignature = imagePortrait;
// Handle error or provide a default value
}
if (responseMap['data'] != null &&
responseMap['data']['image'] != null &&
responseMap['data']['image']['documentFrontSide'] != null) {
imageDocumentFrontSide =
base64Decode(responseMap['data']['image']['documentFrontSide']);
} else {
// Handle error or provide a default value
}
isLoading = false;
update();
} else {}
}
late int times;
Future checkMatchFaceApi() async {
sql.getAllData(TableName.faceDetectTimes).then((value) {
if (value.isEmpty || value == null) {
sql.insertData({'faceDetectTimes': 1}, TableName.faceDetectTimes);
sql.getAllData(TableName.faceDetectTimes).then((value) {
times = value[0]['faceDetectTimes'];
update();
});
} else {
if (times < 4) {
times++;
matchFaceApi();
sql.updateData(
{'faceDetectTimes': times}, TableName.faceDetectTimes, 1);
} else {
Get.defaultDialog(
barrierDismissible: false,
title: 'You have finished all times '.tr,
titleStyle: AppStyle.title,
middleText: 'if you want help you can email us here'.tr,
middleTextStyle: AppStyle.title,
cancel: MyElevatedButton(
title: 'Thanks'.tr,
kolor: AppColor.greenColor,
onPressed: () => Get.back(),
),
confirm: MyElevatedButton(
title: 'Email Us'.tr,
kolor: AppColor.yellowColor, //
onPressed: () {
launchCommunication('email', 'support@mobile-app.store',
'${'Hi'.tr} ${AppInformation.appName}\n${'I cant register in your app in face detection '.tr}');
Get.back();
},
));
}
}
});
}
Map res = {};
Future matchFaceApi() async {
// String? visionApi = await storage.read(key: BoxName.visionApi);
imageFace = await imagePicker.pickImage(
source: ImageSource.camera,
preferredCameraDevice: CameraDevice.front,
);
// If no image was picked, return
if (image == null) {
return;
}
final imageFile = File(imageFace!.path);
// Uint8List imageBytes = await imageFile.readAsBytes();
var headers = {'X-BLOBR-KEY': AK.visionApi};
var request = http.MultipartRequest(
'POST',
Uri.parse(
'https://api.faceonlive.com/sntzbspfsdupgid1/api/face_compare'));
request.files
.add(await http.MultipartFile.fromPath('image1', imageFile.path));
request.files.add(await http.MultipartFile.fromPath('image2', imagePath));
request.headers.addAll(headers);
http.StreamedResponse response = await request.send();
if (response.statusCode == 200) {
res = jsonDecode(await response.stream.bytesToString());
update();
res['data']['result'].toString().contains('No face detected in image')
? Get.defaultDialog(
barrierDismissible: false,
title: 'No face detected'.tr,
middleText: ''.tr,
titleStyle: AppStyle.title,
confirm: MyElevatedButton(
kolor: AppColor.yellowColor,
title: 'Back'.tr,
onPressed: () {
Get.back();
},
)) //
: Get.defaultDialog(
// barrierDismissible: false,
title: 'Image detecting result is '.tr,
titleStyle: AppStyle.title,
content: Column(
children: [
Text(
res['data']['result'].toString(),
style: res['data']['result'].toString() == 'Different'
? AppStyle.title.copyWith(color: AppColor.redColor)
: AppStyle.title.copyWith(color: AppColor.greenColor),
),
res['data']['result'].toString() == 'Different'
? Text(
'${'Be sure for take accurate images please\nYou have'.tr} $times ${'from 3 times Take Attention'.tr}',
style: AppStyle.title,
)
: Text(
'image verified'.tr,
style: AppStyle.title,
)
],
),
confirm: res['data']['result'].toString() == 'Different'
? MyElevatedButton(
title: 'Back'.tr,
onPressed: () => Get.back(),
kolor: AppColor.redColor,
)
: MyElevatedButton(
title: 'Next'.tr,
onPressed: () async {
RegisterCaptainController registerCaptainController =
Get.put(RegisterCaptainController());
await registerCaptainController.register();
await registerCaptainController.addLisence();
await uploadImagePortrate();
// Get.to(() => CarLicensePage());
},
// {
// await uploadImage(
// tempFile, AppLink.uploadImagePortrate);
// Get.to(() => CarLicensePage());
// },
kolor: AppColor.greenColor,
));
} else {}
}
Future<String> uploadImagePortrate() async {
isLoading = true;
update();
final String token = box.read(BoxName.jwt)?.toString().split(AppInformation.addd)[0] ?? '';
final String fingerPrint = box.read(BoxName.deviceFingerprint)?.toString() ?? '';
var request = http.MultipartRequest(
'POST',
Uri.parse(AppLink.uploadImagePortrate),
);
request.files.add(
http.MultipartFile.fromBytes('image', imagePortrait),
);
request.headers.addAll({
'Authorization': 'Bearer $token',
'X-Device-FP': fingerPrint,
});
request.fields['driverID'] = box.read(BoxName.driverID).toString();
var response = await request.send();
var responseData = await response.stream.toBytes();
var responseString = String.fromCharCodes(responseData);
isLoading = false;
update();
return responseString;
}
@override
void onInit() {
// scanDocumentsByApi();
// initializeCamera(0);
sql.getAllData(TableName.faceDetectTimes).then((value) {
if (value.isEmpty) {
times = 0;
update();
// sql.insertData({'faceDetectTimes': 1}, TableName.faceDetectTimes);
} else {
times = value[0]['faceDetectTimes'];
}
});
super.onInit();
}
}
// class PassportDataExtractor extends GetxController {
// @override
// void onInit() {
// extractPassportData();
// super.onInit();
// }
//
// final ImagePicker _imagePicker = ImagePicker();
// late final XFile? image;
// final TextRecognizer _textRecognizer = TextRecognizer();
//
// Future<Map<String, dynamic>> extractPassportData() async {
// image = await _imagePicker.pickImage(source: ImageSource.gallery);
// update();
// if (image == null) {
// throw Exception('No image picked');
// }
//
// final InputImage inputImage = InputImage.fromFile(File(image!.path));
// final RecognizedText recognisedText =
// await _textRecognizer.processImage(inputImage);
//
// final Map<String, dynamic> extractedData = {};
// final List<Map<String, dynamic>> extractedTextWithCoordinates = [];
//
// for (TextBlock block in recognisedText.blocks) {
// for (TextLine line in block.lines) {
// final String lineText = line.text;
// final Rect lineBoundingBox = line.boundingBox!;
//
// extractedTextWithCoordinates.add({
// 'text': lineText,
// 'boundingBox': {
// 'left': lineBoundingBox.left,
// 'top': lineBoundingBox.top,
// 'width': lineBoundingBox.width,
// 'height': lineBoundingBox.height,
// },
// });
//
// // if (lineText.contains('Passport Number')) {
// // final String passportNumber =
// // lineText.split('Passport Number')[1].trim();
// // extractedData['passportNumber'] = passportNumber;
// // }
// // if (lineText.contains('Given Names')) {
// // final String givenNames = lineText.split('Given Names')[1].trim();
// // extractedData['givenNames'] = givenNames;
// // }
// // if (lineText.contains('Surname')) {
// // final String surname = lineText.split('Surname')[1].trim();
// // extractedData['surname'] = surname;
// // }
// // if (lineText.contains('Nationality')) {
// // final String nationality = lineText.split('Nationality')[1].trim();
// // extractedData['nationality'] = nationality;
// // }
// // if (lineText.contains('Date of Birth')) {
// // final String dob = lineText.split('Date of Birth')[1].trim();
// // extractedData['dateOfBirth'] = dob;
// // }
// // Add more field extraction conditions as needed
// }
// }
//
// extractedData['extractedTextWithCoordinates'] =
// extractedTextWithCoordinates;
// return extractedData;
// }
// }
//
// class PassportDataController extends GetxController {
// PassportDataExtractor passportDataExtractor = PassportDataExtractor();
// List<Map<String, dynamic>> extractedTextWithCoordinates = [];
//
// Future<void> extractDataAndDrawBoundingBoxes() async {
// try {
// Map<String, dynamic> extractedData =
// await passportDataExtractor.extractPassportData();
// extractedTextWithCoordinates =
// extractedData['extractedTextWithCoordinates'];
// update(); // Notify GetX that the state has changed
// } catch (e) {
// }
// }
// }
//
// class BoundingBoxPainter extends CustomPainter {
// final List<Map<String, dynamic>> boundingBoxes;
//
// BoundingBoxPainter(this.boundingBoxes);
//
// @override
// void paint(Canvas canvas, Size size) {
// final Paint paint = Paint()
// ..color = Colors.red
// ..style = PaintingStyle.stroke
// ..strokeWidth = 2.0;
//
// for (Map<String, dynamic> boundingBox in boundingBoxes) {
// double left = boundingBox['left'];
// double top = boundingBox['top'];
// double width = boundingBox['width'];
// double height = boundingBox['height'];
//
// Rect rect = Rect.fromLTWH(left, top, width, height);
// canvas.drawRect(rect, paint);
// }
// }
//
// @override
// bool shouldRepaint(covariant CustomPainter oldDelegate) {
// return false;
// }
// }

View File

@@ -0,0 +1,114 @@
import 'dart:io';
import 'package:siro_driver/constant/links.dart';
import 'package:siro_driver/controller/functions/crud.dart';
import 'package:siro_driver/views/widgets/mydialoug.dart';
import 'package:flutter/material.dart';
import 'package:flutter_confetti/flutter_confetti.dart';
import 'package:flutter_overlay_window/flutter_overlay_window.dart';
import 'package:get/get.dart';
import 'package:location/location.dart';
import '../../constant/box_name.dart';
import '../../main.dart';
import '../auth/captin/login_captin_controller.dart';
import '../home/payment/captain_wallet_controller.dart';
Future<void> getPermissionOverlay() async {
if (Platform.isAndroid) {
final bool status = await FlutterOverlayWindow.isPermissionGranted();
if (status == false) {
MyDialog().getDialog(
'Allow overlay permission'.tr,
'To display orders instantly, please grant permission to draw over other apps.'
.tr,
() async {
Get.back();
await FlutterOverlayWindow.requestPermission();
},
);
}
}
}
Future<void> showDriverGiftClaim(BuildContext context) async {
if (box.read(BoxName.is_claimed).toString() == '0' ||
box.read(BoxName.is_claimed) == null) {
MyDialog().getDialog(
'You have gift 300 SYP'.tr, 'This for new registration'.tr, () async {
Get.back();
var res = await CRUD().post(link: AppLink.updateDriverClaim, payload: {
'driverId': box.read(BoxName.driverID),
});
if (res != 'failure') {
Get.find<CaptainWalletController>()
.addDriverWallet('new driver', '300', '300');
Confetti.launch(
context,
options:
const ConfettiOptions(particleCount: 100, spread: 70, y: 0.6),
);
box.write(BoxName.is_claimed, '1');
}
});
}
}
Future<void> closeOverlayIfFound() async {
if (Platform.isAndroid) {
bool isOverlayActive = await FlutterOverlayWindow.isActive();
if (isOverlayActive) {
await FlutterOverlayWindow.closeOverlay();
}
}
}
final location = Location();
Future<void> getLocationPermission() async {
bool serviceEnabled;
PermissionStatus permissionGranted;
// Check if location services are enabled
serviceEnabled = await location.serviceEnabled();
if (!serviceEnabled) {
serviceEnabled = await location.requestService();
if (!serviceEnabled) {
// Location services are still not enabled, handle the error
return;
}
}
// Check if the app has permission to access location
permissionGranted = await location.hasPermission();
if (permissionGranted == PermissionStatus.denied) {
permissionGranted = await location.requestPermission();
if (permissionGranted != PermissionStatus.granted) {
// Location permission is still not granted, handle the error
permissionGranted = await location.requestPermission();
return;
}
}
if (permissionGranted.toString() == 'PermissionStatus.granted') {
box.write(BoxName.locationPermission, 'true');
Get.find<LoginDriverController>().update();
}
// update();
}
Future<void> getOverLay(String myListString) async {
bool isOverlayActive = await FlutterOverlayWindow.isActive();
if (isOverlayActive) {
await FlutterOverlayWindow.closeOverlay();
}
await FlutterOverlayWindow.showOverlay(
enableDrag: true,
flag: OverlayFlag.focusPointer,
visibility: NotificationVisibility.visibilityPublic,
positionGravity: PositionGravity.auto,
height: 700,
width: WindowSize.matchParent,
startPosition: const OverlayPosition(0, -150),
);
await FlutterOverlayWindow.shareData(myListString);
}

View File

@@ -0,0 +1,361 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:ui';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:jailbreak_root_detection/jailbreak_root_detection.dart';
import 'package:siro_driver/constant/box_name.dart';
import 'package:siro_driver/constant/colors.dart';
import 'package:siro_driver/constant/links.dart';
import 'package:siro_driver/controller/functions/crud.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:url_launcher/url_launcher.dart';
import '../../constant/info.dart';
import '../../main.dart';
import '../../print.dart';
import 'encrypt_decrypt.dart';
Future<void> checkForUpdate(BuildContext context) async {
final packageInfo = await PackageInfo.fromPlatform();
final currentVersion = packageInfo.buildNumber;
final version = packageInfo.version;
Log.print('version: $version');
print('currentVersion is : $currentVersion');
// Fetch the latest version from your server
String latestVersion = await getPackageInfo();
box.write(BoxName.packagInfo, version);
if (latestVersion.isNotEmpty && latestVersion != currentVersion) {
showUpdateDialog(context);
}
}
Future<String> getPackageInfo() async {
final response = await CRUD().get(link: AppLink.packageInfo, payload: {
"platform": Platform.isAndroid ? 'android' : 'ios',
"appName": AppInformation.appVersion,
});
if (response != 'failure') {
return jsonDecode(response)['message'][0]['version'];
}
return '';
}
void showUpdateDialog(BuildContext context) {
final String storeUrl = Platform.isAndroid
? 'https://play.google.com/store/apps/details?id=com.intaleq_driver'
: 'https://apps.apple.com/jo/app/intaleq-driver/id6482995159';
showGeneralDialog(
context: context,
barrierDismissible: false,
barrierColor: Colors.black.withOpacity(0.5),
pageBuilder: (_, __, ___) {
return BackdropFilter(
filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5),
child: Center(
child: AlertDialog(
// Using AlertDialog for a more Material Design look
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(16)), // More rounded corners
elevation: 4, // Add a bit more elevation
contentPadding: EdgeInsets.zero, // Remove default content padding
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: const EdgeInsets.only(top: 20.0),
child: Image.asset(
'assets/images/logo.png',
height: 72, // Slightly larger logo
width: 72,
),
),
const SizedBox(height: 16),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24.0),
child: Text(
'Update Available'.tr,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.titleLarge?.copyWith(
// Use theme's title style
fontWeight: FontWeight.bold,
),
),
),
Padding(
padding: const EdgeInsets.all(24.0),
child: Text(
'A new version of the app is available. Please update to the latest version.'
.tr, // More encouraging message
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
// Use theme's body style
color: Colors.black87,
),
),
),
const Divider(height: 0),
Row(
children: [
Expanded(
child: TextButton(
// Using TextButton for "Cancel"
onPressed: () => Navigator.pop(context),
style: TextButton.styleFrom(
foregroundColor: Colors.grey,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(16),
),
),
),
child: Text('Cancel'.tr),
),
),
const SizedBox(
height: 48,
child: VerticalDivider(width: 0), // Using VerticalDivider
),
Expanded(
child: ElevatedButton(
// Using ElevatedButton for "Update"
onPressed: () async {
if (await canLaunchUrl(Uri.parse(storeUrl))) {
await launchUrl(Uri.parse(storeUrl));
}
if (context.mounted) Navigator.pop(context);
},
style: ElevatedButton.styleFrom(
backgroundColor: AppColor
.primaryColor, // Use theme's primary color
foregroundColor: Theme.of(context)
.colorScheme
.onPrimary, // Use theme's onPrimary color
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
bottomRight: Radius.circular(16),
),
),
),
child: Text('Update'.tr),
),
),
],
),
],
),
),
),
);
},
transitionBuilder: (_, animation, __, child) {
return ScaleTransition(
scale: CurvedAnimation(
parent: animation,
curve: Curves.easeOutCubic, // More natural curve
),
child: child,
);
},
);
}
class DeviceHelper {
static Future<String> getDeviceFingerprint() async {
await EncryptionHelper.initialize();
final DeviceInfoPlugin deviceInfoPlugin = DeviceInfoPlugin();
var deviceData;
try {
if (Platform.isAndroid) {
AndroidDeviceInfo androidInfo = await deviceInfoPlugin.androidInfo;
deviceData = androidInfo.toMap();
} else if (Platform.isIOS) {
IosDeviceInfo iosInfo = await deviceInfoPlugin.iosInfo;
deviceData = iosInfo.toMap();
} else {
throw UnsupportedError('Unsupported platform');
}
final String deviceId = Platform.isAndroid
? deviceData['id'] ?? deviceData['androidId'] ?? deviceData['fingerprint'] ?? 'unknown'
: deviceData['identifierForVendor'] ?? 'unknown';
final String deviceModel = deviceData['model'] ?? 'unknown';
final String fingerprint =
EncryptionHelper.instance.encryptData('${deviceId}_$deviceModel');
box.write(BoxName.deviceFingerprint, fingerprint);
return (fingerprint);
} catch (e) {
debugPrint('Error generating device fingerprint: $e');
throw Exception('Failed to generate device fingerprint: $e');
}
}
}
class SecurityHelper {
/// Performs security checks and handles potential risks
static Future<void> performSecurityChecks() async {
bool isNotTrust = false;
bool isJailBroken = false;
bool isRealDevice = true;
bool isOnExternalStorage = false;
bool checkForIssues = false;
bool isDevMode = false;
bool isTampered = false;
String bundleId = "";
try {
isNotTrust = await JailbreakRootDetection.instance.isNotTrust;
isJailBroken = await JailbreakRootDetection.instance.isJailBroken;
isRealDevice = await JailbreakRootDetection.instance.isRealDevice;
// This method is only relevant/implemented for Android
if (Platform.isAndroid) {
isOnExternalStorage =
await JailbreakRootDetection.instance.isOnExternalStorage;
}
List<JailbreakIssue> issues =
await JailbreakRootDetection.instance.checkForIssues;
checkForIssues = issues.isNotEmpty;
isDevMode = await JailbreakRootDetection.instance.isDevMode;
// Get Bundle ID
PackageInfo packageInfo = await PackageInfo.fromPlatform();
bundleId = packageInfo.packageName;
if (bundleId.isNotEmpty) {
isTampered = await JailbreakRootDetection.instance.isTampered(bundleId);
}
} catch (e) {
debugPrint("Error during security checks: $e");
// Consider handling specific exceptions, not just general errors.
}
// Save values to storage (using GetStorage)
await box.write('isNotTrust', isNotTrust); // Use await for write operations
await box.write('isTampered', isTampered); // Use await
await box.write('isJailBroken', isJailBroken); // Use await
// debugPrint("Security Check Results:");
// debugPrint("isNotTrust: $isNotTrust");
// debugPrint("isJailBroken: $isJailBroken");
// debugPrint("isRealDevice: $isRealDevice");
// debugPrint("isOnExternalStorage: $isOnExternalStorage");
// debugPrint("checkForIssues: $checkForIssues");
// debugPrint("isDevMode: $isDevMode");
// debugPrint("isTampered: $isTampered");
// debugPrint("Bundle ID: $bundleId"); // Print the bundle ID
// Check for security risks and potentially show a warning
if (isJailBroken || isRealDevice == false || isTampered) {
// print("security_warning".tr); //using easy_localization
// Use a more robust approach to show a warning, like a dialog:
_showSecurityWarning();
} else {
box.write(BoxName.security_check, 'passed');
}
}
/// Deletes all app data
static Future<void> clearAllData() async {
//await storage.deleteAll(); // What's 'storage'? Be specific. Likely GetStorage as well.
await box.erase(); // Clear GetStorage data
exit(0); // This will terminate the app. Be VERY careful with this.
}
// static void _showSecurityWarning() {
// // Show a dialog, navigate to an error screen, etc.
// // Example using Get.dialog (if you use GetX):
//
// Get.dialog(
// AlertDialog(
// title: Text("Security Warning".tr), // Or use localized string
// content: Text(
// "Potential security risks detected. The application may not function correctly."
// .tr), //Or use localized string
// actions: [
// TextButton(
// onPressed: () async {
// await storage.deleteAll();
// await box.erase();
// Get.back(); // Close the dialog
// // Or, if you really must, exit the app (but give the user a chance!)
// exit(0);
// },
// child: Text("OK"), // Or use a localized string
// ),
// ],
// ),
// barrierDismissible: false, // Prevent closing by tapping outside
// );
// }
static void _showSecurityWarning() {
// Use an RxInt to track the remaining seconds. This is the KEY!
RxInt secondsRemaining = 10.obs;
Get.dialog(
CupertinoAlertDialog(
title: Text("Security Warning".tr),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
Obx(() => Text(
"Potential security risks detected. The application will close in @seconds seconds."
.trParams({
// Use trParams for placeholders
'seconds': secondsRemaining.value.toString(),
}),
// Wrap the Text widget in Obx
)),
SizedBox(height: 24), // More spacing before the progress bar
Obx(() => SizedBox(
width: double.infinity, // Make progress bar full width
child: CupertinoActivityIndicator(
// in case of loading
radius: 15,
animating: true,
))),
SizedBox(height: 8),
Obx(() => ClipRRect(
borderRadius: BorderRadius.circular(8), // Rounded corners
child: LinearProgressIndicator(
value: secondsRemaining.value / 10,
backgroundColor: Colors.grey.shade300, // Lighter background
valueColor: AlwaysStoppedAnimation<Color>(
CupertinoColors.systemRed), // iOS-style red
minHeight: 8, // Slightly thicker progress bar
),
)),
],
),
),
barrierDismissible: false,
);
Timer.periodic(Duration(seconds: 1), (timer) {
secondsRemaining.value--;
if (secondsRemaining.value <= 0) {
timer.cancel();
// Get.back();
_clearDataAndExit();
}
});
}
static Future<void> _clearDataAndExit() async {
await storage.deleteAll();
await box.erase();
exit(0); // Exit the app
print('exit');
}
}

View File

@@ -0,0 +1,43 @@
import 'dart:io';
class PerformanceTester {
/// ✅ فحص سرعة الكتابة إلى التخزين (Storage Write Speed) بوحدة MB/s
static Future<double> testStorageWriteSpeed() async {
try {
final tempDir = Directory.systemTemp;
final testFile = File('${tempDir.path}/speed_test.txt');
final data = List<int>.filled(1024 * 1024 * 5, 0); // 5MB
final stopwatch = Stopwatch()..start();
await testFile.writeAsBytes(data, flush: true);
stopwatch.stop();
await testFile.delete();
double seconds = stopwatch.elapsedMilliseconds / 1000;
if (seconds == 0) seconds = 0.001;
final speed = 5 / seconds;
return double.parse(speed.toStringAsFixed(2));
} catch (e) {
print("❌ Storage write error: $e");
return 0.0;
}
}
/// ✅ فحص سرعة المعالج (CPU Compute Speed) بوحدة الثواني
static Future<double> testCPUSpeed() async {
try {
final stopwatch = Stopwatch()..start();
double x = 0;
for (int i = 0; i < 100000000; i++) {
x += i * 0.000001;
}
stopwatch.stop();
return stopwatch.elapsedMilliseconds / 1000.0;
} catch (e) {
print("❌ CPU compute error: $e");
return 999.0;
}
}
}

View File

@@ -0,0 +1,8 @@
// import 'package:ride/controller/functions/crud.dart';
// class RemoveAccount {
// void removeAccount()async{
// var res=await CRUD().post(link: link)
// }
// }

View File

@@ -0,0 +1,25 @@
// import 'package:credit_card_scanner/credit_card_scanner.dart';
// import 'package:get/get.dart';
//
// class ScanIdCard extends GetxController {
// CardDetails? _cardDetails;
// CardScanOptions scanOptions = const CardScanOptions(
// scanCardHolderName: true,
// enableDebugLogs: true,
// validCardsToScanBeforeFinishingScan: 5,
// possibleCardHolderNamePositions: [
// CardHolderNameScanPosition.aboveCardNumber,
// ],
// );
//
// Future<void> scanCard() async {
// final CardDetails? cardDetails =
// await CardScanner.scanCard(scanOptions: scanOptions);
// if (cardDetails == null) {
// return;
// }
//
// _cardDetails = cardDetails;
// update();
// }
// }

View File

@@ -0,0 +1,100 @@
import 'dart:convert';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:secure_string_operations/secure_string_operations.dart';
import 'package:siro_driver/controller/auth/captin/login_captin_controller.dart';
import 'package:siro_driver/controller/functions/encrypt_decrypt.dart';
import '../../constant/box_name.dart';
import '../../constant/char_map.dart';
import '../../constant/info.dart';
import '../../constant/links.dart';
import '../../main.dart';
import '../../print.dart';
import 'crud.dart';
class SecureStorage {
final FlutterSecureStorage _storage = const FlutterSecureStorage();
void saveData(String key, value) async {
await _storage.write(key: key, value: value);
}
Future<String?> readData(String boxName) async {
final String? value = await _storage.read(key: boxName);
return value;
}
}
const List<String> keysToFetch = [
'serverPHP',
'seferAlexandriaServer',
'seferPaymentServer',
'seferCairoServer',
'seferGizaServer',
];
class AppInitializer {
List<Map<String, dynamic>> links = [];
Future<void> initializeApp() async {
if (box.read(BoxName.jwt) == null) {
await LoginDriverController().getJWT();
} else {
String token = r(box.read(BoxName.jwt)).toString().split(AppInformation.addd)[0];
bool isTokenValid = false;
try {
final parts = token.split('.');
if (parts.length == 3) {
String payload = parts[1];
switch (payload.length % 4) {
case 2: payload += '=='; break;
case 3: payload += '='; break;
}
final decoded = jsonDecode(utf8.decode(base64Url.decode(payload)));
final exp = decoded['exp'];
if (exp != null) {
isTokenValid = DateTime.now().millisecondsSinceEpoch < (exp * 1000 - 30000);
}
}
} catch (_) {}
if (!isTokenValid) {
await LoginDriverController().getJWT();
}
}
// await getKey();
}
Future<void> getAIKey(String key1) async {
var res =
await CRUD().get(link: AppLink.getapiKey, payload: {"keyName": key1});
if (res != 'failure') {
var d = jsonDecode(res)['message'];
final rawValue = d[key1].toString();
// ✅ اكتبها في storage
await storage.write(key: key1, value: rawValue);
await Future.delayed(Duration.zero);
}
}
Future<void> getKey() async {
try {
var res =
await CRUD().get(link: AppLink.getLocationAreaLinks, payload: {});
if (res != 'failure') {
links = List<Map<String, dynamic>>.from(jsonDecode(res)['message']);
await box.write(BoxName.locationName, links);
await box.write(BoxName.basicLink, (links[0]['server_link']));
await box.write(links[2]['name'], (links[2]['server_link']));
await box.write(links[1]['name'], (links[3]['server_link']));
await box.write(links[3]['name'], (links[1]['server_link']));
await box.write(BoxName.paymentLink, (links[4]['server_link']));
}
} catch (e) {}
}
}

View File

@@ -0,0 +1,51 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import '../../constant/box_name.dart';
import '../../main.dart';
class SecurityChecks {
static const platform = MethodChannel(
'com.intaleq_driver/security'); // Choose a unique channel name
static Future<bool> isDeviceCompromised() async {
try {
final bool result = await platform
.invokeMethod('isNativeRooted'); // Invoke the native method
return result;
} on PlatformException catch (e) {
print("Failed to check security status: ${e.message}");
return true; // Treat platform errors as a compromised device (for safety)
}
}
static isDeviceRootedFromNative(BuildContext context) async {
bool compromised = await isDeviceCompromised();
if (compromised) {
showDialog(
barrierDismissible: false,
context: context,
builder: (context) => AlertDialog(
title: Text("Security Warning".tr),
content: Text(
"Your device appears to be compromised. The app will now close."
.tr),
actions: [
TextButton(
onPressed: () {
SystemNavigator.pop(); // Close the app
},
child: Text("OK"),
),
],
),
);
} else {
box.write(BoxName.security_check, 'passed');
// Continue with normal app flow
print("Device is secure.");
}
}
}

View File

@@ -0,0 +1,104 @@
import 'dart:convert';
import 'package:siro_driver/constant/api_key.dart';
import 'package:siro_driver/constant/box_name.dart';
import 'package:siro_driver/constant/info.dart';
import 'package:siro_driver/constant/links.dart';
import 'package:siro_driver/controller/auth/captin/register_captin_controller.dart';
import 'package:siro_driver/controller/functions/crud.dart';
import 'package:siro_driver/main.dart';
import 'package:siro_driver/views/widgets/elevated_btn.dart';
import 'package:get/get.dart';
import 'package:http/http.dart' as http;
import '../auth/captin/login_captin_controller.dart';
import 'encrypt_decrypt.dart';
class SmsEgyptController extends GetxController {
var headers = {'Content-Type': 'application/json'};
Future<String> getSender() async {
var res = await CRUD().get(link: AppLink.getSender, payload: {});
if (res != 'failure') {
var d = jsonDecode(res)['message'][0]['senderId'].toString();
return d;
} else {
return "Sefer Egy";
}
}
Future<dynamic> sendSmsEgypt(String phone) async {
String sender = await getSender();
var body = jsonEncode({"receiver": "2$phone"});
var res = await http.post(
Uri.parse(AppLink.sendSms),
body: body,
headers: headers,
);
if (jsonDecode(res.body)['message'].toString() != "Success") {
await CRUD().post(link: AppLink.updatePhoneInvalidSMS, payload: {
"phone_number":
('+2${Get.find<RegisterCaptainController>().phoneController.text}')
});
box.write(BoxName.phoneDriver,
('+2${Get.find<RegisterCaptainController>().phoneController.text}'));
box.write(BoxName.phoneVerified, '1');
await Get.put(LoginDriverController()).loginWithGoogleCredential(
box.read(BoxName.driverID).toString(),
(box.read(BoxName.emailDriver).toString()),
);
} else {
Get.defaultDialog(
title: 'You will receive code in sms message'.tr,
middleText: '',
confirm: MyElevatedButton(
title: 'OK'.tr,
onPressed: () {
Get.back();
}));
}
}
Future checkCredit(String phone, otp) async {
var res = await http.post(
Uri.parse(AppLink.checkCredit),
body: {
"username": AppInformation.appName,
"password": AK.smsPasswordEgypt,
},
headers: headers,
);
}
Future sendSmsWithValidaty(String phone, otp) async {
var res = await http.post(
Uri.parse(AppLink.checkCredit),
body: {
"username": 'Sefer',
"password": AK.smsPasswordEgypt,
"message": "This is an example SMS message.",
"language": box.read(BoxName.lang) == 'en' ? "e" : 'r',
"sender": "Sefer", //"Kazumi", // todo add sefer sender name
"receiver": "2$phone",
"validity": "10",
"StartTime": DateTime.now().toString() // "1/1/2024 10:00:00"
},
headers: headers,
);
}
Future sendSmsStatus(String smsid) async {
var res = await http.post(
Uri.parse(AppLink.checkCredit),
body: {
"username": AppInformation.appName,
"password": AK.smsPasswordEgypt,
"smsid": smsid //"00b77dfc-5b8f-474d-9def-9f0158b70f98"
},
headers: headers,
);
}
}

View File

@@ -0,0 +1,35 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:siro_driver/constant/colors.dart';
import 'package:siro_driver/constant/style.dart';
class Toast {
static void show(BuildContext context, String message, Color color) {
final snackBar = SnackBar(
clipBehavior: Clip.antiAliasWithSaveLayer,
backgroundColor: color,
elevation: 3,
content: Text(
message,
style: AppStyle.title.copyWith(color: AppColor.secondaryColor),
),
behavior: SnackBarBehavior.floating,
animation: const AlwaysStoppedAnimation(1.0),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0), // Custom border radius
),
width: Get.width * .8,
// shape: const StadiumBorder(
// side: BorderSide(
// color: AppColor.secondaryColor,
// width: 1.0,
// style: BorderStyle.solid,
// )),
duration: const Duration(seconds: 2),
);
ScaffoldMessenger.of(context).showSnackBar(
snackBar,
);
}
}

View File

@@ -0,0 +1,93 @@
import 'dart:io';
import 'package:flutter_tts/flutter_tts.dart';
import 'package:get/get.dart';
import 'package:siro_driver/constant/box_name.dart';
import 'package:siro_driver/main.dart';
class TextToSpeechController extends GetxController {
final FlutterTts flutterTts = FlutterTts();
bool isSpeaking = false;
@override
void onInit() {
super.onInit();
initTts();
}
@override
void onClose() {
flutterTts.stop();
super.onClose();
}
// --- 1. تهيئة المحرك بإعدادات قوية للملاحة ---
Future<void> initTts() async {
try {
// جلب اللغة المحفوظة أو استخدام العربية كافتراضي
String lang = box.read(BoxName.lang) ?? 'ar-SA';
// تصحيح صيغة اللغة إذا لزم الأمر
if (lang == 'ar') lang = 'ar-SA';
if (lang == 'en') lang = 'en-US';
await flutterTts.setLanguage(lang);
await flutterTts.setSpeechRate(0.5); // سرعة متوسطة وواضحة
await flutterTts.setVolume(1.0);
await flutterTts.setPitch(1.0);
// إعدادات خاصة لضمان عمل الصوت مع الملاحة (خاصة للآيفون)
if (Platform.isIOS) {
await flutterTts
.setIosAudioCategory(IosTextToSpeechAudioCategory.playback, [
IosTextToSpeechAudioCategoryOptions.mixWithOthers,
IosTextToSpeechAudioCategoryOptions.duckOthers
]);
}
// الاستماع لحالة الانتهاء
flutterTts.setCompletionHandler(() {
isSpeaking = false;
update();
});
flutterTts.setStartHandler(() {
isSpeaking = true;
update();
});
} catch (e) {
print("TTS Init Error: $e");
}
}
// --- 2. دالة التحدث (تقاطع الكلام القديم) ---
Future<void> speakText(String text) async {
if (text.isEmpty) return;
try {
// إيقاف أي كلام حالي لضمان نطق التوجيه الجديد فوراً (أهم للملاحة)
await flutterTts.stop();
var result = await flutterTts.speak(text);
if (result == 1) {
isSpeaking = true;
update();
}
} catch (error) {
// لا تعرض سناك بار هنا لتجنب إزعاج السائق أثناء القيادة
print('Failed to speak text: $error');
}
}
// --- 3. دالة الإيقاف (ضرورية لزر الكتم) ---
Future<void> stop() async {
try {
var result = await flutterTts.stop();
if (result == 1) {
isSpeaking = false;
update();
}
} catch (e) {
print("Error stopping TTS: $e");
}
}
}

View File

@@ -0,0 +1,22 @@
// import 'package:ride/constant/credential.dart';
// import 'package:twilio_flutter/twilio_flutter.dart';
//
// class TwilioSMS {
// TwilioFlutter twilioFlutter = TwilioFlutter(
// accountSid: AppCredintials.accountSIDTwillo,
// authToken: AppCredintials.authTokenTwillo,
// twilioNumber: '+962 7 9858 3052');
//
// Future<void> sendSMS({
// required String recipientPhoneNumber,
// required String message,
// }) async {
// try {
// await twilioFlutter.sendSMS(
// toNumber: recipientPhoneNumber,
// messageBody: message,
// );
// } catch (e) {
// }
// }
// }

View File

@@ -0,0 +1,537 @@
import 'dart:convert';
import 'dart:io';
import 'package:siro_driver/constant/api_key.dart';
import 'package:siro_driver/views/widgets/error_snakbar.dart';
import 'package:get/get.dart';
import 'package:http/http.dart' as http;
import 'package:image_cropper/image_cropper.dart';
import 'package:image_picker/image_picker.dart';
import 'package:path/path.dart';
import 'package:image/image.dart' as img;
import 'package:flutter_image_compress/flutter_image_compress.dart';
import 'package:path_provider/path_provider.dart' as path_provider;
import '../../constant/box_name.dart';
import '../../constant/colors.dart';
import '../../constant/info.dart';
import '../../main.dart';
import '../../print.dart';
import 'encrypt_decrypt.dart';
class ImageController extends GetxController {
File? myImage;
bool isloading = false;
CroppedFile? croppedFile;
final picker = ImagePicker();
var image;
Future<img.Image> detectAndCropDocument(File imageFile) async {
img.Image? image = img.decodeImage(await imageFile.readAsBytes());
if (image == null) throw Exception('Unable to decode image');
int left = image.width, top = image.height, right = 0, bottom = 0;
// Threshold for considering a pixel as part of the document (adjust as needed)
const int threshold = 240;
for (int y = 0; y < image.height; y++) {
for (int x = 0; x < image.width; x++) {
final pixel = image.getPixel(x, y);
final luminance = img.getLuminance(pixel);
if (luminance < threshold) {
left = x < left ? x : left;
top = y < top ? y : top;
right = x > right ? x : right;
bottom = y > bottom ? y : bottom;
}
}
}
// Add a small padding
left = (left - 5).clamp(0, image.width);
top = (top - 5).clamp(0, image.height);
right = (right + 5).clamp(0, image.width);
bottom = (bottom + 5).clamp(0, image.height);
return img.copyCrop(image,
x: left, y: top, width: right - left, height: bottom - top);
}
Future<File> rotateImageIfNeeded(File imageFile) async {
img.Image croppedDoc = await detectAndCropDocument(imageFile);
// Check if the document is in portrait orientation
bool isPortrait = croppedDoc.height > croppedDoc.width;
img.Image processedImage;
if (isPortrait) {
// Rotate the image by 90 degrees clockwise
processedImage = img.copyRotate(croppedDoc, angle: 90);
} else {
processedImage = croppedDoc;
}
// Get temporary directory
final tempDir = await path_provider.getTemporaryDirectory();
final tempPath = tempDir.path;
// Create the processed image file
File processedFile = File('$tempPath/processed_image.jpg');
await processedFile.writeAsBytes(img.encodeJpg(processedImage));
return processedFile;
}
Future<File> rotateImage(File imageFile) async {
// Read the image file
img.Image? image = img.decodeImage(await imageFile.readAsBytes());
if (image == null) return imageFile;
// Rotate the image by 90 degrees clockwise
img.Image rotatedImage = img.copyRotate(image, angle: 90);
// Get temporary directory
final tempDir = await path_provider.getTemporaryDirectory();
final tempPath = tempDir.path;
// Create the rotated image file
File rotatedFile = File('$tempPath/rotated_image.jpg');
await rotatedFile.writeAsBytes(img.encodeJpg(rotatedImage));
return rotatedFile;
}
choosImage(String link, String imageType) async {
try {
final pickedImage = await picker.pickImage(
source: ImageSource.camera,
preferredCameraDevice: CameraDevice.rear,
);
if (pickedImage == null) return;
image = File(pickedImage.path);
croppedFile = await ImageCropper().cropImage(
sourcePath: image!.path,
uiSettings: [
AndroidUiSettings(
toolbarTitle: 'Cropper'.tr,
toolbarColor: AppColor.blueColor,
toolbarWidgetColor: AppColor.yellowColor,
initAspectRatio: CropAspectRatioPreset.original,
lockAspectRatio: false,
),
IOSUiSettings(
title: 'Cropper'.tr,
),
],
);
if (croppedFile == null) return;
myImage = File(croppedFile!.path);
isloading = true;
update();
// Rotate the compressed image
File processedImage = await rotateImageIfNeeded(File(croppedFile!.path));
File compressedImage = await compressImage(processedImage);
print('link =$link');
// Log.print('link: ${link}');
//n8u22456
await uploadImage(
compressedImage,
{
'driverID':
box.read(BoxName.driverID) ?? box.read(BoxName.passengerID),
'imageType': imageType,
},
link,
);
} catch (e) {
print('Error in choosImage: $e');
mySnackeBarError('Image Upload Failed'.tr);
// Get.snackbar('Image Upload Failed'.tr, e.toString(),
// backgroundColor: AppColor.primaryColor);
} finally {
isloading = false;
update();
}
}
choosImageNewCAr(String link, String imageType) async {
try {
final pickedImage = await picker.pickImage(
source: ImageSource.camera,
preferredCameraDevice: CameraDevice.rear,
);
if (pickedImage == null) return;
image = File(pickedImage.path);
croppedFile = await ImageCropper().cropImage(
sourcePath: image!.path,
uiSettings: [
AndroidUiSettings(
toolbarTitle: 'Cropper'.tr,
toolbarColor: AppColor.blueColor,
toolbarWidgetColor: AppColor.yellowColor,
initAspectRatio: CropAspectRatioPreset.original,
lockAspectRatio: false,
),
IOSUiSettings(
title: 'Cropper'.tr,
),
],
);
if (croppedFile == null) return;
myImage = File(croppedFile!.path);
isloading = true;
update();
// Rotate the compressed image
File processedImage = await rotateImageIfNeeded(File(croppedFile!.path));
File compressedImage = await compressImage(processedImage);
print('link =$link');
// Log.print('link: ${link}');
//n8u22456
await uploadNewCar(
compressedImage,
{
'driverID': box.read(BoxName.driverID) +
'_' +
DateTime.now().toIso8601String(),
'imageType': imageType,
},
link,
);
} catch (e) {
print('Error in choosImage: $e');
// Get.snackbar('Image Upload Failed'.tr, e.toString(),
// backgroundColor: AppColor.primaryColor);
mySnackeBarError('Image Upload Failed'.tr);
} finally {
isloading = false;
update();
}
}
// choosFaceFromDriverLicense(String link, String imageType) async {
// final pickedImage = await picker.pickImage(
// source: ImageSource.camera,
// preferredCameraDevice: CameraDevice.rear,
// );
// if (pickedImage == null) return;
// image = File(pickedImage.path);
// File? processedImage;
// // For face images, use face detection and cropping
// processedImage = await detectAndCropFace(image!);
// if (processedImage == null) {
// Get.snackbar('Face Detection Failed', 'No face detected in the image.');
// return;
// }
// isloading = true;
// update();
// File compressedImage = await compressImage(processedImage);
// try {
// await uploadImage(
// compressedImage,
// {
// 'driverID': box.read(BoxName.driverID).toString(),
// 'imageType': imageType
// },
// link,
// );
// } catch (e) {
// Get.snackbar('Image Upload Failed'.tr, e.toString(),
// backgroundColor: AppColor.redColor);
// } finally {
// isloading = false;
// update();
// }
// }
choosFace(String link, String imageType) async {
final pickedImage = await picker.pickImage(
source: ImageSource.camera,
preferredCameraDevice: CameraDevice.front,
);
if (pickedImage != null) {
image = File(pickedImage.path);
isloading = true;
update();
// Compress the image
File compressedImage = await compressImage(File(pickedImage.path));
// Save the picked image directly
// File savedImage = File(pickedImage.path);
print('link =$link');
try {
await uploadImage(
compressedImage,
{
'driverID':
box.read(BoxName.driverID) ?? box.read(BoxName.passengerID),
'imageType': imageType
},
link,
);
} catch (e) {
mySnackeBarError('Image Upload Failed'.tr);
// Get.snackbar('Image Upload Failed'.tr, e.toString(),
// backgroundColor: AppColor.redColor);
} finally {
isloading = false;
update();
}
}
}
uploadImage(File file, Map data, String link) async {
final String token = r(box.read(BoxName.jwt)).split(AppInformation.addd)[0];
final String fingerPrint = box.read(BoxName.deviceFingerprint)?.toString() ?? '';
var request = http.MultipartRequest('POST', Uri.parse(link));
Log.print('uploadImage -> $link');
request.headers.addAll({
'Authorization': 'Bearer $token',
'X-Device-FP': fingerPrint,
});
var length = await file.length();
var stream = http.ByteStream(file.openRead());
request.files.add(
http.MultipartFile(
'image',
stream,
length,
filename: '${box.read(BoxName.driverID)}.jpg',
),
);
data.forEach((key, value) {
request.fields[key] = value;
});
var myrequest = await request.send();
var res = await http.Response.fromStream(myrequest);
Log.print('uploadImage response [${res.statusCode}]: ${res.body}');
if (res.statusCode == 200) {
return jsonDecode(res.body);
} else {
throw Exception('Failed to upload image: ${res.statusCode} - ${res.body}');
}
}
uploadNewCar(File file, Map data, String link) async {
final String token = r(box.read(BoxName.jwt)).split(AppInformation.addd)[0];
final String fingerPrint = box.read(BoxName.deviceFingerprint)?.toString() ?? '';
var request = http.MultipartRequest('POST', Uri.parse(link));
request.headers.addAll({
'Authorization': 'Bearer $token',
'X-Device-FP': fingerPrint,
});
var length = await file.length();
var stream = http.ByteStream(file.openRead());
request.files.add(
http.MultipartFile(
'image',
stream,
length,
filename: '${box.read(BoxName.driverID)}.jpg',
),
);
data.forEach((key, value) {
request.fields[key] = value;
});
var myrequest = await request.send();
var res = await http.Response.fromStream(myrequest);
Log.print('uploadNewCar response [${res.statusCode}]: ${res.body}');
if (res.statusCode == 200) {
return jsonDecode(res.body);
} else {
throw Exception('Failed to upload image: ${res.statusCode} - ${res.body}');
}
}
choosImagePicture(String link, String imageType) async {
final pickedImage = await picker.pickImage(
source: ImageSource.gallery,
// preferredCameraDevice: CameraDevice.rear,
// maxHeight: Get.height * .3,
// maxWidth: Get.width * .9,
// imageQuality: 100,
);
image = File(pickedImage!.path);
croppedFile = await ImageCropper().cropImage(
sourcePath: image!.path,
uiSettings: [
AndroidUiSettings(
toolbarTitle: 'Cropper'.tr,
toolbarColor: AppColor.blueColor,
toolbarWidgetColor: AppColor.yellowColor,
initAspectRatio: CropAspectRatioPreset.original,
lockAspectRatio: false),
IOSUiSettings(
title: 'Cropper'.tr,
),
],
);
myImage = File(pickedImage.path);
isloading = true;
update();
// Save the cropped image
// File savedCroppedImage = File(croppedFile!.path);
File compressedImage = await compressImage(File(croppedFile!.path));
print('link =$link');
try {
var response = await uploadImage(
compressedImage,
{'driverID': (box.read(BoxName.driverID)), 'imageType': imageType},
link,
);
// Save the returned URL from the V3 backend to local storage
if (response != null && response['status'] == 'success' && response['message'] != null) {
if (response['message']['file_link'] != null) {
box.write(BoxName.driverPhotoUrl, response['message']['file_link'].toString());
}
}
} catch (e) {
Log.print('e: ${e}');
mySnackeBarError('Image Upload Failed'.tr);
} finally {
isloading = false;
update();
}
}
uploadImagePicture(File file, Map data, String link) async {
final String token = r(box.read(BoxName.jwt)).split(AppInformation.addd)[0];
final String fingerPrint = box.read(BoxName.deviceFingerprint)?.toString() ?? '';
var request = http.MultipartRequest('POST', Uri.parse(link));
request.headers.addAll({
'Authorization': 'Bearer $token',
'X-Device-FP': fingerPrint,
});
var length = await file.length();
var stream = http.ByteStream(file.openRead());
request.files.add(
http.MultipartFile(
'image',
stream,
length,
filename: '${box.read(BoxName.driverID)}.jpg',
),
);
data.forEach((key, value) {
request.fields[key] = value;
});
var myrequest = await request.send();
var res = await http.Response.fromStream(myrequest);
Log.print('uploadImagePicture response [${res.statusCode}]: ${res.body}');
if (res.statusCode == 200) {
return jsonDecode(res.body);
} else {
throw Exception('Failed to upload image: ${res.statusCode} - ${res.body}');
}
}
}
Future<File> compressImage(File file) async {
final dir = await path_provider.getTemporaryDirectory();
final targetPath = "${dir.absolute.path}/temp.jpg";
var result = await FlutterImageCompress.compressAndGetFile(
file.absolute.path,
targetPath,
quality: 70,
minWidth: 1024,
minHeight: 1024,
);
return File(result!.path);
}
// Future<File> detectAndCropFace(File imageFile) async {
// final inputImage = InputImage.fromFilePath(imageFile.path);
// final options = FaceDetectorOptions(
// enableClassification: false,
// enableLandmarks: false,
// enableTracking: false,
// minFaceSize: 0.15,
// performanceMode: FaceDetectorMode.accurate,
// );
// final faceDetector = FaceDetector(options: options);
// try {
// final List<Face> faces = await faceDetector.processImage(inputImage);
// final image = img.decodeImage(await imageFile.readAsBytes());
// if (image == null) throw Exception('Unable to decode image');
// int left, top, width, height;
// if (faces.isNotEmpty) {
// // Face detected, crop around the face
// final face = faces[0];
// double padding = 0.2; // 20% padding
// int paddingX = (face.boundingBox.width * padding).round();
// int paddingY = (face.boundingBox.height * padding).round();
// left = (face.boundingBox.left - paddingX).round();
// top = (face.boundingBox.top - paddingY).round();
// width = (face.boundingBox.width + 2 * paddingX).round();
// height = (face.boundingBox.height + 2 * paddingY).round();
// } else {
// // No face detected, crop the center of the image
// int size = min(image.width, image.height);
// left = (image.width - size) ~/ 2;
// top = (image.height - size) ~/ 2;
// width = size;
// height = size;
// }
// // Ensure dimensions are within image bounds
// left = left.clamp(0, image.width - 1);
// top = top.clamp(0, image.height - 1);
// width = width.clamp(1, image.width - left);
// height = height.clamp(1, image.height - top);
// final croppedImage =
// img.copyCrop(image, x: left, y: top, width: width, height: height);
// // Save the cropped image
// final tempDir = await path_provider.getTemporaryDirectory();
// final tempPath = tempDir.path;
// final croppedFile = File('$tempPath/cropped_image.jpg');
// await croppedFile.writeAsBytes(img.encodeJpg(croppedImage, quality: 100));
// return croppedFile;
// } finally {
// faceDetector.close();
// }
// }

View File

@@ -0,0 +1,15 @@
import 'package:get/get.dart';
import '../../constant/box_name.dart';
import '../../main.dart';
class HomePageController extends GetxController {
late bool isVibrate = box.read(BoxName.isvibrate) ?? true;
void changeVibrateOption(bool value) {
isVibrate = box.read(BoxName.isvibrate) ?? true;
isVibrate = value;
box.write(BoxName.isvibrate, value);
update();
}
}