From c42c30658f32c678502ed1fcdd33274fc0bb6724 Mon Sep 17 00:00:00 2001 From: Sultan khan Date: Mon, 12 Oct 2026 16:44:10 +0300 Subject: [PATCH] no message --- lib/api/login_api_client.dart | 40 ++++++++++++- lib/classes/notifications.dart | 26 +++------ lib/dialogs/otp_dialog.dart | 96 +++++++++++++++++++++++++++---- lib/models/post_params_model.dart | 6 +- lib/ui/login/login_screen.dart | 1 + 5 files changed, 135 insertions(+), 34 deletions(-) diff --git a/lib/api/login_api_client.dart b/lib/api/login_api_client.dart index 22ecc7f..efd4122 100644 --- a/lib/api/login_api_client.dart +++ b/lib/api/login_api_client.dart @@ -13,6 +13,7 @@ import 'package:mohem_flutter_app/models/generic_response_model.dart'; import 'package:mohem_flutter_app/models/dependent/get_mobile_login_info_dependent_model.dart'; import 'package:mohem_flutter_app/models/get_mobile_login_info_list_model.dart'; import 'package:mohem_flutter_app/models/member_login_list_model.dart'; +import 'package:sms_otp_auto_verify/sms_otp_auto_verify.dart'; class LoginApiClient { static final LoginApiClient _instance = LoginApiClient._internal(); @@ -21,6 +22,25 @@ class LoginApiClient { factory LoginApiClient() => _instance; + static String? _cachedSignature; + + static Future getSignature() async { + if (!Platform.isAndroid) return null; + if (_cachedSignature != null && _cachedSignature!.isNotEmpty) { + return _cachedSignature; + } + try { + String? sig = await SmsVerification.getAppSignature(); + if (sig != null && sig.isNotEmpty) { + _cachedSignature = sig; + } + return sig; + } catch (e) { + debugPrint("Error getting app signature: $e"); + return null; + } + } + Future getMobileLoginInfoNEW(String deviceToken, String deviceType, {int? memberType}) async { String url = "${ApiConsts.erpRest}Mohemm_GetMobileLoginInfo_NEW"; Map postParams = {}; @@ -186,8 +206,15 @@ class LoginApiClient { }, url, postParams); } - Future mohemmSendActivationCodeByOTPNotificationType(int isMobileFingerPrint, String? mobileNumber, int optSendType, String? pUserName) async { + Future mohemmSendActivationCodeByOTPNotificationType( + int isMobileFingerPrint, + String? mobileNumber, + int optSendType, + String? pUserName, { + String? smsSignature, + }) async { String url = "${ApiConsts.erpRest}Mohemm_SendActivationCodebyOTPNotificationType"; + String? sig = smsSignature ?? await getSignature(); Map postParams = { "IsMobileFingerPrint": isMobileFingerPrint, "MobileNumber": mobileNumber, @@ -197,6 +224,8 @@ class LoginApiClient { "NotificationCenterAppSeviceGUID": ApiConsts.notificationCenterLoginServiceGUID, }; postParams.addAll(AppState().postParamsJson); + postParams["smsSignature"] = sig ?? ""; + postParams["SMSSignature"] = sig ?? ""; return await ApiClient().postJsonForObject((json) => GenericResponseModel.fromJson(json).basicMemberInformation, url, postParams); } @@ -228,8 +257,9 @@ class LoginApiClient { return await ApiClient().postJsonForObject((json) => GenericResponseModel.fromJson(json).basicMemberInformation, url, postParams); } - Future sendPublicActivationCode(String? mobileNumber, String? pUsername) async { + Future sendPublicActivationCode(String? mobileNumber, String? pUsername, {String? smsSignature}) async { String url = "${ApiConsts.erpRest}SendPublicActivationCode"; + String? sig = smsSignature ?? await getSignature(); Map postParams = { "MobileNumber": mobileNumber, "P_MOBILE_NUMBER": mobileNumber, @@ -238,6 +268,8 @@ class LoginApiClient { "NotificationCenterAppSeviceGUID": ApiConsts.notificationCenterForgotPasswordServiceGUID, }; postParams.addAll(AppState().postParamsJson); + postParams["smsSignature"] = sig ?? ""; + postParams["SMSSignature"] = sig ?? ""; return await ApiClient().postJsonForObject((json) { GenericResponseModel responseData = GenericResponseModel.fromJson(json); AppState().postParamsObject?.setLogInTokenID = responseData.logInTokenID; @@ -361,8 +393,10 @@ class LoginApiClient { required String mobileNumber, required String logInTokenID, BuildContext? context, + String? smsSignature, }) async { String url = "${ApiConsts.erpRest}Mohemm_SendOTP_Dependent"; + String? sig = smsSignature ?? await getSignature(); Map postParams = { "VersionID": 20, "Channel": 31, @@ -374,6 +408,8 @@ class LoginApiClient { "P_DEP_NATIONAL_ID": nationalId, "P_USER_NAME": nationalId, "LogInTokenID": logInTokenID, + "smsSignature": sig ?? "", + "SMSSignature": sig ?? "", }; return await ApiClient().postJsonForObject((json) { GenericResponseModel responseData = GenericResponseModel.fromJson(json); diff --git a/lib/classes/notifications.dart b/lib/classes/notifications.dart index 058606c..9769756 100644 --- a/lib/classes/notifications.dart +++ b/lib/classes/notifications.dart @@ -2,7 +2,6 @@ import 'dart:io'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; @@ -13,7 +12,6 @@ import 'package:mohem_flutter_app/classes/date_uitl.dart'; import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:mohem_flutter_app/models/get_notifications_response_model.dart'; import 'package:mohem_flutter_app/ui/notifications/notification_details_page.dart'; -import 'package:permission_handler/permission_handler.dart'; final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); @@ -29,20 +27,14 @@ class AppNotifications { String _huaweiToken = ''; late BuildContext context; + bool _isInitialized = false; Future requestPermissions() async { if (Platform.isIOS) { await flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation()?.requestPermissions(alert: true, badge: true, sound: true); } else if (Platform.isAndroid) { - AndroidFlutterLocalNotificationsPlugin? androidImplementation = flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation(); - bool? granted = await androidImplementation?.requestNotificationsPermission(); - if (granted == false) { - if (kDebugMode) { - print("-------------------- Permission Granted ------------------------"); - print(granted); - } - await Permission.notification.request(); - } + AndroidFlutterLocalNotificationsPlugin? androidImplementation = flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation(); + await androidImplementation?.requestNotificationsPermission(); } } @@ -53,13 +45,11 @@ class AppNotifications { this.context = context; - await requestPermissions(); + if (!_isInitialized) { + _isInitialized = true; + await requestPermissions(); + } AppState().setDeviceToken = firebaseToken; - await Permission.notification.isDenied.then((bool value) { - if (value) { - Permission.notification.request(); - } - }); RemoteMessage? initialMessage = await FirebaseMessaging.instance.getInitialMessage(); if (initialMessage != null) _handleMessage(initialMessage); @@ -80,7 +70,7 @@ class AppNotifications { // await hmsApiAvailability.isHMSAvailable().then((value) async { if (!(await Utils.isGoogleServicesAvailable())) { huawei_push.Push.enableLogger(); - var result = await huawei_push.Push.setAutoInitEnabled(true); + await huawei_push.Push.setAutoInitEnabled(true); huawei_push.Push.onNotificationOpenedApp.listen((message) { // newMessage(toFirebaseRemoteMessage(message)); diff --git a/lib/dialogs/otp_dialog.dart b/lib/dialogs/otp_dialog.dart index b80b5e6..a5ab8ec 100644 --- a/lib/dialogs/otp_dialog.dart +++ b/lib/dialogs/otp_dialog.dart @@ -1,14 +1,17 @@ import 'dart:async'; +import 'dart:io'; import 'package:easy_localization/src/public_ext.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:mohem_flutter_app/api/login_api_client.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/generated/locale_keys.g.dart'; import 'package:mohem_flutter_app/widgets/button/default_button.dart'; import 'package:mohem_flutter_app/widgets/otp_widget.dart'; +import 'package:sms_otp_auto_verify/sms_otp_auto_verify.dart'; final ValueNotifier otpFieldClear = ValueNotifier(""); @@ -24,10 +27,6 @@ class OtpDialog { Future? timer; - static BuildContext? _context; - - static bool? _loading; - OtpDialog(this.context, this.type, this.mobileNo, this.onSuccess, this.onFailure, {required this.onResendCode}); GlobalKey? verifyAccountForm = GlobalKey(); @@ -53,7 +52,6 @@ class OtpDialog { // ProjectViewModel projectProvider; String displayTime = ''; - String? _code; dynamic setState; bool stopTimer = false; bool isCodeExpired = false; @@ -62,6 +60,7 @@ class OtpDialog { void displayDialog(BuildContext context) async { otpFieldClear.value = ""; + _startListeningForSms(); showDialog( context: context, barrierColor: Colors.black.withOpacity(0.63), @@ -203,6 +202,7 @@ class OtpDialog { ); }).then((_) { stopTimer = true; + _stopListeningForSms(); }); } @@ -277,28 +277,100 @@ class OtpDialog { stopTimer = true; }); } catch (_) {} + _stopListeningForSms(); } }); } void hideSMSBox(context) { stopTimer = true; + _stopListeningForSms(); onFailure(); } void _onOtpCallBack(String otpCode, bool? isAutofill) { if (isCodeExpired) return; if (otpCode.length == 4) { + _stopListeningForSms(); otpFieldClear.value = otpCode; onSuccess(otpCode, _pinPutController); } } -// static getSignature() async { -// // if (Platform.isAndroid) { -// // return await SmsRetriever.getAppSignature(); -// // } else { -// // return null; -// // } -// } + void _startListeningForSms() { + if (!Platform.isAndroid) return; + try { + SmsVerification.startListeningSms().then((String? message) { + if (stopTimer || isCodeExpired) return; + if (message != null && message.isNotEmpty) { + debugPrint("SMS OTP Auto-Verify received message: $message"); + String? otpCode = extractOtpCode(message); + if (otpCode != null && otpCode.length == 4) { + debugPrint("Auto-filling OTP: $otpCode"); + _pinPutController.text = otpCode; + _pinPutController.selection = TextSelection.collapsed(offset: otpCode.length); + _onOtpCallBack(otpCode, true); + } + } + }).catchError((e) { + debugPrint("Error in startListeningSms: $e"); + }); + } catch (e) { + debugPrint("Error starting SMS listening: $e"); + } + } + + void _stopListeningForSms() { + if (!Platform.isAndroid) return; + try { + SmsVerification.stopListening(); + } catch (e) { + debugPrint("Error stopping SMS listening: $e"); + } + } + + static String? extractOtpCode(String? message) { + if (message == null || message.trim().isEmpty) return null; + String normalized = _normalizeDigits(message); + + // 1. Try matching with OTP context keyword if available + RegExp contextRegex = RegExp( + r'(?:code|otp|رمز|كود|تفعيل|تحقق)[^\d]{0,15}(\d{4})\b', + caseSensitive: false, + ); + Match? contextMatch = contextRegex.firstMatch(normalized); + if (contextMatch != null && contextMatch.group(1) != null) { + return contextMatch.group(1); + } + + // 2. Try exact 4-digit word boundary match + Iterable matches = RegExp(r'\b\d{4}\b').allMatches(normalized); + if (matches.isNotEmpty) { + return matches.first.group(0); + } + + // 3. Fallback + try { + Iterable allDigits = RegExp(r'\d{4}').allMatches(normalized); + if (allDigits.isNotEmpty) { + return allDigits.first.group(0); + } + } catch (_) {} + + return null; + } + + static String _normalizeDigits(String input) { + const String arabic = '٠١٢٣٤٥٦٧٨٩'; + const String english = '0123456789'; + String res = input; + for (int i = 0; i < arabic.length; i++) { + res = res.replaceAll(arabic[i], english[i]); + } + return res; + } + + static Future getSignature() async { + return await LoginApiClient.getSignature(); + } } diff --git a/lib/models/post_params_model.dart b/lib/models/post_params_model.dart index ac2a690..1762cf6 100644 --- a/lib/models/post_params_model.dart +++ b/lib/models/post_params_model.dart @@ -1,5 +1,3 @@ -import 'package:mohem_flutter_app/app_state/app_state.dart'; - class PostParamsModel { double? versionID; int? channel; @@ -15,6 +13,7 @@ class PostParamsModel { String? payrollCodeStr; int? pSessionId; String? userName; + String? sMSSignature; PostParamsModel({ this.versionID, @@ -31,6 +30,7 @@ class PostParamsModel { this.pSelectedEmployeeNumber, this.payrollCodeStr, this.pLegislationCode, + this.sMSSignature, }); PostParamsModel.fromJson(Map json) { @@ -95,4 +95,6 @@ class PostParamsModel { set setPLegislationCode(String? v) => pLegislationCode = v?.trim().toUpperCase(); set setPayrollCodeStr(String? v) => payrollCodeStr = v; + set setSMSSignature(String? v) => sMSSignature = v; + set setSmsSignature(String? v) => sMSSignature = v; } diff --git a/lib/ui/login/login_screen.dart b/lib/ui/login/login_screen.dart index 9957cc1..8cd4c71 100644 --- a/lib/ui/login/login_screen.dart +++ b/lib/ui/login/login_screen.dart @@ -784,6 +784,7 @@ class _LoginScreenState extends State { ); Utils.hideLoading(context); Utils.showToast(LocaleKeys.verificationCodeResent.tr()); + _showDependentOtpDialog(logInTokenID, mobileNumber); } catch (ex) { Utils.hideLoading(context); if (Utils.isMazayaAccessRevoked(ex)) {