Files
intaleq_driver/lib/controller/functions/package_info.dart
Hamza-Ayed ba02d41e6d 25-8-4-1
2025-08-04 22:43:04 +03:00

363 lines
14 KiB
Dart
Executable File

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 '../../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/ae/app/intaleq-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,
);
},
);
}
class DeviceHelper {
static Future<String> getDeviceFingerprint() async {
final DeviceInfoPlugin deviceInfoPlugin = DeviceInfoPlugin();
var deviceData;
try {
if (Platform.isAndroid) {
// Fetch Android-specific device information
AndroidDeviceInfo androidInfo = await deviceInfoPlugin.androidInfo;
deviceData = androidInfo.toMap(); // Convert to a map for easier access
// Log.print('deviceData: ${jsonEncode(deviceData)}');
} else if (Platform.isIOS) {
// Fetch iOS-specific device information
IosDeviceInfo iosInfo = await deviceInfoPlugin.iosInfo;
deviceData = iosInfo.toMap(); // Convert to a map for easier access
} else {
throw UnsupportedError('Unsupported platform');
}
// Extract relevant device information
final String deviceId = Platform.isAndroid
? deviceData['androidId'] ?? deviceData['serialNumber'] ?? 'unknown'
: deviceData['identifierForVendor'] ?? 'unknown';
final String deviceModel = deviceData['model'] ?? 'unknown';
final String osVersion = Platform.isAndroid
? deviceData['version']['release'] ?? 'unknown'
: deviceData['systemVersion'] ?? 'unknown';
// Log the extracted information
// Generate and return the encrypted fingerprint
final String fingerprint = '${deviceId}_${deviceModel}_$osVersion';
// print(EncryptionHelper.instance.encryptData(fingerprint));
return (fingerprint);
} catch (e) {
throw Exception('Failed to generate device fingerprint');
}
}
}
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;
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) {
// 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<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');
}
}