diff --git a/lib/classes/consts.dart b/lib/classes/consts.dart index 7509e7f..119e418 100644 --- a/lib/classes/consts.dart +++ b/lib/classes/consts.dart @@ -2,8 +2,8 @@ import 'package:mohem_flutter_app/ui/marathon/widgets/question_card.dart'; class ApiConsts { //static String baseUrl = "http://10.200.204.20:2801/"; // Local server - //static String baseUrl = "https://uat.hmgwebservices.com"; // UAT server - static String baseUrl = "https://hmgwebservices.com"; // Live server + // static String baseUrl = "https://uat.hmgwebservices.com"; // UAT server + static String baseUrl = "https://hmgwebservices.com"; // Live server static String baseUrlServices = baseUrl + "/Services/"; // server // static String baseUrlServices = "https://api.cssynapses.com/tangheem/"; // Live server static String utilitiesRest = baseUrlServices + "Utilities.svc/REST/"; diff --git a/lib/classes/utils.dart b/lib/classes/utils.dart index 33d9830..3b30d05 100644 --- a/lib/classes/utils.dart +++ b/lib/classes/utils.dart @@ -86,6 +86,11 @@ class Utils { return prefs.getString(key) ?? ""; } + static Future removeStringFromPrefs(String key) async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + return prefs.remove(key); + } + static Future saveStringFromPrefs(String key, String value) async { SharedPreferences prefs = await SharedPreferences.getInstance(); return await prefs.setString(key, value); diff --git a/lib/config/routes.dart b/lib/config/routes.dart index d1b6b08..365a1c0 100644 --- a/lib/config/routes.dart +++ b/lib/config/routes.dart @@ -67,6 +67,7 @@ import 'package:mohem_flutter_app/ui/screens/pending_transactions/pending_transa import 'package:mohem_flutter_app/ui/screens/pending_transactions/pending_transactions_details.dart'; import 'package:mohem_flutter_app/ui/screens/submenu_screen.dart'; import 'package:mohem_flutter_app/ui/termination/end_employement.dart'; +import 'package:mohem_flutter_app/ui/unsafe_device_screen.dart'; import 'package:mohem_flutter_app/ui/work_list/item_history_screen.dart'; import 'package:mohem_flutter_app/ui/work_list/itg_detail_screen.dart'; import 'package:mohem_flutter_app/ui/work_list/work_list_screen.dart'; @@ -191,6 +192,8 @@ class AppRoutes { static const String marathonSponsorVideoScreen = "/marathonSponsorVideoScreen"; static const String marathonWaitingScreen = "/marathonWaitingScreen"; + static const String unsafeDeviceScreen = "/unsafeDeviceScreen"; + static final Map routes = { login: (BuildContext context) => LoginScreen(), verifyLogin: (BuildContext context) => VerifyLoginScreen(), @@ -300,5 +303,7 @@ class AppRoutes { marathonWinnerScreen: (BuildContext context) => WinnerScreen(), marathonSponsorVideoScreen: (BuildContext context) => const SponsorVideoScreen(), marathonWaitingScreen: (BuildContext context) => const MarathonWaitingScreen(), + + unsafeDeviceScreen: (BuildContext context) => const UnsafeDeviceScreen(), }; } diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart index 3261149..01b7a08 100644 --- a/lib/extensions/string_extensions.dart +++ b/lib/extensions/string_extensions.dart @@ -90,8 +90,9 @@ extension EmailValidator on String { style: TextStyle(fontSize: 13, fontWeight: FontWeight.w600, color: color ?? MyColors.darkTextColor, letterSpacing: -0.52, decoration: isUnderLine ? TextDecoration.underline : null), ); - Widget toText14({Color? color, bool isUnderLine = false, bool isBold = false, FontWeight? weight, int? maxlines}) => Text( + Widget toText14({Color? color, bool isUnderLine = false, bool isBold = false, FontWeight? weight, int? maxlines, bool isCenter = false}) => Text( this, + textAlign: isCenter ? TextAlign.center : TextAlign.left, maxLines: maxlines, style: TextStyle( color: color ?? MyColors.darkTextColor, diff --git a/lib/ui/login/login_screen.dart b/lib/ui/login/login_screen.dart index e225459..a0ab3c9 100644 --- a/lib/ui/login/login_screen.dart +++ b/lib/ui/login/login_screen.dart @@ -25,6 +25,7 @@ import 'package:mohem_flutter_app/models/member_login_list_model.dart'; import 'package:mohem_flutter_app/models/privilege_list_model.dart'; import 'package:mohem_flutter_app/widgets/button/default_button.dart'; import 'package:mohem_flutter_app/widgets/input_widget.dart'; +import 'package:safe_device/safe_device.dart'; class LoginScreen extends StatefulWidget { LoginScreen({Key? key}) : super(key: key); @@ -48,10 +49,34 @@ class _LoginScreenState extends State { bool? isAppOpenBySystem; + bool isJailBroken = false; + bool isRealDevice = false; + bool isOnExternalStorage = false; + bool isDevelopmentModeEnable = false; + @override void initState() { super.initState(); - // checkFirebaseToken(); + // checkFirebaseToken(); + if (kReleaseMode) { + checkDeviceSafety(); + } + } + + void checkDeviceSafety() async { + try { + isJailBroken = await SafeDevice.isJailBroken; + isRealDevice = await SafeDevice.isRealDevice; + if (Platform.isAndroid) { + isOnExternalStorage = await SafeDevice.isOnExternalStorage; + isDevelopmentModeEnable = await SafeDevice.isDevelopmentModeEnable; + } + if (isJailBroken || !isRealDevice || isOnExternalStorage || isDevelopmentModeEnable) { + Navigator.pushNamedAndRemoveUntil(context, AppRoutes.unsafeDeviceScreen, (_) => false); + } + } catch (error) { + print(error); + } } @override @@ -68,8 +93,7 @@ class _LoginScreenState extends State { await Firebase.initializeApp(); _firebaseMessaging = FirebaseMessaging.instance; firebaseToken = await _firebaseMessaging.getToken(); - loginInfo = await LoginApiClient().getMobileLoginInfoNEW( - firebaseToken ?? "", Platform.isAndroid ? "android" : "ios"); + loginInfo = await LoginApiClient().getMobileLoginInfoNEW(firebaseToken ?? "", Platform.isAndroid ? "android" : "ios"); if (loginInfo == null) { Utils.hideLoading(context); return; @@ -86,11 +110,9 @@ class _LoginScreenState extends State { } Future checkPrefs() async { - String username = - await Utils.getStringFromPrefs(SharedPrefsConsts.username); + String username = await Utils.getStringFromPrefs(SharedPrefsConsts.username); if (username.isNotEmpty) { - String password = - await Utils.getStringFromPrefs(SharedPrefsConsts.password); + String password = await Utils.getStringFromPrefs(SharedPrefsConsts.password); // String firebaseToken = await Utils.getStringFromPrefs(SharedPrefsConsts.firebaseToken); // print("firebaseToken:$firebaseToken"); this.username.text = username; @@ -103,30 +125,23 @@ class _LoginScreenState extends State { Utils.showLoading(context); try { _checkMobileAppVersion = await LoginApiClient().checkMobileAppVersion(); - _memberLoginList = - await LoginApiClient().memberLogin(username.text, password.text); + _memberLoginList = await LoginApiClient().memberLogin(username.text, password.text); AppState().setMemberLoginListModel = _memberLoginList; AppState().setUserName = username.text; AppState().password = password.text; if (_autoLogin) { - AppState().setMemberInformationListModel = - (await MemberInformationListModel.getFromPrefs()).first; - AppState().setPrivilegeListModel = - await PrivilegeListModel.getFromPrefs(); - String mohemmWifiSSID = - await Utils.getStringFromPrefs(SharedPrefsConsts.mohemmWifiSSID); - String mohemmWifiPassword = await Utils.getStringFromPrefs( - SharedPrefsConsts.mohemmWifiPassword); + AppState().setMemberInformationListModel = (await MemberInformationListModel.getFromPrefs()).first; + AppState().setPrivilegeListModel = await PrivilegeListModel.getFromPrefs(); + String mohemmWifiSSID = await Utils.getStringFromPrefs(SharedPrefsConsts.mohemmWifiSSID); + String mohemmWifiPassword = await Utils.getStringFromPrefs(SharedPrefsConsts.mohemmWifiPassword); AppState().setMohemmWifiSSID = mohemmWifiSSID; AppState().setMohemmWifiPassword = mohemmWifiPassword; } Utils.hideLoading(context); if (_autoLogin) { - Navigator.pushReplacementNamed(context, AppRoutes.verifyLastLogin, - arguments: loginInfo); + Navigator.pushReplacementNamed(context, AppRoutes.verifyLastLogin, arguments: loginInfo); } else { - Navigator.pushNamed(context, AppRoutes.verifyLogin, - arguments: "$firebaseToken"); + Navigator.pushNamed(context, AppRoutes.verifyLogin, arguments: "$firebaseToken"); } Utils.saveStringFromPrefs(SharedPrefsConsts.password, password.text); } catch (ex) { @@ -169,13 +184,7 @@ class _LoginScreenState extends State { Expanded(child: SizedBox()), Row( children: [ - LocaleKeys.english - .tr() - .toText14( - color: AppState().isArabic(context) - ? null - : MyColors.textMixColor) - .onPress(() { + LocaleKeys.english.tr().toText14(color: AppState().isArabic(context) ? null : MyColors.textMixColor).onPress(() { context.setLocale(const Locale("en", "US")); }), Container( @@ -184,13 +193,7 @@ class _LoginScreenState extends State { height: 16, margin: const EdgeInsets.only(left: 10, right: 10), ), - LocaleKeys.arabic - .tr() - .toText14( - color: !AppState().isArabic(context) - ? null - : MyColors.textMixColor) - .onPress(() { + LocaleKeys.arabic.tr().toText14(color: !AppState().isArabic(context) ? null : MyColors.textMixColor).onPress(() { context.setLocale(const Locale("ar", "SA")); }), ], @@ -206,23 +209,14 @@ class _LoginScreenState extends State { LocaleKeys.login.tr().toText24(isBold: true), LocaleKeys.pleaseEnterLoginDetails.tr().toText16(), 16.height, - InputWidget( - LocaleKeys.username.tr(), "123456", username), + InputWidget(LocaleKeys.username.tr(), "123456", username), 12.height, - InputWidget( - LocaleKeys.password.tr(), "xxxxxx", password, - isTextIsPassword: true), + InputWidget(LocaleKeys.password.tr(), "xxxxxx", password, isTextIsPassword: true), 9.height, Align( alignment: Alignment.centerRight, - child: LocaleKeys.forgotPassword - .tr() - .toText12( - isUnderLine: true, - color: MyColors.textMixColor) - .onPress(() { - Navigator.pushNamed( - context, AppRoutes.forgotPassword); + child: LocaleKeys.forgotPassword.tr().toText12(isUnderLine: true, color: MyColors.textMixColor).onPress(() { + Navigator.pushNamed(context, AppRoutes.forgotPassword); }), ), ], diff --git a/lib/ui/screens/items_for_sale/fragments/add_details_fragment.dart b/lib/ui/screens/items_for_sale/fragments/add_details_fragment.dart index f0f2c62..9b82d9d 100644 --- a/lib/ui/screens/items_for_sale/fragments/add_details_fragment.dart +++ b/lib/ui/screens/items_for_sale/fragments/add_details_fragment.dart @@ -127,6 +127,7 @@ class _AddItemDetailsFragmentState extends State { isPopup: false, lines: 1, isInputTypeNum: true, + isInputTypeNumSigned: false, isReadOnly: false, onChange: (String value) { itemPrice = num.parse(value); diff --git a/lib/ui/screens/items_for_sale/items_for_sale_home.dart b/lib/ui/screens/items_for_sale/items_for_sale_home.dart index 437b400..443c8ba 100644 --- a/lib/ui/screens/items_for_sale/items_for_sale_home.dart +++ b/lib/ui/screens/items_for_sale/items_for_sale_home.dart @@ -1,6 +1,8 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:mohem_flutter_app/classes/colors.dart'; +import 'package:mohem_flutter_app/classes/consts.dart'; +import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:mohem_flutter_app/config/routes.dart'; import 'package:mohem_flutter_app/extensions/int_extensions.dart'; import 'package:mohem_flutter_app/extensions/string_extensions.dart'; @@ -74,6 +76,7 @@ class _ItemsForSaleState extends State { child: const Icon(Icons.add, color: Colors.white, size: 30), ).onPress( () { + Utils.removeStringFromPrefs(SharedPrefsConsts.editItemForSale); Navigator.pushNamed(context, AppRoutes.addNewItemForSale); }, ), diff --git a/lib/ui/screens/offers_and_discounts/offers_and_discounts_details.dart b/lib/ui/screens/offers_and_discounts/offers_and_discounts_details.dart index 325f4f3..e9f46d7 100644 --- a/lib/ui/screens/offers_and_discounts/offers_and_discounts_details.dart +++ b/lib/ui/screens/offers_and_discounts/offers_and_discounts_details.dart @@ -143,7 +143,7 @@ class _OffersAndDiscountsDetailsState extends State { List getItemsForSaleWidgets() { List itemsList = []; - for (int i = 1; i < 5; i++) { + for (int i = 1; i < getOffersList.length; i++) { itemsList.add(getItemCard(getOffersList[i])); } return itemsList; diff --git a/lib/ui/screens/offers_and_discounts/offers_and_discounts_home.dart b/lib/ui/screens/offers_and_discounts/offers_and_discounts_home.dart index 343eed8..c62cc9c 100644 --- a/lib/ui/screens/offers_and_discounts/offers_and_discounts_home.dart +++ b/lib/ui/screens/offers_and_discounts/offers_and_discounts_home.dart @@ -80,39 +80,26 @@ class _OffersAndDiscountsHomeState extends State { // getItemsForSale(currentPageNo, currentCategoryID); }); }, - child: Container( - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(15), - boxShadow: [ - BoxShadow( - color: const Color(0xff000000).withOpacity(.05), - blurRadius: 26, - offset: const Offset(0, -3), - ), - ], - ), - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SvgPicture.string( - getCategoriesList[index].content!, - fit: BoxFit.contain, - width: 25, - height: 25, - ), - currentCategoryID == getCategoriesList[index].id ? const Icon(Icons.check_circle_rounded, color: MyColors.greenColor, size: 16.0) : Container(), - ], - ).expanded, - AppState().isArabic(context) ? getCategoriesList[index].categoryNameAr!.toText10(maxlines: 1) : getCategoriesList[index].categoryNameEn!.toText10(maxlines: 1) - ], - ).paddingOnly(left: 10, right: 10, bottom: 10, top: 12), - ), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SvgPicture.string( + getCategoriesList[index].content!, + fit: BoxFit.contain, + width: 25, + height: 25, + ), + currentCategoryID == getCategoriesList[index].id ? const Icon(Icons.check_circle_rounded, color: MyColors.greenColor, size: 16.0) : Container(), + ], + ).expanded, + AppState().isArabic(context) ? getCategoriesList[index].categoryNameAr!.toText10() : getCategoriesList[index].categoryNameEn!.toText10() + ], + ).paddingOnly(left: 10, right: 10, bottom: 10, top: 12).expanded.objectContainerView(disablePadding: true), ), ); }, diff --git a/lib/ui/unsafe_device_screen.dart b/lib/ui/unsafe_device_screen.dart new file mode 100644 index 0000000..dca8748 --- /dev/null +++ b/lib/ui/unsafe_device_screen.dart @@ -0,0 +1,72 @@ +import 'dart:io'; + +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:mohem_flutter_app/classes/colors.dart'; +import 'package:mohem_flutter_app/extensions/int_extensions.dart'; +import 'package:mohem_flutter_app/extensions/string_extensions.dart'; +import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; +import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; +import 'package:mohem_flutter_app/widgets/button/default_button.dart'; + +class UnsafeDeviceScreen extends StatefulWidget { + const UnsafeDeviceScreen({Key? key}) : super(key: key); + + @override + State createState() => _UnsafeDeviceScreenState(); +} + +class _UnsafeDeviceScreenState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + body: SafeArea( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + 21.height, + Center(child: Image.asset("assets/images/logos/main_mohemm_logo.png", width: 200, height: 50)), + 50.height, + "Sorry".toText24(isBold: true), + 21.height, + "You are using Mohemm app on an unsafe device. To be able to use the app with all it's features, Please make sure that the below points are considered: " + .toText14(isCenter: true) + .paddingOnly(left: 20.0, right: 20.0), + 48.height, + passwordConstraintsUI("The device is not jailbroken or rooted.", true).paddingOnly(left: 24.0, right: 5.0), + 8.height, + passwordConstraintsUI("The app is not installed on external storage.", true).paddingOnly(left: 24.0, right: 5.0), + 8.height, + passwordConstraintsUI("Development mode is disabled.", true).paddingOnly(left: 24.0, right: 5.0), + 21.height, + DefaultButton(LocaleKeys.ok.tr(), () async { + if (Platform.isAndroid) { + SystemChannels.platform.invokeMethod('SystemNavigator.pop'); + } else { + // MinimizeApp.minimizeApp(); + } + }).paddingAll(24) + ], + ), + ), + ); + } + + Widget passwordConstraintsUI(String description, bool check) { + return Row( + children: [ + 4.width, + SizedBox( + width: 12, + height: 12, + child: Checkbox(fillColor: MaterialStateProperty.all(MyColors.gradiantEndColor), shape: const CircleBorder(), value: check, onChanged: null), + ), + 8.width, + description.toText14() + ], + ); + } +} diff --git a/lib/widgets/dynamic_forms/dynamic_textfield_widget.dart b/lib/widgets/dynamic_forms/dynamic_textfield_widget.dart index 91e0ce1..299ef7a 100644 --- a/lib/widgets/dynamic_forms/dynamic_textfield_widget.dart +++ b/lib/widgets/dynamic_forms/dynamic_textfield_widget.dart @@ -4,6 +4,7 @@ import 'package:mohem_flutter_app/classes/colors.dart'; class DynamicTextFieldWidget extends StatelessWidget { final String labelText; final String hintText; + // final TextEditingController controller; final VoidCallback? onTap; final IconData? suffixIconData; @@ -13,6 +14,7 @@ class DynamicTextFieldWidget extends StatelessWidget { final bool isPopup; final int? lines; final bool isInputTypeNum; + final bool isInputTypeNumSigned; final bool isObscureText; final bool isBackgroundEnable; final void Function(String)? onChange; @@ -28,6 +30,7 @@ class DynamicTextFieldWidget extends StatelessWidget { this.inputAction, this.onChange, this.isInputTypeNum = false, + this.isInputTypeNumSigned = true, this.isBackgroundEnable = false}); @override @@ -63,8 +66,13 @@ class DynamicTextFieldWidget extends StatelessWidget { ), TextField( enabled: isEnable, - scrollPadding: EdgeInsets.zero, readOnly: isReadOnly, - keyboardType: isInputTypeNum ? const TextInputType.numberWithOptions(signed: true) : TextInputType.text, + scrollPadding: EdgeInsets.zero, + readOnly: isReadOnly, + keyboardType: (isInputTypeNum) + ? isInputTypeNumSigned + ? const TextInputType.numberWithOptions(signed: true) + : TextInputType.number + : TextInputType.text, textInputAction: TextInputAction.done, //controller: controller, maxLines: lines, diff --git a/lib/widgets/item_detail_view_widget.dart b/lib/widgets/item_detail_view_widget.dart index 5148389..ccdedea 100644 --- a/lib/widgets/item_detail_view_widget.dart +++ b/lib/widgets/item_detail_view_widget.dart @@ -37,9 +37,9 @@ class ItemDetailViewCol extends StatelessWidget { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - "$title:".toText12(isBold: true, color: const Color(0xff2BB8A6)), + "$title:".toText12(isBold: true, color: const Color(0xff2BB8A6), maxLine: 2), 4.width, - (value.isEmpty ? "--" : value).toText12(color: MyColors.normalTextColor), + (value.isEmpty ? "--" : value).toText12(color: MyColors.normalTextColor, maxLine: 2), ], ); } diff --git a/pubspec.yaml b/pubspec.yaml index 4fc00c8..39afde3 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -99,6 +99,8 @@ dependencies: video_player: ^2.4.7 just_audio: ^0.9.30 + safe_device: ^1.1.2 + dev_dependencies: flutter_test: sdk: flutter