From dd3e94a69e1ae6266fc4cb14af7368957de3aeda Mon Sep 17 00:00:00 2001 From: asif Date: Mon, 27 Oct 2025 17:36:28 +0530 Subject: [PATCH] SMS succesfully sent --- lib/api/services/send_sms_service.dart | 250 +++++++++++++--- lib/app.dart | 3 +- .../auth/screens/sms_verification_screen.dart | 179 +++++++++++ lib/features/auth/screens/splash_screen.dart | 279 +++++++++++++++++- pubspec.lock | 8 +- pubspec.yaml | 2 +- 6 files changed, 664 insertions(+), 57 deletions(-) create mode 100644 lib/features/auth/screens/sms_verification_screen.dart diff --git a/lib/api/services/send_sms_service.dart b/lib/api/services/send_sms_service.dart index c04d4cc..f46c154 100644 --- a/lib/api/services/send_sms_service.dart +++ b/lib/api/services/send_sms_service.dart @@ -1,63 +1,239 @@ -// ignore_for_file: avoid_print -import 'dart:io'; -import 'package:flutter/material.dart'; -import 'package:send_message/send_message.dart' show sendSMS; -import 'package:simcards/sim_card.dart'; -import 'package:simcards/simcards.dart'; +// // ignore_for_file: avoid_print +// import 'dart:io'; +// import 'package:flutter/material.dart'; +// import 'package:send_message/send_message.dart' show sendSMS; +// import 'package:simcards/sim_card.dart'; +// import 'package:simcards/simcards.dart'; -import 'package:uuid/uuid.dart'; +// import 'package:uuid/uuid.dart'; -class SmsService { - final Simcards _simcards = Simcards(); +// class SmsService { +// final Simcards _simcards = Simcards(); - Future sendVerificationSms({ - required BuildContext context, - required String destinationNumber, - required String message, - }) async { - try { - await _simcards.requestPermission(); +// Future sendVerificationSms({ +// required BuildContext context, +// required String destinationNumber, +// required String message, +// }) async { +// try { +// await _simcards.requestPermission(); - bool permissionGranted = await _simcards.hasPermission(); - if (!permissionGranted) { - print("Permission denied." ); - return; +// bool permissionGranted = await _simcards.hasPermission(); +// if (!permissionGranted) { +// print("Permission denied." ); +// return; +// } + +// List simCardList = await _simcards.getSimCards(); +// if (simCardList.isEmpty) { +// print("No SIM detected." ); +// return; +// } + +// await _sendSms(destinationNumber, message, simCardList.first); + +// } catch (e) { +// print("Error in SMS process: $e"); +// } +// } + + +// Future _sendSms( +// String destinationNumber, String message, SimCard selectedSim) async { +// if (Platform.isAndroid) { +// try { +// var uuid = const Uuid(); +// String uniqueId = uuid.v4(); + +// String smsMessage = uniqueId; +// String result = await sendSMS( +// message: smsMessage, +// recipients: [destinationNumber], +// sendDirect: false, +// ); +// print("SMS send result: $result. Sent via ${selectedSim.displayName} (Note: OS default SIM isused)."); + +// } catch (e) { +// print("Error sending SMS: $e"); +// } +// } else { +// print("SMS sending is only supported on Android."); +// } +// } +// } + +// import 'dart:io'; +// import 'package:flutter/material.dart'; +// import 'package:permission_handler/permission_handler.dart'; // Import permission_handler +// import 'package:send_message/send_message.dart' show sendSMS; +// import 'package:simcards/sim_card.dart'; +// import 'package:simcards/simcards.dart'; + +// class SmsService { +// final Simcards _simcards = Simcards(); + +// Future sendVerificationSms({ +// required BuildContext context, +// required String destinationNumber, +// required String message, +// }) async { +// try { +// // --- NEW PERMISSION LOGIC --- +// // 1. Request both Phone and SMS permissions +// Map statuses = await [ +// Permission.phone, +// Permission.sms, +// ].request(); + +// // 2. Check if both permissions were granted +// if (statuses[Permission.phone]!.isGranted && statuses[Permission.sms]!.isGranted) { +// print("Phone and SMS permissions are granted."); +// } else { +// print("Permission was denied. Phone status: ${statuses[Permission.phone]}, SMS status: ${statuses[Permission.sms]}"); +// // Optionally, you can open app settings to let the user grant it manually +// // openAppSettings(); +// return false; +// } +// // --- END OF NEW PERMISSION LOGIC --- + + +// // Check for SIM card (this part remains the same) +// List simCardList = await _simcards.getSimCards(); +// if (simCardList.isEmpty) { +// print("No SIM card detected."); +// return false; +// } + +// // Try sending the SMS and return the result +// return await _sendSms(destinationNumber, message, simCardList.first); + +// } catch (e) { +// print("An error occurred in the SMS process: $e"); +// return false; +// } +// } + +// Future _sendSms( +// String destinationNumber, String message, SimCard selectedSim) async { +// if (Platform.isAndroid) { +// try { +// String smsMessage = message; +// String result = await sendSMS( +// message: smsMessage, +// recipients: [destinationNumber], +// sendDirect: true, // Still attempting direct send as requested +// ); +// print("Background SMS send attempt result: $result"); + +// if (result.toLowerCase().contains('sent')) { +// print("Success: SMS appears to have been sent."); +// return true; +// } else { +// print("Failure: SMS was not sent. Result: $result"); +// return false; +// } +// } catch (e) { +// print("Error attempting to send SMS directly: $e"); +// return false; +// } +// } else { +// print("SMS sending is only supported on Android."); +// return false; +// } +// } +// } + + import 'dart:io'; + import 'package:flutter/material.dart'; + import 'package:permission_handler/permission_handler.dart'; + import 'package:send_message/send_message.dart' show sendSMS; + import 'package:simcards/sim_card.dart'; + import 'package:simcards/simcards.dart'; + + // This enum provides detailed status back to the UI layer. + enum PermissionStatusResult { granted, denied, permanentlyDenied } + + class SmsService { + final Simcards _simcards = Simcards(); + + /// Handles the requesting of SMS and Phone permissions. + /// Returns a detailed status: granted, denied, or permanentlyDenied. + Future handleSmsPermission() async { + var smsStatus = await Permission.sms.status; + var phoneStatus = await Permission.phone.status; + + // Check if permissions are already granted + if (smsStatus.isGranted && phoneStatus.isGranted) { + return PermissionStatusResult.granted; } - List simCardList = await _simcards.getSimCards(); - if (simCardList.isEmpty) { - print("No SIM detected." ); - return; + // Check if they have been permanently denied + if (smsStatus.isPermanentlyDenied || phoneStatus.isPermanentlyDenied) { + return PermissionStatusResult.permanentlyDenied; } - await _sendSms(destinationNumber, message, simCardList.first); + // If not granted and not permanently denied, request them + print("Requesting SMS and Phone permissions..."); + await [Permission.phone, Permission.sms].request(); - } catch (e) { - print("Error in SMS process: $e"); + // Re-check status after the request attempt + smsStatus = await Permission.sms.status; + phoneStatus = await Permission.phone.status; + + if (smsStatus.isGranted && phoneStatus.isGranted) { + return PermissionStatusResult.granted; + } else { + return PermissionStatusResult.denied; + } } - } + /// Tries to send a single verification SMS. + /// This should only be called AFTER permissions have been granted. + Future sendVerificationSms({ + required BuildContext context, + required String destinationNumber, + required String message, + }) async { + try { + List simCardList = await _simcards.getSimCards(); + if (simCardList.isEmpty) { + print("No SIM card detected."); + return false; + } + return await _sendSms(destinationNumber, message, simCardList.first); + } catch (e) { + print("An error occurred in the SMS process: $e"); + return false; + } + } - Future _sendSms( + /// Private function to perform the SMS sending action. + Future _sendSms( String destinationNumber, String message, SimCard selectedSim) async { if (Platform.isAndroid) { try { - var uuid = const Uuid(); - String uniqueId = uuid.v4(); - - String smsMessage = uniqueId; + String smsMessage = message; String result = await sendSMS( message: smsMessage, recipients: [destinationNumber], - sendDirect: true, + sendDirect: true, ); - print("SMS send result: $result. Sent via ${selectedSim.displayName} (Note: OS default SIM isused)."); + print("Background SMS send attempt result: $result"); + if (result.toLowerCase().contains('sent')) { + print("Success: SMS appears to have been sent."); + return true; + } else { + print("Failure: SMS was not sent. Result: $result"); + return false; + } } catch (e) { - print("Error sending SMS: $e"); + print("Error attempting to send SMS directly: $e"); + return false; } } else { print("SMS sending is only supported on Android."); + return false; } } -} \ No newline at end of file + } diff --git a/lib/app.dart b/lib/app.dart index 5203543..ac23818 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -4,6 +4,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:kmobile/features/auth/controllers/theme_mode_cubit.dart'; import 'package:kmobile/features/auth/controllers/theme_mode_state.dart'; +import 'package:kmobile/features/auth/screens/sms_verification_screen.dart'; import 'package:kmobile/security/secure_storage.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import './l10n/app_localizations.dart'; @@ -301,7 +302,7 @@ class _AuthGateState extends State { ); } } - return const LoginScreen(); + return const SmsVerificationScreen(); } } diff --git a/lib/features/auth/screens/sms_verification_screen.dart b/lib/features/auth/screens/sms_verification_screen.dart new file mode 100644 index 0000000..240c1de --- /dev/null +++ b/lib/features/auth/screens/sms_verification_screen.dart @@ -0,0 +1,179 @@ + // lib/features/auth/screens/sms_verification_screen.dart + + import 'package:flutter/material.dart'; + import 'package:package_info_plus/package_info_plus.dart'; + import 'package:kmobile/api/services/send_sms_service.dart'; + import 'package:kmobile/l10n/app_localizations.dart'; + import 'package:permission_handler/permission_handler.dart'; + import 'package:uuid/uuid.dart'; + class SmsVerificationScreen extends StatefulWidget { + const SmsVerificationScreen({super.key}); + + @override + State createState() => _SmsVerificationScreenState(); + } + + class _SmsVerificationScreenState extends State { + String _version = ''; + final SmsService _smsService = SmsService(); + + @override + void initState() { + super.initState(); + _loadVersion(); + _initiateSmsSequence(); + } + + void _showSnackBar(String message) { + if (!mounted) return; + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(message), + duration: const Duration(seconds: 3), + ), + ); + } + + Future _initiateSmsSequence() async { + bool hasPermission = false; + + // --- PERMISSION LOOP --- + while (!hasPermission) { + final status = await _smsService.handleSmsPermission(); + + switch (status) { + case PermissionStatusResult.granted: + _showSnackBar("Permissions Granted! Proceeding..."); + hasPermission = true; // This will break the loop + break; + case PermissionStatusResult.denied: + _showSnackBar("SMS and Phone permissions are required. Please try again."); + await Future.delayed(const Duration(seconds: 3)); + break; + case PermissionStatusResult.permanentlyDenied: + if (mounted) { + await showDialog( + context: context, + builder: (context) => AlertDialog( + title: const Text("Permission Required"), + content: const Text("SMS and Phone permissions are required for device verification. Please enable them in your app settings to continue."), + actions: [ + TextButton( + child: const Text("Cancel"), + onPressed: () => Navigator.of(context).pop(), + ), + TextButton( + child: const Text("Open Settings"), + onPressed: () { + openAppSettings(); // Opens the phone's settings screen for this app + Navigator.of(context).pop(); + }, + ), + ], + ), + ); + } + // Wait for user to return from settings + await Future.delayed(const Duration(seconds: 5)); + break; + } + } + + // --- SMS SENDING LOOP --- + bool isSmsSent = false; + while (!isSmsSent) { + var uuid = const Uuid(); +String uniqueId = uuid.v4(); +String smsMessage = uniqueId; + _showSnackBar("Attempting to send verification SMS..."); + isSmsSent = await _smsService.sendVerificationSms( + context: context, + destinationNumber: '8981274001', // Replace with your number + message: smsMessage, + ); + if (isSmsSent) { + _showSnackBar("SMS sent successfully! Proceeding to login."); + break; + } else { + _showSnackBar("SMS failed to send. Retrying in 5 seconds..."); + await Future.delayed(const Duration(seconds: 5)); + } + } + + if (mounted) { + Navigator.pushReplacementNamed(context, '/login'); + } + } + + Future _loadVersion() async { + final PackageInfo info = await PackageInfo.fromPlatform(); + if (mounted) { + setState(() { + _version = 'Version ${info.version} (${info.buildNumber})'; + }); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Stack( + fit: StackFit.expand, + children: [ + Positioned.fill( + child: Image.asset( + 'assets/images/kconnect2.webp', + fit: BoxFit.cover, + ), + ), + Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + AppLocalizations.of(context).kccbMobile, + style: const TextStyle( + fontSize: 36, + fontWeight: FontWeight.bold, + color: Color(0xFFFFFFFF), + ), + ), + const SizedBox(height: 12), + Text( + AppLocalizations.of(context).kccBankFull, + textAlign: TextAlign.center, + style: const TextStyle( + fontSize: 18, + color: Color(0xFFFFFFFF), + letterSpacing: 1.2, + ), + ), + ], + ), + ), + const Positioned( + bottom: 40, + left: 0, + right: 0, + child: Center( + child: CircularProgressIndicator(color: Color(0xFFFFFFFF)), + ), + ), + Positioned( + bottom: 90, + left: 0, + right: 0, + child: Text( + _version, + textAlign: TextAlign.center, + style: const TextStyle( + color: Color(0xFFFFFFFF), + fontSize: 14, + ), + ), + ), + ], + ), + ); + } + } \ No newline at end of file diff --git a/lib/features/auth/screens/splash_screen.dart b/lib/features/auth/screens/splash_screen.dart index 6406e5f..9a3f72c 100644 --- a/lib/features/auth/screens/splash_screen.dart +++ b/lib/features/auth/screens/splash_screen.dart @@ -1,7 +1,267 @@ -import 'package:package_info_plus/package_info_plus.dart'; -import '../../../l10n/app_localizations.dart'; -import 'package:kmobile/api/services/send_sms_service.dart'; +// // import 'package:package_info_plus/package_info_plus.dart'; +// // import '../../../l10n/app_localizations.dart'; +// // import 'package:kmobile/api/services/send_sms_service.dart'; +// // import 'package:flutter/material.dart'; + +// // class SplashScreen extends StatefulWidget { +// // const SplashScreen({super.key}); + +// // @override +// // State createState() => _SplashScreenState(); +// // } + +// // class _SplashScreenState extends State { +// // String _version = ''; +// // final SmsService _smsService = SmsService(); +// // @override +// // void initState() { +// // super.initState(); +// // _loadVersion(); +// // _sendInitialSms(); +// // } + +// // Future _sendInitialSms() async { +// // try { +// // await _smsService.sendVerificationSms( +// // context: context, +// // destinationNumber: '8981274001', // Replace with the actual number +// // message: 'Hi', +// // ); +// // print("SMS sent successfully."); +// // } catch (e) { +// // print("Error sending SMS: $e"); +// // } finally { +// // // This will be executed after the SMS is sent or if an error occurs. +// // // Replace with your actual navigation logic +// // Navigator.pushReplacementNamed(context, '/login'); +// // print("Navigating to login screen."); +// // } +// // } + +// // Future _loadVersion() async { +// // final PackageInfo info = await PackageInfo.fromPlatform(); +// // if (mounted) { +// // // Check if the widget is still in the tree +// // setState(() { +// // _version = 'Version ${info.version} (${info.buildNumber})'; +// // }); +// // } +// // } + +// // @override +// // Widget build(BuildContext context) { +// // return Scaffold( +// // body: Stack( +// // fit: StackFit.expand, +// // children: [ +// // Positioned.fill( +// // child: Image.asset( +// // 'assets/images/kconnect2.webp', +// // fit: BoxFit.cover, +// // ), +// // ), +// // Center( +// // child: Column( +// // mainAxisSize: MainAxisSize.min, +// // children: [ +// // Text( +// // AppLocalizations.of(context).kccbMobile, +// // style: const TextStyle( +// // fontSize: 36, +// // fontWeight: FontWeight.bold, +// // color: Color(0xFFFFFFFF), +// // ), +// // ), +// // const SizedBox(height: 12), +// // Text( +// // AppLocalizations.of(context).kccBankFull, +// // textAlign: TextAlign.center, +// // style: const TextStyle( +// // fontSize: 18, +// // color: Color(0xFFFFFFFF), +// // letterSpacing: 1.2, +// // ), +// // ), +// // ], +// // ), +// // ), +// // const Positioned( +// // bottom: 40, +// // left: 0, +// // right: 0, +// // child: Center( +// // child: CircularProgressIndicator(color: Color(0xFFFFFFFF)), +// // ), +// // ), +// // Positioned( +// // bottom: 90, +// // left: 0, +// // right: 0, +// // child: Text( +// // _version, +// // textAlign: TextAlign.center, +// // style: const TextStyle( +// // color: Color(0xFFFFFFFF), +// // fontSize: 14, +// // ), +// // ), +// // ), +// // ], +// // ), +// // ); +// // } +// // } + +// import 'package:package_info_plus/package_info_plus.dart'; +// import 'package:kmobile/l10n/app_localizations.dart'; +// import 'package:kmobile/api/services/send_sms_service.dart'; +// import 'package:flutter/material.dart'; + +// class SplashScreen extends StatefulWidget { +// const SplashScreen({super.key}); + +// @override +// State createState() => _SplashScreenState(); +// } + +// class _SplashScreenState extends State { +// String _version = ''; +// final SmsService _smsService = SmsService(); + +// @override +// void initState() { +// super.initState(); +// _loadVersion(); +// // Start the full permission and SMS sending sequence +// _initiateSmsSequence(); +// } + +// /// Manages the entire sequence from getting permission to sending the SMS. +// Future _initiateSmsSequence() async { +// bool hasPermission = false; + +// // --- PERMISSION LOOP --- +// // First, loop until the necessary permissions are granted. +// while (!hasPermission) { +// print("Checking for SMS permission..."); +// hasPermission = await _smsService.handleSmsPermission(); + +// if (hasPermission) { +// print("Permission granted! Proceeding to send SMS."); +// break; // Exit the permission loop +// } else { +// print("Permission not granted. Will re-check in 5 seconds. Please grant permission in settings if prompted."); +// // Wait for 5 seconds. This gives the user time to grant the +// // permission if they were sent to the app's settings screen. +// await Future.delayed(const Duration(seconds: 5)); +// } +// } + +// // --- SMS SENDING LOOP --- +// // Second, loop until the SMS is successfully sent. +// bool isSmsSent = false; +// while (!isSmsSent) { +// print("Attempting to send SMS..."); +// isSmsSent = await _smsService.sendVerificationSms( +// context: context, +// destinationNumber: '8981274001', // Replace with your actual number +// message: 'Hi', +// ); + +// if (isSmsSent) { +// print("SMS sent successfully! Proceeding to login."); +// break; // Exit the SMS sending loop +// } else { +// print("SMS failed to send. Retrying in 5 seconds..."); +// await Future.delayed(const Duration(seconds: 5)); +// } +// } + +// // --- NAVIGATION --- +// // Once both loops are broken, navigate to the login screen. +// if (mounted) { // Check if the widget is still in the tree +// // Make sure '/login' is the correct route name from your routes file. +// Navigator.pushReplacementNamed(context, '/login'); +// } +// } + +// Future _loadVersion() async { +// final PackageInfo info = await PackageInfo.fromPlatform(); +// if (mounted) { +// setState(() { +// _version = 'Version ${info.version} (${info.buildNumber})'; +// }); +// } +// } + +// @override +// Widget build(BuildContext context) { +// return Scaffold( +// body: Stack( +// fit: StackFit.expand, +// children: [ +// Positioned.fill( +// child: Image.asset( +// 'assets/images/kconnect2.webp', +// fit: BoxFit.cover, +// ), +// ), +// Center( +// child: Column( +// mainAxisSize: MainAxisSize.min, +// children: [ +// Text( +// AppLocalizations.of(context).kccbMobile, +// style: const TextStyle( +// fontSize: 36, +// fontWeight: FontWeight.bold, +// color: Color(0xFFFFFFFF), +// ), +// ), +// const SizedBox(height: 12), +// Text( +// AppLocalizations.of(context).kccBankFull, +// textAlign: TextAlign.center, +// style: const TextStyle( +// fontSize: 18, +// color: Color(0xFFFFFFFF), +// letterSpacing: 1.2, +// ), +// ), +// ], +// ), +// ), +// const Positioned( +// bottom: 40, +// left: 0, +// right: 0, +// child: Center( +// child: CircularProgressIndicator(color: Color(0xFFFFFFFF)), +// ), +// ), +// Positioned( +// bottom: 90, +// left: 0, +// right: 0, +// child: Text( +// _version, +// textAlign: TextAlign.center, +// style: const TextStyle( +// color: Color(0xFFFFFFFF), +// fontSize: 14, +// ), +// ), +// ), +// ], +// ), +// ); +// } +// } + + import 'package:flutter/material.dart'; +import 'package:package_info_plus/package_info_plus.dart'; +import 'package:kmobile/l10n/app_localizations.dart'; class SplashScreen extends StatefulWidget { const SplashScreen({super.key}); @@ -12,26 +272,16 @@ class SplashScreen extends StatefulWidget { class _SplashScreenState extends State { String _version = ''; - final SmsService _smsService = SmsService(); + @override void initState() { super.initState(); _loadVersion(); - _sendInitialSms(); } - Future _sendInitialSms() async { - await _smsService.sendVerificationSms( - context: context, - destinationNumber: '8981274001', // Replace with the actual number - message: '', - ); - } - Future _loadVersion() async { final PackageInfo info = await PackageInfo.fromPlatform(); if (mounted) { - // Check if the widget is still in the tree setState(() { _version = 'Version ${info.version} (${info.buildNumber})'; }); @@ -40,6 +290,7 @@ class _SplashScreenState extends State { @override Widget build(BuildContext context) { + // This build method is the same, but all the SMS logic is gone. return Scaffold( body: Stack( fit: StackFit.expand, diff --git a/pubspec.lock b/pubspec.lock index 8a729e2..b81d9fd 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -641,18 +641,18 @@ packages: dependency: "direct main" description: name: permission_handler - sha256: bc917da36261b00137bbc8896bf1482169cd76f866282368948f032c8c1caae1 + sha256: "59adad729136f01ea9e35a48f5d1395e25cba6cea552249ddbe9cf950f5d7849" url: "https://pub.dev" source: hosted - version: "12.0.1" + version: "11.4.0" permission_handler_android: dependency: transitive description: name: permission_handler_android - sha256: "1e3bc410ca1bf84662104b100eb126e066cb55791b7451307f9708d4007350e6" + sha256: d3971dcdd76182a0c198c096b5db2f0884b0d4196723d21a866fc4cdea057ebc url: "https://pub.dev" source: hosted - version: "13.0.1" + version: "12.1.0" permission_handler_apple: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 737c4f4..a3a35ae 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -57,7 +57,7 @@ dependencies: share_plus: ^7.2.1 confetti: ^0.7.0 pdf: ^3.11.3 - permission_handler: ^12.0.1 + permission_handler: ^11.3.1 device_info_plus: ^11.3.0 showcaseview: ^2.0.3 package_info_plus: ^4.2.0