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:sefer_driver/constant/box_name.dart'; import 'package:sefer_driver/constant/colors.dart'; import 'package:sefer_driver/constant/links.dart'; import 'package:sefer_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 'encrypt_decrypt.dart'; Future checkForUpdate(BuildContext context) async { final packageInfo = await PackageInfo.fromPlatform(); final currentVersion = packageInfo.buildNumber; final version = packageInfo.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 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.sefer_driver' : 'https://apps.apple.com/ae/app/sefer-driver/id6502189302'; 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, ); }, ); } getDeviceFingerprint() async { final deviceInfo = await DeviceInfoPlugin().deviceInfo; var deviceData; if (Platform.isAndroid) { deviceData = deviceInfo.data; } else if (Platform.isIOS) { deviceData = deviceInfo.data; } final String deviceId = deviceData['androidId'] ?? deviceData['identifierForVendor']; final String deviceModel = deviceData['model']; final String osVersion = deviceData['systemVersion']; return EncryptionHelper.instance .encryptData('${deviceId}_${deviceModel}_$osVersion'); } class SecurityHelper { /// Performs security checks and handles potential risks static Future 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; isOnExternalStorage = await JailbreakRootDetection.instance.isOnExternalStorage; List 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) { // Pass the CORRECT bundle ID to isTampered 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(); } } /// Deletes all app data static Future 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( 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 _clearDataAndExit() async { await storage.deleteAll(); await box.erase(); exit(0); // Exit the app print('exit'); } }