Update: 2026-05-08 06:19:56
This commit is contained in:
@@ -1,3 +1,6 @@
|
||||
import java.util.Properties
|
||||
import java.io.FileInputStream
|
||||
|
||||
plugins {
|
||||
id("com.android.application")
|
||||
// START: FlutterFire Configuration
|
||||
@@ -8,10 +11,17 @@ plugins {
|
||||
id("dev.flutter.flutter-gradle-plugin")
|
||||
}
|
||||
|
||||
// Load keystore properties from key.properties file
|
||||
val keystorePropertiesFile = rootProject.file("key.properties")
|
||||
val keystoreProperties = Properties()
|
||||
if (keystorePropertiesFile.exists()) {
|
||||
keystoreProperties.load(FileInputStream(keystorePropertiesFile))
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.example.musadaq_app"
|
||||
namespace = "com.musadaq.app"
|
||||
compileSdk = flutter.compileSdkVersion
|
||||
ndkVersion = "27.0.12077973"
|
||||
ndkVersion = "28.2.13676358"
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
@@ -22,11 +32,19 @@ android {
|
||||
jvmTarget = JavaVersion.VERSION_11.toString()
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
create("release") {
|
||||
if (keystorePropertiesFile.exists()) {
|
||||
keyAlias = keystoreProperties["keyAlias"] as String
|
||||
keyPassword = keystoreProperties["keyPassword"] as String
|
||||
storeFile = file(keystoreProperties["storeFile"] as String)
|
||||
storePassword = keystoreProperties["storePassword"] as String
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||
applicationId = "com.example.musadaq_app"
|
||||
// You can update the following values to match your application needs.
|
||||
// For more information, see: https://flutter.dev/to/review-gradle-config.
|
||||
applicationId = "com.musadaq.app"
|
||||
minSdk = 24
|
||||
targetSdk = flutter.targetSdkVersion
|
||||
versionCode = flutter.versionCode
|
||||
@@ -35,9 +53,17 @@ android {
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
// TODO: Add your own signing config for the release build.
|
||||
// Signing with the debug keys for now, so `flutter run --release` works.
|
||||
signingConfig = signingConfigs.getByName("debug")
|
||||
signingConfig = if (keystorePropertiesFile.exists()) {
|
||||
signingConfigs.getByName("release")
|
||||
} else {
|
||||
signingConfigs.getByName("debug")
|
||||
}
|
||||
isMinifyEnabled = true
|
||||
isShrinkResources = true
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
"client": [
|
||||
{
|
||||
"client_info": {
|
||||
"mobilesdk_app_id": "1:512384487867:android:eac271c0b0ea64b708749e",
|
||||
"mobilesdk_app_id": "1:512384487867:android:6098e34775ad6c7708749e",
|
||||
"android_client_info": {
|
||||
"package_name": "com.example.musadaq_app"
|
||||
"package_name": "com.musadaq.app"
|
||||
}
|
||||
},
|
||||
"oauth_client": [],
|
||||
|
||||
22
musadaq-app/android/app/proguard-rules.pro
vendored
Normal file
22
musadaq-app/android/app/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
# Flutter ProGuard Rules for Musadaq
|
||||
|
||||
# Keep Flutter engine
|
||||
-keep class io.flutter.** { *; }
|
||||
-keep class io.flutter.plugins.** { *; }
|
||||
|
||||
# Keep Firebase
|
||||
-keep class com.google.firebase.** { *; }
|
||||
|
||||
# Keep Dio
|
||||
-keep class io.github.nicekun.** { *; }
|
||||
-dontwarn okhttp3.**
|
||||
-dontwarn okio.**
|
||||
|
||||
# Keep CameraAwesome
|
||||
-keep class com.apparence.camerawesome.** { *; }
|
||||
|
||||
# Keep ObjectBox
|
||||
-keep class io.objectbox.** { *; }
|
||||
|
||||
# Ignore missing Play Core classes referenced by Flutter Engine
|
||||
-dontwarn com.google.android.play.core.**
|
||||
@@ -16,7 +16,7 @@
|
||||
<uses-permission android:name="android.permission.USE_BIOMETRIC"/>
|
||||
|
||||
<application
|
||||
android:label="musadaq_app"
|
||||
android:label="مُصادَق"
|
||||
android:name="${applicationName}"
|
||||
android:icon="@mipmap/launcher_icon"
|
||||
android:enableOnBackInvokedCallback="true">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.example.musadaq_app
|
||||
package com.musadaq.app
|
||||
|
||||
import io.flutter.embedding.android.FlutterFragmentActivity
|
||||
|
||||
4
musadaq-app/android/app/src/main/res/values/strings.xml
Normal file
4
musadaq-app/android/app/src/main/res/values/strings.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">مُصادَق</string>
|
||||
</resources>
|
||||
663
musadaq-app/android/build/reports/problems/problems-report.html
Normal file
663
musadaq-app/android/build/reports/problems/problems-report.html
Normal file
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
{"flutter":{"platforms":{"android":{"default":{"projectId":"musadaq-c12ca","appId":"1:512384487867:android:eac271c0b0ea64b708749e","fileOutput":"android/app/google-services.json"}},"ios":{"default":{"projectId":"musadaq-c12ca","appId":"1:512384487867:ios:03bd28c6088a4aa008749e","uploadDebugSymbols":false,"fileOutput":"ios/Runner/GoogleService-Info.plist"}},"dart":{"lib/firebase_options.dart":{"projectId":"musadaq-c12ca","configurations":{"android":"1:512384487867:android:eac271c0b0ea64b708749e","ios":"1:512384487867:ios:03bd28c6088a4aa008749e"}}}}}}
|
||||
{"flutter":{"platforms":{"android":{"default":{"projectId":"musadaq-c12ca","appId":"1:512384487867:android:6098e34775ad6c7708749e","fileOutput":"android/app/google-services.json"}},"ios":{"default":{"projectId":"musadaq-c12ca","appId":"1:512384487867:ios:c743b83778682d8308749e","uploadDebugSymbols":false,"fileOutput":"ios/Runner/GoogleService-Info.plist"}},"dart":{"lib/firebase_options.dart":{"projectId":"musadaq-c12ca","configurations":{"android":"1:512384487867:android:6098e34775ad6c7708749e","ios":"1:512384487867:ios:c743b83778682d8308749e"}}}}}}
|
||||
@@ -9,7 +9,7 @@
|
||||
<key>PLIST_VERSION</key>
|
||||
<string>1</string>
|
||||
<key>BUNDLE_ID</key>
|
||||
<string>com.example.musadaqApp</string>
|
||||
<string>com.musadaq.app</string>
|
||||
<key>PROJECT_ID</key>
|
||||
<string>musadaq-c12ca</string>
|
||||
<key>STORAGE_BUCKET</key>
|
||||
@@ -25,6 +25,6 @@
|
||||
<key>IS_SIGNIN_ENABLED</key>
|
||||
<true></true>
|
||||
<key>GOOGLE_APP_ID</key>
|
||||
<string>1:512384487867:ios:03bd28c6088a4aa008749e</string>
|
||||
<string>1:512384487867:ios:c743b83778682d8308749e</string>
|
||||
</dict>
|
||||
</plist>
|
||||
237
musadaq-app/lib/core/services/device_security_service.dart
Normal file
237
musadaq-app/lib/core/services/device_security_service.dart
Normal file
@@ -0,0 +1,237 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:freerasp/freerasp.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
/// Device Security Service
|
||||
/// Enterprise-grade jailbreak/root/tamper detection for Musadaq using freeRASP.
|
||||
///
|
||||
/// Detects: root, jailbreak, debugger, emulator, hooking frameworks,
|
||||
/// tampered apps, unofficial stores, missing passcode.
|
||||
///
|
||||
/// Policy: On detection → block app with security warning (no data access).
|
||||
class DeviceSecurityService extends GetxService {
|
||||
final isCompromised = false.obs;
|
||||
final threatDetails = <String>[].obs;
|
||||
final securityChecked = false.obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
_initSecurity();
|
||||
}
|
||||
|
||||
/// Initialize freeRASP
|
||||
Future<void> _initSecurity() async {
|
||||
try {
|
||||
await _initFreeRASP();
|
||||
} catch (e) {
|
||||
debugPrint('[Security] freeRASP init failed: $e');
|
||||
}
|
||||
securityChecked.value = true;
|
||||
}
|
||||
|
||||
/// Initialize freeRASP with full threat callbacks
|
||||
Future<void> _initFreeRASP() async {
|
||||
final callback = ThreatCallback(
|
||||
onAppIntegrity: () => _onThreatDetected('تلاعب بالتطبيق'),
|
||||
onObfuscationIssues: () => _onThreatDetected('عدم وجود تشفير الكود'),
|
||||
onDebug: () => _onThreatDetected('وضع التصحيح مفعّل'),
|
||||
onDeviceBinding: () => _onThreatDetected('تغيير جهاز غير مصرح'),
|
||||
onDeviceID: () => _onThreatDetected('معرّف جهاز مزوّر'),
|
||||
onHooks: () => _onThreatDetected('أدوات اختراق (Hooking)'),
|
||||
onPasscode: () => _onThreatDetected('الجهاز بدون قفل شاشة'),
|
||||
onPrivilegedAccess: () => _onThreatDetected('جهاز مروّت (Root/Jailbreak)'),
|
||||
onSecureHardwareNotAvailable: () =>
|
||||
_onThreatDetected('عدم توفر عتاد آمن'),
|
||||
onSimulator: () => _onThreatDetected('محاكي (Emulator)'),
|
||||
onUnofficialStore: () => _onThreatDetected('تثبيت من متجر غير رسمي'),
|
||||
);
|
||||
|
||||
// Configure talsec
|
||||
final config = TalsecConfig(
|
||||
androidConfig: AndroidConfig(
|
||||
packageName: 'com.musadaq.app',
|
||||
signingCertHashes: ['4F:10:B4:E9:29:E0:5E:0A:3E:AE:B0:31:4C:C1:3A:DB:CB:E6:FF:DF:6A:F5:85:FC:68:FE:C1:E4:B6:29:6B:6F'],
|
||||
supportedStores: ['com.android.vending'], // Google Play only
|
||||
),
|
||||
iosConfig: IOSConfig(
|
||||
bundleIds: ['com.musadaq.app'],
|
||||
teamId: 'REPLACE_WITH_YOUR_TEAM_ID',
|
||||
),
|
||||
watcherMail: 'security@musadaq.jo',
|
||||
isProd: true,
|
||||
);
|
||||
|
||||
await Talsec.instance.start(config);
|
||||
Talsec.instance.attachListener(callback);
|
||||
debugPrint('[Security] freeRASP initialized');
|
||||
}
|
||||
|
||||
/// Handle threat detection
|
||||
void _onThreatDetected(String threat) {
|
||||
debugPrint('[Security] ⚠️ THREAT: $threat');
|
||||
threatDetails.add(threat);
|
||||
isCompromised.value = true;
|
||||
|
||||
// Block the app immediately
|
||||
_showSecurityBlock();
|
||||
}
|
||||
|
||||
/// Show a non-dismissible security warning that blocks the app
|
||||
void _showSecurityBlock() {
|
||||
// Only show once
|
||||
if (Get.isDialogOpen == true) return;
|
||||
|
||||
Get.dialog(
|
||||
PopScope(
|
||||
canPop: false,
|
||||
child: Scaffold(
|
||||
backgroundColor: const Color(0xFF1A1A2E),
|
||||
body: SafeArea(
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(32),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
// Shield icon
|
||||
Container(
|
||||
width: 100,
|
||||
height: 100,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Colors.red.withValues(alpha: 0.15),
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.shield_rounded,
|
||||
size: 56,
|
||||
color: Colors.red,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
|
||||
// Title
|
||||
const Text(
|
||||
'تحذير أمني',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 28,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontFamily: 'El Messiri',
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Message
|
||||
const Text(
|
||||
'تم اكتشاف أن هذا الجهاز غير آمن.\n'
|
||||
'لحماية بياناتك المالية والضريبية،\n'
|
||||
'لا يمكن تشغيل مُصادَق على هذا الجهاز.',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: Colors.white70,
|
||||
fontSize: 15,
|
||||
height: 1.6,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// Threat details
|
||||
Obx(() => Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.red.withValues(alpha: 0.1),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(
|
||||
color: Colors.red.withValues(alpha: 0.3)),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
'التهديدات المكتشفة:',
|
||||
style: TextStyle(
|
||||
color: Colors.red,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 13,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
...threatDetails.map((t) => Padding(
|
||||
padding: const EdgeInsets.only(bottom: 4),
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.warning_amber,
|
||||
color: Colors.red, size: 16),
|
||||
const SizedBox(width: 8),
|
||||
Text(t,
|
||||
style: const TextStyle(
|
||||
color: Colors.white60,
|
||||
fontSize: 13)),
|
||||
],
|
||||
),
|
||||
)),
|
||||
],
|
||||
),
|
||||
)),
|
||||
|
||||
const SizedBox(height: 32),
|
||||
|
||||
// Close button (exits the app)
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: () => SystemNavigator.pop(),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.red,
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12)),
|
||||
),
|
||||
icon: const Icon(Icons.exit_to_app,
|
||||
color: Colors.white),
|
||||
label: const Text(
|
||||
'إغلاق التطبيق',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Help text
|
||||
const Text(
|
||||
'إذا كنت تعتقد أن هذا خطأ، تواصل مع\nsupport@musadaq.jo',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(color: Colors.white38, fontSize: 12),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
barrierDismissible: false,
|
||||
);
|
||||
}
|
||||
|
||||
/// Quick check if device is safe (for use in API calls)
|
||||
bool get isSafe => !isCompromised.value;
|
||||
|
||||
/// Get security status for display
|
||||
Map<String, dynamic> getSecurityStatus() {
|
||||
return {
|
||||
'is_safe': isSafe,
|
||||
'checked': securityChecked.value,
|
||||
'threats': threatDetails.toList(),
|
||||
'threat_count': threatDetails.length,
|
||||
};
|
||||
}
|
||||
}
|
||||
147
musadaq-app/lib/core/services/shorebird_update_service.dart
Normal file
147
musadaq-app/lib/core/services/shorebird_update_service.dart
Normal file
@@ -0,0 +1,147 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:shorebird_code_push/shorebird_code_push.dart';
|
||||
|
||||
/// Shorebird Code Push Service
|
||||
/// Handles OTA (Over-The-Air) updates for Dart code.
|
||||
/// Checks for patches on app launch and notifies users when updates are available.
|
||||
class ShorebirdUpdateService extends GetxService {
|
||||
final _updater = ShorebirdUpdater();
|
||||
|
||||
final isUpdateAvailable = false.obs;
|
||||
final isDownloading = false.obs;
|
||||
final currentPatchNumber = 0.obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
_checkForUpdate();
|
||||
}
|
||||
|
||||
/// Check if Shorebird is available (only works with shorebird release builds)
|
||||
bool get isShorebirdAvailable => _updater.isAvailable;
|
||||
|
||||
/// Get current patch number
|
||||
Future<void> loadCurrentPatch() async {
|
||||
if (!isShorebirdAvailable) return;
|
||||
try {
|
||||
final patch = await _updater.readCurrentPatch();
|
||||
currentPatchNumber.value = patch?.number ?? 0;
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
/// Check for updates on launch
|
||||
Future<void> _checkForUpdate() async {
|
||||
if (!isShorebirdAvailable) {
|
||||
debugPrint('[Shorebird] Not available (debug build)');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await loadCurrentPatch();
|
||||
debugPrint('[Shorebird] Current patch: ${currentPatchNumber.value}');
|
||||
|
||||
final status = await _updater.checkForUpdate();
|
||||
if (status == UpdateStatus.outdated) {
|
||||
isUpdateAvailable.value = true;
|
||||
debugPrint('[Shorebird] Update available!');
|
||||
_showUpdateDialog();
|
||||
} else {
|
||||
debugPrint('[Shorebird] App is up to date');
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('[Shorebird] Error checking for update: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Download and apply the update
|
||||
Future<void> downloadUpdate() async {
|
||||
if (!isShorebirdAvailable) return;
|
||||
|
||||
isDownloading.value = true;
|
||||
try {
|
||||
await _updater.update();
|
||||
isDownloading.value = false;
|
||||
isUpdateAvailable.value = false;
|
||||
|
||||
// Show restart prompt
|
||||
Get.defaultDialog(
|
||||
title: '✅ تم التحديث',
|
||||
middleText: 'تم تحميل التحديث بنجاح.\nأعد تشغيل التطبيق لتطبيق التغييرات.',
|
||||
textConfirm: 'حسناً',
|
||||
confirmTextColor: Colors.white,
|
||||
buttonColor: const Color(0xFF0F4C81),
|
||||
onConfirm: () => Get.back(),
|
||||
titleStyle: const TextStyle(fontWeight: FontWeight.bold),
|
||||
radius: 14,
|
||||
);
|
||||
} catch (e) {
|
||||
isDownloading.value = false;
|
||||
debugPrint('[Shorebird] Download failed: $e');
|
||||
Get.snackbar(
|
||||
'خطأ',
|
||||
'فشل تحميل التحديث',
|
||||
backgroundColor: Colors.red,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Show update available dialog
|
||||
void _showUpdateDialog() {
|
||||
Get.defaultDialog(
|
||||
title: '🔄 تحديث متاح',
|
||||
middleText: 'يتوفر تحديث جديد لمُصادَق.\nهل تريد تحميله الآن؟',
|
||||
textConfirm: 'تحديث',
|
||||
textCancel: 'لاحقاً',
|
||||
confirmTextColor: Colors.white,
|
||||
buttonColor: const Color(0xFF0F4C81),
|
||||
onConfirm: () {
|
||||
Get.back();
|
||||
downloadUpdate();
|
||||
},
|
||||
titleStyle: const TextStyle(fontWeight: FontWeight.bold),
|
||||
radius: 14,
|
||||
);
|
||||
}
|
||||
|
||||
/// Manual check (can be called from settings)
|
||||
Future<void> manualCheckForUpdate() async {
|
||||
if (!isShorebirdAvailable) {
|
||||
Get.snackbar(
|
||||
'تنبيه',
|
||||
'التحديث التلقائي متاح فقط في نسخة الإصدار',
|
||||
backgroundColor: Colors.orange,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
Get.snackbar(
|
||||
'جارٍ الفحص...',
|
||||
'يتم البحث عن تحديثات',
|
||||
backgroundColor: const Color(0xFF0F4C81),
|
||||
colorText: Colors.white,
|
||||
showProgressIndicator: true,
|
||||
duration: const Duration(seconds: 2),
|
||||
);
|
||||
|
||||
try {
|
||||
final status = await _updater.checkForUpdate();
|
||||
if (status == UpdateStatus.outdated) {
|
||||
isUpdateAvailable.value = true;
|
||||
_showUpdateDialog();
|
||||
} else {
|
||||
Get.snackbar(
|
||||
'✅ محدّث',
|
||||
'التطبيق يعمل بأحدث إصدار (Patch ${currentPatchNumber.value})',
|
||||
backgroundColor: const Color(0xFF10B981),
|
||||
colorText: Colors.white,
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
Get.snackbar('خطأ', 'فشل البحث عن تحديثات',
|
||||
backgroundColor: Colors.red, colorText: Colors.white);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,8 @@ import 'package:get/get.dart';
|
||||
import '../controllers/settings_controller.dart';
|
||||
import '../../../app/routes/app_pages.dart';
|
||||
import '../../../core/utils/app_snackbar.dart';
|
||||
import '../../../core/services/shorebird_update_service.dart';
|
||||
import '../../../core/services/device_security_service.dart';
|
||||
|
||||
class SettingsView extends GetView<SettingsController> {
|
||||
const SettingsView({super.key});
|
||||
@@ -148,6 +150,18 @@ class SettingsView extends GetView<SettingsController> {
|
||||
isDark: isDark,
|
||||
),
|
||||
const Divider(height: 1),
|
||||
Builder(builder: (_) {
|
||||
final security = Get.find<DeviceSecurityService>();
|
||||
return Obx(() => _buildInfoTile(
|
||||
icon: security.isSafe
|
||||
? Icons.verified_user_rounded
|
||||
: Icons.gpp_bad_rounded,
|
||||
title: 'حالة الأمان',
|
||||
trailing: security.isSafe ? '✅ آمن' : '⚠️ غير آمن',
|
||||
isDark: isDark,
|
||||
));
|
||||
}),
|
||||
const Divider(height: 1),
|
||||
_buildInfoTile(
|
||||
icon: Icons.diamond_rounded,
|
||||
title: 'الاشتراكات والباقات',
|
||||
@@ -170,6 +184,17 @@ class SettingsView extends GetView<SettingsController> {
|
||||
isDark: isDark,
|
||||
onTap: () {},
|
||||
),
|
||||
const Divider(height: 1),
|
||||
_buildInfoTile(
|
||||
icon: Icons.system_update_rounded,
|
||||
title: 'البحث عن تحديثات',
|
||||
trailing: 'فحص →',
|
||||
isDark: isDark,
|
||||
onTap: () {
|
||||
final shorebird = Get.find<ShorebirdUpdateService>();
|
||||
shorebird.manualCheckForUpdate();
|
||||
},
|
||||
),
|
||||
]),
|
||||
const SizedBox(height: 32),
|
||||
_buildLogoutButton(),
|
||||
|
||||
@@ -51,7 +51,7 @@ class DefaultFirebaseOptions {
|
||||
|
||||
static const FirebaseOptions android = FirebaseOptions(
|
||||
apiKey: 'AIzaSyB7Gc_RNvFaFCsuN5acHK2SNkY5iMDecqk',
|
||||
appId: '1:512384487867:android:eac271c0b0ea64b708749e',
|
||||
appId: '1:512384487867:android:6098e34775ad6c7708749e',
|
||||
messagingSenderId: '512384487867',
|
||||
projectId: 'musadaq-c12ca',
|
||||
storageBucket: 'musadaq-c12ca.firebasestorage.app',
|
||||
@@ -59,10 +59,11 @@ class DefaultFirebaseOptions {
|
||||
|
||||
static const FirebaseOptions ios = FirebaseOptions(
|
||||
apiKey: 'AIzaSyBLKc35OqzY6oQA5507E2uHCCHQbRWAC_M',
|
||||
appId: '1:512384487867:ios:03bd28c6088a4aa008749e',
|
||||
appId: '1:512384487867:ios:c743b83778682d8308749e',
|
||||
messagingSenderId: '512384487867',
|
||||
projectId: 'musadaq-c12ca',
|
||||
storageBucket: 'musadaq-c12ca.firebasestorage.app',
|
||||
iosBundleId: 'com.example.musadaqApp',
|
||||
iosBundleId: 'com.musadaq.app',
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,6 +6,8 @@ import 'app/routes/app_pages.dart';
|
||||
import 'core/services/push_notification_service.dart';
|
||||
import 'core/services/upload_progress_service.dart';
|
||||
import 'core/services/home_widget_service.dart';
|
||||
import 'core/services/shorebird_update_service.dart';
|
||||
import 'core/services/device_security_service.dart';
|
||||
import 'app/theme/app_theme.dart';
|
||||
|
||||
@pragma('vm:entry-point')
|
||||
@@ -24,9 +26,13 @@ void main() async {
|
||||
// 2. Register background handler
|
||||
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
|
||||
|
||||
// 3. Register global services
|
||||
// 3. Security check (MUST be first)
|
||||
Get.put(DeviceSecurityService(), permanent: true);
|
||||
|
||||
// 4. Register global services
|
||||
Get.put(UploadProgressService(), permanent: true);
|
||||
Get.put(HomeWidgetService(), permanent: true);
|
||||
Get.put(ShorebirdUpdateService(), permanent: true);
|
||||
|
||||
runApp(const MusadaqApp());
|
||||
}
|
||||
|
||||
@@ -592,6 +592,14 @@ packages:
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
freerasp:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: freerasp
|
||||
sha256: "76a3fb6f8e3fdd7d83e224866998523e7fb79d5779321983e484a6cfbf4b01b5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.12.0"
|
||||
frontend_server_client:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1264,6 +1272,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.0"
|
||||
shorebird_code_push:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: shorebird_code_push
|
||||
sha256: "55d5eea098a4293199d22dfd263ce1d6c10820ceb42c2acb950f971a8bcee0ce"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.6"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
|
||||
@@ -63,6 +63,12 @@ dependencies:
|
||||
firebase_core: ^4.7.0
|
||||
firebase_messaging: ^16.2.0
|
||||
|
||||
# ─── Code Push (OTA Updates) ────────────────────────
|
||||
shorebird_code_push: ^2.0.0
|
||||
|
||||
# ─── Security (Root/Jailbreak/Tamper Detection) ─────
|
||||
freerasp: ^6.6.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
@@ -85,6 +91,7 @@ flutter_launcher_icons:
|
||||
flutter:
|
||||
uses-material-design: true
|
||||
assets:
|
||||
- shorebird.yaml
|
||||
- assets/images/logo.jpg
|
||||
- assets/images/onboarding_1.png
|
||||
- assets/images/onboarding_2.png
|
||||
|
||||
14
musadaq-app/shorebird.yaml
Normal file
14
musadaq-app/shorebird.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
# This file is used to configure the Shorebird updater used by your app.
|
||||
# Learn more at https://docs.shorebird.dev
|
||||
# This file does not contain any sensitive information and should be checked into version control.
|
||||
|
||||
# Your app_id is the unique identifier assigned to your app.
|
||||
# It is used to identify your app when requesting patches from Shorebird's servers.
|
||||
# It is not a secret and can be shared publicly.
|
||||
app_id: 34bbf488-9870-4717-b6c7-408485fcf264
|
||||
|
||||
# auto_update controls if Shorebird should automatically update in the background on launch.
|
||||
# If auto_update: false, you will need to use package:shorebird_code_push to trigger updates.
|
||||
# https://pub.dev/packages/shorebird_code_push
|
||||
# Uncomment the following line to disable automatic updates.
|
||||
# auto_update: false
|
||||
Reference in New Issue
Block a user