Merge branch 'master' into Main_App_Replacement
# Conflicts: # ios/Runner.xcodeproj/project.pbxproj # ios/Runner/Info.plist # lib/core/api_consts.dart # pubspec.yamlMain_App_Replacement
commit
1d3ea2817c
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg width="800px" height="800px" viewBox="0 0 120 120" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="120" height="120" fill="#EFF1F3"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M33.2503 38.4816C33.2603 37.0472 34.4199 35.8864 35.8543 35.875H83.1463C84.5848 35.875 85.7503 37.0431 85.7503 38.4816V80.5184C85.7403 81.9528 84.5807 83.1136 83.1463 83.125H35.8543C34.4158 83.1236 33.2503 81.957 33.2503 80.5184V38.4816ZM80.5006 41.1251H38.5006V77.8751L62.8921 53.4783C63.9172 52.4536 65.5788 52.4536 66.6039 53.4783L80.5006 67.4013V41.1251ZM43.75 51.6249C43.75 54.5244 46.1005 56.8749 49 56.8749C51.8995 56.8749 54.25 54.5244 54.25 51.6249C54.25 48.7254 51.8995 46.3749 49 46.3749C46.1005 46.3749 43.75 48.7254 43.75 51.6249Z" fill="#687787"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 892 B |
@ -0,0 +1,7 @@
|
||||
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="40" height="40" rx="10" fill="#EFEFF0"/>
|
||||
<g transform="translate(8.8, 8.8) scale(1.6)">
|
||||
<path d="M11.5658 1.95717C9.82689 0.890531 8.26692 1.31563 7.32452 2.02335C7.26938 2.06476 7.22039 2.10153 7.17659 2.13413C7.08676 2.20099 7.04185 2.23442 6.98387 2.23442C6.92589 2.23442 6.88098 2.20099 6.79115 2.13413C6.74735 2.10153 6.69836 2.06476 6.64322 2.02335C5.70082 1.31563 4.14085 0.890531 2.40195 1.95717C1.53619 2.48823 0.943044 3.43564 0.767402 4.5853C0.741775 4.75304 0.728961 4.83691 0.781341 4.89789C0.83372 4.95888 0.923033 4.95887 1.10166 4.95885L3.00095 4.95868C3.24133 4.95767 3.64619 4.95596 4.03964 5.0991C4.19392 5.15522 4.33336 5.22877 4.4573 5.3094C4.63656 5.42602 4.72618 5.48433 4.79864 5.46761C4.87109 5.45089 4.92018 5.36908 5.01835 5.20545L5.44147 4.50026C5.71062 4.05168 6.19943 3.78162 6.72246 3.79256C7.24549 3.80349 7.72259 4.09374 7.97275 4.55319L8.72948 5.94294C8.77795 6.03196 8.80218 6.07647 8.8435 6.10102C8.88482 6.12557 8.9355 6.12557 9.03686 6.12557H9.31698C10.1224 6.12557 10.7753 6.77849 10.7753 7.5839C10.7753 8.38932 10.1224 9.04224 9.31698 9.04224L8.66574 9.04348C8.42373 9.04908 7.69714 9.06588 7.06184 8.57794C7.0432 8.56363 7.02501 8.54906 7.00725 8.5343C6.81047 8.37075 6.71208 8.28898 6.63276 8.30247C6.55344 8.31596 6.49635 8.41111 6.38218 8.6014L6.19249 8.91754C5.93991 9.33851 5.49248 9.60414 5.00197 9.62434C4.51146 9.64453 4.0437 9.41658 3.75738 9.0178L3.04199 8.02146C2.99064 7.94995 2.96497 7.91419 2.92736 7.8949C2.88975 7.8756 2.84573 7.8756 2.75769 7.8756L1.88603 7.87559C1.63453 7.87558 1.50878 7.87558 1.45819 7.96269C1.40759 8.0498 1.46788 8.15548 1.58846 8.36685C2.2106 9.45742 3.18686 10.5632 4.58832 11.6005C5.43495 12.2275 6.05634 12.6877 6.98387 12.6877C7.91141 12.6877 8.53279 12.2275 9.37943 11.6005C11.8175 9.79586 12.9688 7.78416 13.2073 6.00548C13.4447 4.23535 12.7682 2.69472 11.5658 1.95717Z" fill="#8F9AA3"/>
|
||||
<path d="M7.20469 4.97105C7.10463 4.78727 6.91379 4.67117 6.70458 4.66679C6.49537 4.66242 6.29984 4.77044 6.19218 4.94988L4.8987 7.10568L4.50819 6.5618C4.50002 6.55043 4.49145 6.53935 4.48248 6.52859C4.47338 6.51766 4.4634 6.50547 4.45255 6.49222C4.32787 6.33991 4.08822 6.04716 3.74091 5.92081C3.49697 5.83206 3.23003 5.83265 2.965 5.83324L2.90072 5.83333H1.73405C1.41188 5.83333 1.15072 6.0945 1.15072 6.41667C1.15072 6.73883 1.41188 7 1.73405 7H2.90072C3.27245 7 3.31696 7.00823 3.34111 7.01684C3.34329 7.01792 3.36229 7.02776 3.40145 7.06546C3.45005 7.11226 3.4979 7.16947 3.57276 7.25931L4.46854 8.50689C4.58307 8.6664 4.77018 8.75758 4.96638 8.74951C5.16258 8.74143 5.34156 8.63518 5.44259 8.46679L6.66782 6.42473L7.16648 7.34054C7.17206 7.35078 7.17794 7.36086 7.18412 7.37075L7.20842 7.40983C7.30282 7.56206 7.41744 7.74688 7.59523 7.88343C7.98366 8.18176 8.4448 8.17225 8.67006 8.16761C8.69423 8.16711 8.71569 8.16667 8.73405 8.16667H9.31738C9.63955 8.16667 9.90072 7.9055 9.90072 7.58333C9.90072 7.26117 9.63955 7 9.31738 7H8.73405C8.69863 7 8.66723 7.00017 8.63892 7.00032C8.53614 7.00086 8.47421 7.00119 8.40933 6.99253C8.35288 6.98499 8.32768 6.97346 8.31135 6.96218C8.30864 6.95892 8.30403 6.95313 8.29726 6.94384C8.27206 6.90926 8.24113 6.86063 8.18271 6.76722L7.20469 4.97105Z" fill="#8F9AA3"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.3 KiB |
@ -0,0 +1,15 @@
|
||||
import 'package:freerasp/freerasp.dart';
|
||||
|
||||
final talsecConfig = TalsecConfig(
|
||||
androidConfig: AndroidConfig(
|
||||
packageName: 'com.cloudsolutions.HMGPatientApp',
|
||||
signingCertHashes: ['6tvWaoN5coG4SnfxGbdQlcLmM0J4ePQwDjrKIg+QkV0=', 'j6VEqVhrypHMIiXiFdRLDdGwjGaMGWY7KAdBJA+Z4Pc='], // Must be the release cert hash
|
||||
supportedStores: ['com.android.vending'], // Google Play Store
|
||||
),
|
||||
iosConfig: IOSConfig(
|
||||
bundleIds: ['com.cloudsolutions.HMGPatientApp'], // iOS Bundle ID
|
||||
teamId: '3A359E86ZF', // Found in Apple Developer portal
|
||||
),
|
||||
watcherMail: '', // Required to receive security alerts
|
||||
isProd: true, // Enforces strict checks for release builds
|
||||
);
|
||||
@ -0,0 +1,230 @@
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hmg_patient_app_new/core/app_state.dart';
|
||||
import 'package:hmg_patient_app_new/core/dependencies.dart';
|
||||
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
|
||||
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
|
||||
|
||||
import '../../../generated/locale_keys.g.dart';
|
||||
|
||||
class LiveCareTermsConditions extends StatelessWidget {
|
||||
const LiveCareTermsConditions({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isArabic = getIt.get<AppState>().isArabic();
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: AppColors.scaffoldBgColor,
|
||||
body: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: CollapsingListView(
|
||||
title: LocaleKeys.livecare.tr(context: context),
|
||||
child: SingleChildScrollView(
|
||||
padding: EdgeInsets.symmetric(horizontal: 24.w, vertical: 16.h),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildHeading(context, LocaleKeys.liveCareTermsHeading.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildHeading(context, LocaleKeys.liveCareTermsIntroHeading.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions1.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions2.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildHeading(context, LocaleKeys.liveCareTermsHeading2.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions3.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions3a.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions3b.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions3c.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions3d.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions4.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions5.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions6.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildHeading(context, LocaleKeys.liveCareTermsHeading3.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions7.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions8.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions9.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions10.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions11.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions12.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions13.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildHeading(context, LocaleKeys.liveCareTermsHeading4.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions14.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions15.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions16.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildHeading(context, LocaleKeys.liveCareTermsHeading5.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions17.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions18.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions19.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions19a.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions19b.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions19c.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions19d.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions19e.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions20.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildHeading(context, LocaleKeys.liveCareTermsHeading6.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions21.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions22.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions23.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildHeading(context, LocaleKeys.liveCareTermsHeading7.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions24.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions25.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions26.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions27.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions28.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions29.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions30.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildHeading(context, LocaleKeys.liveCareTermsHeading8.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions31.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildHeading(context, LocaleKeys.liveCareTermsHeading9.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions32.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions33.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions34.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions35.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildHeading(context, LocaleKeys.liveCareTermsHeading10.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions36.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildHeading(context, LocaleKeys.liveCareTermsHeading11.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions37.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions38.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildHeading(context, LocaleKeys.liveCareTermsHeading12.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions39.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions40.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions41.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildHeading(context, LocaleKeys.liveCareTermsHeading13.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions42.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildHeading(context, LocaleKeys.liveCareTermsHeading14.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions43.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions43a.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions43b.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions43c.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions43d.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions43e.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions43f.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions43g.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildHeading(context, LocaleKeys.liveCareTermsHeading15.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions44.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions45.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions46.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildHeading(context, LocaleKeys.liveCareTermsHeading16.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions47.tr(context: context), isArabic),
|
||||
SizedBox(height: 12.h),
|
||||
_buildContent(context, LocaleKeys.liveCareTermsConditions48.tr(context: context), isArabic),
|
||||
SizedBox(height: 24.h),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Helper method to build heading text with consistent styling
|
||||
Widget _buildHeading(BuildContext context, String text, bool isArabic) {
|
||||
return Text(
|
||||
text,
|
||||
style: TextStyle(
|
||||
fontSize: 20.f,
|
||||
fontFamily: isArabic ? 'CairoArabic' : 'Poppins',
|
||||
fontWeight: FontWeight.w700,
|
||||
color: AppColors.textColor,
|
||||
letterSpacing: -0.5,
|
||||
height: 1.4,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Helper method to build content text with consistent styling
|
||||
Widget _buildContent(BuildContext context, String text, bool isArabic) {
|
||||
return Text(
|
||||
text,
|
||||
style: TextStyle(
|
||||
fontSize: 16.f,
|
||||
fontFamily: isArabic ? 'CairoArabic' : 'Poppins',
|
||||
color: AppColors.textColor,
|
||||
letterSpacing: -0.3,
|
||||
height: 1.5,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,157 @@
|
||||
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:freerasp/freerasp.dart';
|
||||
import 'package:hmg_patient_app_new/core/app_state.dart';
|
||||
import 'package:hmg_patient_app_new/core/talsec_config.dart';
|
||||
import 'package:hmg_patient_app_new/services/logger_service.dart';
|
||||
|
||||
/// Enum to categorize threat severity levels
|
||||
enum ThreatSeverity {
|
||||
critical, // Block app usage
|
||||
warning, // Log only, don't block
|
||||
}
|
||||
|
||||
/// Model to track threat details
|
||||
class ThreatEvent {
|
||||
final String threatType;
|
||||
final ThreatSeverity severity;
|
||||
final DateTime timestamp;
|
||||
final String? additionalInfo;
|
||||
|
||||
ThreatEvent({
|
||||
required this.threatType,
|
||||
required this.severity,
|
||||
this.additionalInfo,
|
||||
}) : timestamp = DateTime.now();
|
||||
}
|
||||
|
||||
/// Callback type for critical threat detection
|
||||
typedef OnCriticalThreatDetected = void Function();
|
||||
|
||||
/// Abstract class defining the security service interface
|
||||
abstract class SecurityService {
|
||||
/// Initialize and start the security monitoring
|
||||
Future<void> initialize();
|
||||
|
||||
/// Check if device is currently safe
|
||||
bool get isSafeDevice;
|
||||
|
||||
/// Get list of detected threats
|
||||
List<ThreatEvent> get detectedThreats;
|
||||
|
||||
/// Set callback for when critical threat is detected
|
||||
void setOnCriticalThreatCallback(OnCriticalThreatDetected callback);
|
||||
}
|
||||
|
||||
/// Implementation of SecurityService using Talsec (freeRASP)
|
||||
class SecurityServiceImpl implements SecurityService {
|
||||
final AppState appState;
|
||||
final LoggerService loggerService;
|
||||
|
||||
final List<ThreatEvent> _detectedThreats = [];
|
||||
bool _isInitialized = false;
|
||||
OnCriticalThreatDetected? _onCriticalThreatCallback;
|
||||
|
||||
SecurityServiceImpl({
|
||||
required this.appState,
|
||||
required this.loggerService,
|
||||
});
|
||||
|
||||
@override
|
||||
bool get isSafeDevice => appState.isSafeDevice;
|
||||
|
||||
@override
|
||||
List<ThreatEvent> get detectedThreats => List.unmodifiable(_detectedThreats);
|
||||
|
||||
@override
|
||||
Future<void> initialize() async {
|
||||
if (_isInitialized) {
|
||||
loggerService.logInfo('SecurityService already initialized');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
loggerService.logInfo('Initializing SecurityService with Talsec');
|
||||
|
||||
// Start the RASP engine
|
||||
await Talsec.instance.start(talsecConfig);
|
||||
|
||||
// Setup threat callbacks
|
||||
final callback = ThreatCallback(
|
||||
onAppIntegrity: () => _handleThreat('App Integrity', ThreatSeverity.critical),
|
||||
onObfuscationIssues: () => _handleThreat('Obfuscation Issues', ThreatSeverity.warning),
|
||||
onDebug: () => _handleThreat('Debug Mode', ThreatSeverity.critical),
|
||||
onDeviceBinding: () => _handleThreat('Device Binding', ThreatSeverity.critical),
|
||||
onDeviceID: () => _handleThreat('Device ID Mismatch', ThreatSeverity.critical),
|
||||
onHooks: () => _handleThreat('Hooks Detected', ThreatSeverity.critical),
|
||||
onPasscode: () => _handleThreat('Passcode Not Set', ThreatSeverity.warning),
|
||||
onPrivilegedAccess: () => _handleThreat('Privileged Access (Root/Jailbreak)', ThreatSeverity.critical),
|
||||
onSecureHardwareNotAvailable: () => _handleThreat('Secure Hardware Not Available', ThreatSeverity.warning),
|
||||
onSimulator: () => _handleThreat('Simulator/Emulator Detected', ThreatSeverity.critical),
|
||||
onSystemVPN: () => _handleThreat('System VPN Active', ThreatSeverity.warning),
|
||||
onDevMode: () => _handleThreat('Developer Mode', ThreatSeverity.warning),
|
||||
onADBEnabled: () => _handleThreat('USB Debugging Enabled', ThreatSeverity.warning),
|
||||
onUnofficialStore: () => _handleThreat('Unofficial Store Installation', ThreatSeverity.critical),
|
||||
onScreenshot: () => _handleThreat('Screenshot Detected', ThreatSeverity.warning),
|
||||
onScreenRecording: () => _handleThreat('Screen Recording Active', ThreatSeverity.warning),
|
||||
onMultiInstance: () => _handleThreat('Multiple Instances', ThreatSeverity.warning),
|
||||
onLocationSpoofing: () => _handleThreat('Location Spoofing', ThreatSeverity.warning),
|
||||
onTimeSpoofing: () => _handleThreat('Time Spoofing', ThreatSeverity.warning),
|
||||
onAutomation: () => _handleThreat('Automation Detected', ThreatSeverity.warning),
|
||||
onBootloader: () => _handleThreat('Unlocked Bootloader', ThreatSeverity.critical),
|
||||
onMalware: (suspiciousApps) => _handleThreat('Malware/Suspicious Apps', ThreatSeverity.critical, additionalInfo: suspiciousApps.toString()),
|
||||
);
|
||||
|
||||
Talsec.instance.attachListener(callback);
|
||||
|
||||
_isInitialized = true;
|
||||
loggerService.logInfo('SecurityService initialized successfully');
|
||||
} catch (e) {
|
||||
loggerService.logError('Failed to initialize SecurityService: $e');
|
||||
if (!kDebugMode) {
|
||||
FirebaseCrashlytics.instance.recordError(
|
||||
e,
|
||||
StackTrace.current,
|
||||
reason: 'SecurityService initialization failed',
|
||||
fatal: false,
|
||||
);
|
||||
}
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void setOnCriticalThreatCallback(OnCriticalThreatDetected callback) {
|
||||
_onCriticalThreatCallback = callback;
|
||||
loggerService.logInfo('Critical threat callback registered');
|
||||
}
|
||||
|
||||
/// Handle detected threats with appropriate severity
|
||||
void _handleThreat(String threatType, ThreatSeverity severity, {String? additionalInfo}) {
|
||||
final threat = ThreatEvent(
|
||||
threatType: threatType,
|
||||
severity: severity,
|
||||
additionalInfo: additionalInfo,
|
||||
);
|
||||
|
||||
_detectedThreats.add(threat);
|
||||
|
||||
// Log to console
|
||||
if (severity == ThreatSeverity.critical) {
|
||||
loggerService.logError('🔴 CRITICAL THREAT: $threatType ${additionalInfo != null ? "- $additionalInfo" : ""}');
|
||||
} else {
|
||||
loggerService.logInfo('⚠️ WARNING: $threatType ${additionalInfo != null ? "- $additionalInfo" : ""}');
|
||||
}
|
||||
|
||||
// Block app if critical threat
|
||||
if (severity == ThreatSeverity.critical) {
|
||||
appState.setIsSafeDevice = false;
|
||||
loggerService.logError('Device marked as UNSAFE due to: $threatType');
|
||||
|
||||
// Trigger callback to navigate to unsafe device page
|
||||
if (_onCriticalThreatCallback != null) {
|
||||
_onCriticalThreatCallback!();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,119 @@
|
||||
import 'package:clarity_flutter/clarity_flutter.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:freerasp/freerasp.dart';
|
||||
import 'package:hmg_patient_app_new/core/app_assets.dart';
|
||||
import 'package:hmg_patient_app_new/core/dependencies.dart';
|
||||
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
|
||||
import 'package:hmg_patient_app_new/core/utils/utils.dart';
|
||||
import 'package:hmg_patient_app_new/services/security_service.dart';
|
||||
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||
|
||||
class UnsafeDevice extends StatefulWidget {
|
||||
const UnsafeDevice({super.key});
|
||||
|
||||
@override
|
||||
State<UnsafeDevice> createState() => _UnsafeDeviceState();
|
||||
}
|
||||
|
||||
class _UnsafeDeviceState extends State<UnsafeDevice> {
|
||||
@override
|
||||
void initState() {
|
||||
Clarity.setCurrentScreenName('Unsafe Device Page');
|
||||
Talsec.instance.detachListener();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: AppColors.whiteColor,
|
||||
body: SafeArea(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 24.w),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
// Logo
|
||||
Utils.buildImgWithAssets(icon: AppAssets.hmgLogo, width: MediaQuery.of(context).size.width * 0.7, height: 90.h, fit: BoxFit.contain),
|
||||
SizedBox(height: 32.h),
|
||||
// Warning Icon
|
||||
Icon(
|
||||
Icons.security,
|
||||
size: 80.h,
|
||||
color: AppColors.primaryRedColor,
|
||||
),
|
||||
SizedBox(height: 24.h),
|
||||
// Title
|
||||
Text(
|
||||
'Unsafe Device Detected',
|
||||
style: TextStyle(
|
||||
fontSize: 24.f,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: AppColors.primaryRedColor,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
SizedBox(height: 16.h),
|
||||
|
||||
// Description
|
||||
Text(
|
||||
'For your security, this app cannot run on devices with security vulnerabilities.',
|
||||
style: TextStyle(
|
||||
fontSize: 16.f,
|
||||
color: Colors.black87,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
SizedBox(height: 24.h),
|
||||
if (getIt.get<SecurityService>().detectedThreats.isNotEmpty) ...[
|
||||
Text(
|
||||
'Detected Issues:',
|
||||
style: TextStyle(
|
||||
fontSize: 14.f,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black87,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 8.h),
|
||||
Container(
|
||||
padding: EdgeInsets.all(12.w),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.red.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(8.r),
|
||||
border: Border.all(color: Colors.red.withOpacity(0.3)),
|
||||
),
|
||||
child: Column(
|
||||
children: getIt
|
||||
.get<SecurityService>()
|
||||
.detectedThreats
|
||||
.where((t) => t.severity == ThreatSeverity.critical)
|
||||
.take(5) // Show max 5 threats
|
||||
.map((threat) => Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 4.h),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.error_outline, size: 16.f, color: AppColors.primaryRedColor),
|
||||
SizedBox(width: 8.w),
|
||||
Expanded(
|
||||
child: Text(
|
||||
threat.threatType,
|
||||
style: TextStyle(fontSize: 12.f),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
))
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 24.h),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue