This commit is contained in:
Hamza-Ayed
2025-08-06 01:10:52 +03:00
parent 57b7574657
commit 441385069c
49 changed files with 2577 additions and 1822 deletions

View File

@@ -12,7 +12,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_contacts/contact.dart';
import 'package:flutter_contacts/flutter_contacts.dart';
import 'package:get/get.dart';
import 'package:share/share.dart';
import 'package:share_plus/share_plus.dart';
import '../../../main.dart';
import '../../../views/widgets/error_snakbar.dart';

View File

@@ -4,6 +4,7 @@ import 'package:crypto/crypto.dart';
import 'dart:math';
import 'package:http/http.dart' as http;
import 'package:permission_handler/permission_handler.dart';
import 'package:secure_string_operations/secure_string_operations.dart';
import 'package:sefer_driver/controller/functions/location_background_controller.dart';
import 'package:sefer_driver/views/auth/captin/cards/sms_signup.dart';
@@ -49,6 +50,42 @@ class LoginDriverController extends GetxController {
update();
}
bool showManualForm = false;
bool isRegisterMode = false; // false = Login, true = Register
/// تبديل عرض نموذج الدخول/التسجيل اليدوي
void toggleManualFormView() {
showManualForm = !showManualForm;
// مسح الحقول عند إغلاق النموذج
if (!showManualForm) {
emailController.clear();
passwordController.clear();
}
update();
}
/// تبديل بين وضع تسجيل الدخول ووضع إنشاء حساب جديد
void toggleRegisterMode() {
isRegisterMode = !isRegisterMode;
update();
}
bool isRegistering = false;
void toggleRegistration() {
isRegistering = !isRegistering;
update();
}
bool isPasswordHidden = true;
void togglePasswordVisibility() {
isPasswordHidden = !isPasswordHidden;
update([
'passwordVisibility'
]); // Use a unique ID to only update the password field
}
void changeGoogleDashOpen() {
isGoogleDashOpen = !isGoogleDashOpen;
update();
@@ -195,8 +232,10 @@ class LoginDriverController extends GetxController {
}
Future<void> getLocationPermission() async {
Get.put(LocationBackgroundController()).requestLocationPermission();
var status = await Permission.locationAlways.status;
if (!status.isGranted) {
await Permission.locationAlways.request();
}
update();
}
@@ -229,9 +268,10 @@ class LoginDriverController extends GetxController {
loginWithGoogleCredential(String driverID, email) async {
isloading = true;
update();
await SecurityHelper.performSecurityChecks();
// await SecurityHelper.performSecurityChecks();
Log.print('(BoxName.emailDriver): ${box.read(BoxName.emailDriver)}');
Log.print('email: ${email}');
Log.print('driverID: ${driverID}');
var res = await CRUD().get(link: AppLink.loginFromGoogleCaptin, payload: {
'email': email,
'id': driverID,
@@ -312,10 +352,14 @@ class LoginDriverController extends GetxController {
key: BoxName.fingerPrint, value: fingerPrint.toString());
if (token != 'failure') {
Log.print(
'box.read(BoxName.tokenDriver): ${box.read(BoxName.tokenDriver)}');
Log.print(
' (jsonDecode(token): ${(jsonDecode(token)['data'][0]['token']).toString()}');
if ((jsonDecode(token)['data'][0]['token']) !=
(box.read(BoxName.tokenDriver))) {
Get.put(FirebaseMessagesController()).sendNotificationToDriverMAP(
'token change'.tr,
'token change',
'change device'.tr,
(jsonDecode(token)['data'][0]['token']).toString(),
[],
@@ -342,14 +386,7 @@ class LoginDriverController extends GetxController {
(box.read(BoxName.driverID)).toString(),
'fingerPrint': (fingerPrint).toString()
});
// await CRUD().post(
// link:
// "${AppLink.seferGizaServer}/ride/firebase/addDriver.php",
// payload: {
// 'token': box.read(BoxName.tokenDriver),
// 'captain_id':
// box.read(BoxName.driverID).toString()
// });
Get.back();
}));
}
@@ -374,7 +411,7 @@ class LoginDriverController extends GetxController {
loginUsingCredentialsWithoutGoogle(String password, email) async {
isloading = true;
isGoogleLogin = true;
update();
// update();
var res = await CRUD()
.get(link: AppLink.loginUsingCredentialsWithoutGoogle, payload: {
'email': (email),

View File

@@ -71,7 +71,7 @@ class FirebaseMessagesController extends GetxController {
Future getToken() async {
fcmToken.getToken().then((token) {
// Log.print('token: ${token}');
Log.print('token: ${token}');
box.write(BoxName.tokenDriver, (token!));
});
@@ -98,7 +98,7 @@ class FirebaseMessagesController extends GetxController {
}
Future<void> fireBaseTitles(RemoteMessage message) async {
if (message.notification!.title! == 'Order'.tr) {
if (message.notification!.title! == 'Order') {
if (Platform.isAndroid) {
notificationController.showNotification(
message.notification!.title.toString(),

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,206 @@
import 'dart:async';
import 'dart:io';
import 'package:device_info_plus/device_info_plus.dart';
import 'performance_test.dart';
/// 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

@@ -1,54 +1,54 @@
import 'dart:async';
import 'package:background_location/background_location.dart';
import 'package:get/get.dart';
import 'package:permission_handler/permission_handler.dart';
// import 'dart:async';
// import 'package:background_location/background_location.dart';
// import 'package:get/get.dart';
// import 'package:permission_handler/permission_handler.dart';
class LocationBackgroundController extends GetxController {
@override
void onInit() {
super.onInit();
requestLocationPermission();
configureBackgroundLocation();
}
// class LocationBackgroundController extends GetxController {
// @override
// void onInit() {
// super.onInit();
// requestLocationPermission();
// configureBackgroundLocation();
// }
Future<void> requestLocationPermission() async {
var status = await Permission.locationAlways.status;
if (!status.isGranted) {
await Permission.locationAlways.request();
}
}
// Future<void> requestLocationPermission() async {
// var status = await Permission.locationAlways.status;
// if (!status.isGranted) {
// await Permission.locationAlways.request();
// }
// }
Future<void> configureBackgroundLocation() async {
await BackgroundLocation.setAndroidNotification(
title: 'Location Tracking Active'.tr,
message: 'Your location is being tracked in the background.'.tr,
icon: '@mipmap/launcher_icon',
);
// Future<void> configureBackgroundLocation() async {
// await BackgroundLocation.setAndroidNotification(
// title: 'Location Tracking Active'.tr,
// message: 'Your location is being tracked in the background.'.tr,
// icon: '@mipmap/launcher_icon',
// );
BackgroundLocation.setAndroidConfiguration(3000);
BackgroundLocation.startLocationService();
BackgroundLocation.getLocationUpdates((location) {
// Handle location updates here
});
}
// BackgroundLocation.setAndroidConfiguration(3000);
// BackgroundLocation.startLocationService();
// BackgroundLocation.getLocationUpdates((location) {
// // Handle location updates here
// });
// }
startBackLocation() async {
Timer.periodic(const Duration(seconds: 3), (timer) {
getBackgroundLocation();
});
}
// startBackLocation() async {
// Timer.periodic(const Duration(seconds: 3), (timer) {
// getBackgroundLocation();
// });
// }
getBackgroundLocation() async {
var status = await Permission.locationAlways.status;
if (status.isGranted) {
await BackgroundLocation.startLocationService(
distanceFilter: 20, forceAndroidLocationManager: true);
BackgroundLocation.setAndroidConfiguration(
Duration.microsecondsPerSecond); // Set interval to 5 seconds
// getBackgroundLocation() async {
// var status = await Permission.locationAlways.status;
// if (status.isGranted) {
// await BackgroundLocation.startLocationService(
// distanceFilter: 20, forceAndroidLocationManager: true);
// BackgroundLocation.setAndroidConfiguration(
// Duration.microsecondsPerSecond); // Set interval to 5 seconds
BackgroundLocation.getLocationUpdates((location1) {});
} else {
await Permission.locationAlways.request();
}
}
}
// BackgroundLocation.getLocationUpdates((location1) {});
// } else {
// await Permission.locationAlways.request();
// }
// }
// }

View File

@@ -12,6 +12,7 @@ import '../../main.dart';
import '../../print.dart';
import '../home/captin/home_captain_controller.dart';
import '../home/payment/captain_wallet_controller.dart';
import 'battery_status.dart';
import 'crud.dart';
import 'encrypt_decrypt.dart';
@@ -40,6 +41,7 @@ class LocationController extends GetxController {
location = Location();
await location.changeSettings(
accuracy: LocationAccuracy.high, interval: 5000, distanceFilter: 0);
await location.enableBackgroundMode(enable: true);
await getLocation();
await startLocationUpdates();
@@ -82,6 +84,7 @@ class LocationController extends GetxController {
_locationTimer =
Timer.periodic(const Duration(seconds: 5), (timer) async {
try {
await BatteryNotifier.checkBatteryAndNotify();
totalPoints =
Get.find<CaptainWalletController>().totalPoints.toString();
isActive = Get.find<HomeCaptainController>().isActive;

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

@@ -54,7 +54,7 @@ class HomeCaptainController extends GetxController {
double widthMapTypeAndTraffic = 50;
// Inject the LocationController class
final locationController = Get.put(LocationController());
final locationBackController = Get.put(LocationBackgroundController());
// final locationBackController = Get.put(LocationBackgroundController());
String formatDuration(Duration duration) {
String twoDigits(int n) => n.toString().padLeft(2, "0");
String twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60));

View File

@@ -65,7 +65,44 @@ class MyTranslation extends Translations {
"Order Applied": "تم تطبيق الطلب",
//firebase above
"Driver Portal": "بوابة السائق",
"Sign in to start your journey": "سجّل الدخول لبدء رحلتك",
"Sign in with a provider for easy access":
"سجّل الدخول عبر أحد المزودين للوصول بسهولة",
"Sign In with Google": "تسجيل الدخول باستخدام جوجل",
"Sign in with Apple": "تسجيل الدخول باستخدام آبل",
"Or": "أو",
'Is device compatible': 'هل الجهاز متوافق',
"Create Account with Email": "إنشاء حساب بالبريد الإلكتروني",
"Need help? Contact Us": "هل تحتاج للمساعدة؟ تواصل معنا",
"Create Driver Account": "إنشاء حساب سائق",
"Driver Login": "تسجيل دخول السائق",
"Email": "البريد الإلكتروني",
"Enter your email": "أدخل بريدك الإلكتروني",
"Please enter a valid email": "الرجاء إدخال بريد إلكتروني صالح",
"Password": "كلمة المرور",
"Enter your password": "أدخل كلمة المرور",
"Password must be at least 6 characters":
"يجب أن تتكون كلمة المرور من 6 أحرف على الأقل",
"Create Account": "إنشاء حساب",
"Login": "تسجيل الدخول",
"Back to other sign-in options": "العودة إلى خيارات التسجيل الأخرى",
"Driver Agreement": "اتفاقية السائق",
"To become a driver, you must review and agree to the ":
"لتصبح سائقًا، يجب عليك مراجعة والموافقة على ",
"Terms of Use": "شروط الاستخدام",
" and acknowledge our Privacy Policy.":
" والإقرار بسياسة الخصوصية الخاصة بنا.",
"I Agree": "أنا أوافق",
"Continue": "متابعة",
"Privacy Policy": "سياسة الخصوصية",
"Location Access Required": "مطلوب الوصول إلى الموقع",
"We need access to your location to match you with nearby passengers and provide accurate navigation.":
"نحتاج للوصول إلى موقعك لمطابقتك مع الركاب القريبين وتوفير توجيه دقيق.",
"Please allow location access \"all the time\" to receive ride requests even when the app is in the background.":
"الرجاء السماح بالوصول إلى الموقع \"طوال الوقت\" لاستقبال طلبات الرحلات حتى عندما يكون التطبيق في الخلفية.",
"Allow Location Access": "السماح بالوصول إلى الموقع",
"Open Settings": "فتح الإعدادات",
"payment_success": "تمت العملية بنجاح",
"transaction_id": "رقم العملية",
"amount_paid": "المبلغ المدفوع",

View File

@@ -15,7 +15,7 @@ class DriverWalletHistoryController extends GetxController {
getArchivePayment() async {
isLoading = true;
update();
var res = await CRUD().get(
var res = await CRUD().getWallet(
link: AppLink.getWalletByDriver,
payload: {'driverID': box.read(BoxName.driverID)});
if (res == 'failure') {
@@ -40,7 +40,7 @@ class DriverWalletHistoryController extends GetxController {
getWeekllyArchivePayment() async {
isLoading = true;
update();
var res = await CRUD().get(
var res = await CRUD().getWallet(
link: AppLink.getDriverWeekPaymentMove,
payload: {'driverID': box.read(BoxName.driverID)});
if (res == 'failure') {

View File

@@ -13,7 +13,7 @@ ThemeData lightThemeEnglish = ThemeData(
bodyMedium: AppStyle.subtitle,
),
primarySwatch: Colors.blue,
dialogTheme: DialogTheme(
dialogTheme: DialogThemeData(
backgroundColor: AppColor.secondaryColor,
contentTextStyle: AppStyle.title,
titleTextStyle: AppStyle.headTitle2,
@@ -49,7 +49,7 @@ ThemeData darkThemeEnglish = ThemeData(
bodyMedium: AppStyle.subtitle,
),
primarySwatch: Colors.blue,
dialogTheme: DialogTheme(
dialogTheme: DialogThemeData(
backgroundColor: AppColor.secondaryColor,
contentTextStyle: AppStyle.title,
titleTextStyle: AppStyle.headTitle2,
@@ -85,7 +85,7 @@ ThemeData lightThemeArabic = ThemeData(
bodyMedium: AppStyle.subtitle,
),
primarySwatch: Colors.blue,
dialogTheme: DialogTheme(
dialogTheme: DialogThemeData(
backgroundColor: AppColor.secondaryColor,
contentTextStyle: AppStyle.title,
titleTextStyle: AppStyle.headTitle2,
@@ -121,7 +121,7 @@ ThemeData darkThemeArabic = ThemeData(
bodyMedium: AppStyle.subtitle,
),
primarySwatch: Colors.blue,
dialogTheme: DialogTheme(
dialogTheme: DialogThemeData(
backgroundColor: AppColor.secondaryColor,
contentTextStyle: AppStyle.title,
titleTextStyle: AppStyle.headTitle2,