refactor: إعادة تسمية التطبيقات الأربعة siro_* → intaleq_*
المجلدات وأسماء حزم dart واستيراداتها: siro_rider → intaleq_rider siro_driver → intaleq_driver siro_admin → intaleq_admin siro_service → intaleq_service شمل ذلك `name:` في كل pubspec وكل `package:siro_*` في الاستيرادات (115 ملفاً في rider وحده)، وإشارات أسماء المجلدات في docs/ وتعليق في backend/Admin/notifications/broadcast.php. لم تبقَ إشارة واحدة (تحقّقت). + ضُمّت حزم get و get_storage داخل intaleq_driver/packages/ وحُوّلت مساراتها الثلاثة من `../../Intaleq/packages/*` إلى `./packages/*`. كانت تُحل عرضاً إلى مجلد App/Intaleq القديم المجاور — تبعية خارج المستودع تنكسر بأول نقل. لم يبقَ في أي تطبيق تبعية مسار خارج الشجرة. ⚠️ لم تُمسّ بعد: هويات المتاجر (applicationId · PRODUCT_BUNDLE_IDENTIFIER · shorebird app_id) ولا الألوان ولا النصوص المرئية ولا النطاقات — كل منها كوميت منفصل، والهويات تحتاج قرار المالك. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
92dc6b3641
commit
0aab85c732
@@ -0,0 +1,242 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
import 'dart:ui';
|
||||
import 'package:device_info_plus/device_info_plus.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/box_name.dart';
|
||||
import '../../constant/colors.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('currentVersion is : $currentVersion');
|
||||
// Fetch the latest version from your server
|
||||
String latestVersion = box.read(BoxName.package)?.toString() ?? '';
|
||||
box.write(BoxName.packagInfo, version);
|
||||
|
||||
if (latestVersion.isNotEmpty && latestVersion != currentVersion) {
|
||||
showUpdateDialog(context);
|
||||
}
|
||||
}
|
||||
|
||||
checkForBounusInvitation() {
|
||||
if (box.read(BoxName.inviteCode) != null) {}
|
||||
}
|
||||
|
||||
// Future<String> getPackageInfo() async {
|
||||
// final response = await CRUD().get(link: AppLink.packageInfo, payload: {
|
||||
// "platform": Platform.isAndroid ? 'android' : 'ios',
|
||||
// "appName": AppInformation.appName,
|
||||
// });
|
||||
|
||||
// if (response != 'failure') {
|
||||
// return jsonDecode(response)['message'][0]['version'];
|
||||
// }
|
||||
// return '';
|
||||
// }
|
||||
// getDeviceFingerprint() async {
|
||||
// final deviceInfo = await DeviceInfoPlugin().deviceInfo;
|
||||
// var deviceData;
|
||||
|
||||
// if (Platform.isAndroid) {
|
||||
// deviceData = deviceInfo.data;
|
||||
// Log.print('deviceData: ${jsonEncode(deviceData)}');
|
||||
// } else if (Platform.isIOS) {
|
||||
// deviceData = deviceInfo.data;
|
||||
// }
|
||||
|
||||
// final String deviceId =
|
||||
// deviceData['device'] ?? deviceData['identifierForVendor'];
|
||||
// final String deviceModel = deviceData['model'];
|
||||
// final String osVersion = deviceData['systemVersion'];
|
||||
|
||||
// Log.print('fingr: ${'${deviceId}_${deviceModel}_$osVersion'}');
|
||||
// Log.print('deviceModel: ${deviceModel}');
|
||||
// Log.print('osVersion: ${osVersion}');
|
||||
// return EncryptionHelper.instance
|
||||
// .encryptData('${deviceId}_${deviceModel}_$osVersion');
|
||||
// }
|
||||
|
||||
void showUpdateDialog(BuildContext context) {
|
||||
final String storeUrl = Platform.isAndroid
|
||||
? 'https://play.google.com/store/apps/details?id=com.Siro.siro'
|
||||
: 'https://apps.apple.com/jo/app/siro-rider/id6748075179';
|
||||
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 {
|
||||
// ── التحقق من وجود بصمة مخزّنة مسبقاً ──────────────────────
|
||||
// AES-GCM يستخدم IV عشوائي كل مرة، فالتشفير ينتج نتيجة مختلفة
|
||||
// حتى لو النص الأصلي نفسه. لذلك نخزّن البصمة المشفرة أول مرة
|
||||
// ونرجعها من التخزين في كل مرة بعدها لضمان الثبات.
|
||||
final String? cachedFp = box.read(BoxName.deviceFpEncrypted);
|
||||
if (cachedFp != null && cachedFp.isNotEmpty) {
|
||||
return cachedFp;
|
||||
}
|
||||
|
||||
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['fingerprint'] ?? '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';
|
||||
final String encryptedFp =
|
||||
EncryptionHelper.instance.encryptData(fingerprint);
|
||||
box.write(BoxName.deviceFpEncrypted, encryptedFp);
|
||||
//Log.print(EncryptionHelper.instance.encryptData(fingerprint));
|
||||
return encryptedFp;
|
||||
} catch (e) {
|
||||
throw Exception('Failed to generate device fingerprint');
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user