Test Sim Binding APK
This commit is contained in:
@@ -8,73 +8,40 @@ class AuthService {
|
|||||||
final Dio _dio;
|
final Dio _dio;
|
||||||
AuthService(this._dio);
|
AuthService(this._dio);
|
||||||
|
|
||||||
Future <void> simVerify(String uuid, String cifNo) async{
|
Future<void> simVerify(String uuid, String cifNo) async {
|
||||||
|
try {
|
||||||
|
final response = await _dio.post('/api/sim-details-verify', data: {
|
||||||
|
'uuid': uuid,
|
||||||
|
'cifNo': cifNo,
|
||||||
|
});
|
||||||
|
|
||||||
try{
|
if (response.statusCode == 200) {
|
||||||
|
final String message = response.data.toString().toUpperCase();
|
||||||
final response = await _dio.post(
|
|
||||||
|
|
||||||
'/api/sim-details-verify',
|
|
||||||
|
|
||||||
data: {
|
|
||||||
|
|
||||||
'uuid': uuid,
|
|
||||||
|
|
||||||
'cifNo': cifNo,
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
);
|
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
|
||||||
|
|
||||||
final String message = response.data.toString().toUpperCase();
|
|
||||||
|
|
||||||
if (message.contains("VERIFIED")) {
|
|
||||||
|
|
||||||
return; // Success
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
throw AuthException(message); // Throw message received
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (message.contains("VERIFIED")) {
|
||||||
|
return; // Success
|
||||||
} else {
|
} else {
|
||||||
|
throw AuthException(message); // Throw message received
|
||||||
throw AuthException('Verification Failed');
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
} on DioException catch (e) {
|
throw AuthException('Verification Failed');
|
||||||
|
}
|
||||||
if (kDebugMode) {
|
} on DioException catch (e) {
|
||||||
|
if (kDebugMode) {
|
||||||
print(e.toString());
|
print(e.toString());
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (e.response?.statusCode == 401) {
|
|
||||||
|
|
||||||
throw AuthException(
|
|
||||||
|
|
||||||
e.response?.data['error'] ?? 'SOMETHING WENT WRONG');
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
throw NetworkException('Network error during verification');
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
catch (e) {
|
if (e.response?.statusCode == 401) {
|
||||||
|
throw AuthException(
|
||||||
throw UnexpectedException(
|
e.response?.data['error'] ?? 'SOMETHING WENT WRONG');
|
||||||
|
|
||||||
'Unexpected error during verification: ${e.toString()}');
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
throw NetworkException('Network error during verification');
|
||||||
|
} catch (e) {
|
||||||
|
throw UnexpectedException(
|
||||||
|
'Unexpected error during verification: ${e.toString()}');
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<AuthToken> login(AuthCredentials credentials) async {
|
Future<AuthToken> login(AuthCredentials credentials) async {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1,102 +1,102 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:permission_handler/permission_handler.dart';
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
import 'package:send_message/send_message.dart' show sendSMS;
|
import 'package:send_message/send_message.dart' show sendSMS;
|
||||||
import 'package:simcards/sim_card.dart';
|
import 'package:simcards/sim_card.dart';
|
||||||
import 'package:simcards/simcards.dart';
|
import 'package:simcards/simcards.dart';
|
||||||
|
|
||||||
// This enum provides detailed status back to the UI layer.
|
// This enum provides detailed status back to the UI layer.
|
||||||
enum PermissionStatusResult { granted, denied, permanentlyDenied, restricted }
|
enum PermissionStatusResult { granted, denied, permanentlyDenied, restricted }
|
||||||
|
|
||||||
class SmsService {
|
class SmsService {
|
||||||
final Simcards _simcards = Simcards();
|
final Simcards _simcards = Simcards();
|
||||||
|
|
||||||
/// Handles the requesting of SMS and Phone permissions.
|
/// Handles the requesting of SMS and Phone permissions.
|
||||||
/// Returns a detailed status: granted, denied, or permanentlyDenied.
|
/// Returns a detailed status: granted, denied, or permanentlyDenied.
|
||||||
Future<PermissionStatusResult> handleSmsPermission() async {
|
Future<PermissionStatusResult> handleSmsPermission() async {
|
||||||
var smsStatus = await Permission.sms.status;
|
var smsStatus = await Permission.sms.status;
|
||||||
var phoneStatus = await Permission.phone.status;
|
var phoneStatus = await Permission.phone.status;
|
||||||
|
|
||||||
// Check initial status
|
// Check initial status
|
||||||
if (smsStatus.isGranted && phoneStatus.isGranted) {
|
if (smsStatus.isGranted && phoneStatus.isGranted) {
|
||||||
return PermissionStatusResult.granted;
|
return PermissionStatusResult.granted;
|
||||||
}
|
}
|
||||||
if (smsStatus.isPermanentlyDenied || phoneStatus.isPermanentlyDenied) {
|
if (smsStatus.isPermanentlyDenied || phoneStatus.isPermanentlyDenied) {
|
||||||
return PermissionStatusResult.permanentlyDenied;
|
return PermissionStatusResult.permanentlyDenied;
|
||||||
}
|
}
|
||||||
if (smsStatus.isRestricted || phoneStatus.isRestricted) {
|
if (smsStatus.isRestricted || phoneStatus.isRestricted) {
|
||||||
return PermissionStatusResult.restricted;
|
return PermissionStatusResult.restricted;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Request permissions if not granted
|
// Request permissions if not granted
|
||||||
print("Requesting SMS and Phone permissions...");
|
print("Requesting SMS and Phone permissions...");
|
||||||
await [Permission.phone, Permission.sms].request();
|
await [Permission.phone, Permission.sms].request();
|
||||||
|
|
||||||
// Re-check status after request
|
// Re-check status after request
|
||||||
smsStatus = await Permission.sms.status;
|
smsStatus = await Permission.sms.status;
|
||||||
phoneStatus = await Permission.phone.status;
|
phoneStatus = await Permission.phone.status;
|
||||||
|
|
||||||
if (smsStatus.isGranted && phoneStatus.isGranted) {
|
if (smsStatus.isGranted && phoneStatus.isGranted) {
|
||||||
return PermissionStatusResult.granted;
|
return PermissionStatusResult.granted;
|
||||||
}
|
}
|
||||||
if (smsStatus.isPermanentlyDenied || phoneStatus.isPermanentlyDenied) {
|
if (smsStatus.isPermanentlyDenied || phoneStatus.isPermanentlyDenied) {
|
||||||
return PermissionStatusResult.permanentlyDenied;
|
return PermissionStatusResult.permanentlyDenied;
|
||||||
}
|
}
|
||||||
if (smsStatus.isRestricted || phoneStatus.isRestricted) {
|
if (smsStatus.isRestricted || phoneStatus.isRestricted) {
|
||||||
return PermissionStatusResult.restricted;
|
return PermissionStatusResult.restricted;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If none of the above, it's denied
|
// If none of the above, it's denied
|
||||||
return PermissionStatusResult.denied;
|
return PermissionStatusResult.denied;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tries to send a single verification SMS.
|
/// Tries to send a single verification SMS.
|
||||||
/// This should only be called AFTER permissions have been granted.
|
/// This should only be called AFTER permissions have been granted.
|
||||||
Future<bool> sendVerificationSms({
|
Future<bool> sendVerificationSms({
|
||||||
required BuildContext context,
|
required BuildContext context,
|
||||||
required String destinationNumber,
|
required String destinationNumber,
|
||||||
required String message,
|
required String message,
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
List<SimCard> simCardList = await _simcards.getSimCards();
|
List<SimCard> simCardList = await _simcards.getSimCards();
|
||||||
if (simCardList.isEmpty) {
|
if (simCardList.isEmpty) {
|
||||||
print("No SIM card detected.");
|
print("No SIM card detected.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return await _sendSms(destinationNumber, message, simCardList.first);
|
return await _sendSms(destinationNumber, message, simCardList.first);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print("An error occurred in the SMS process: $e");
|
print("An error occurred in the SMS process: $e");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Private function to perform the SMS sending action.
|
/// Private function to perform the SMS sending action.
|
||||||
Future<bool> _sendSms(
|
Future<bool> _sendSms(
|
||||||
String destinationNumber, String message, SimCard selectedSim) async {
|
String destinationNumber, String message, SimCard selectedSim) async {
|
||||||
if (Platform.isAndroid) {
|
if (Platform.isAndroid) {
|
||||||
try {
|
try {
|
||||||
String smsMessage = message;
|
String smsMessage = message;
|
||||||
String result = await sendSMS(
|
String result = await sendSMS(
|
||||||
message: smsMessage,
|
message: smsMessage,
|
||||||
recipients: [destinationNumber],
|
recipients: [destinationNumber],
|
||||||
sendDirect: true,
|
sendDirect: true,
|
||||||
);
|
);
|
||||||
print("Background SMS send attempt result: $result");
|
print("Background SMS send attempt result: $result");
|
||||||
|
|
||||||
if (result.toLowerCase().contains('sent')) {
|
if (result.toLowerCase().contains('sent')) {
|
||||||
print("Success: SMS appears to have been sent.");
|
print("Success: SMS appears to have been sent.");
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
print("Failure: SMS was not sent. Result: $result");
|
print("Failure: SMS was not sent. Result: $result");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print("Error attempting to send SMS directly: $e");
|
print("Error attempting to send SMS directly: $e");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
print("SMS sending is only supported on Android.");
|
print("SMS sending is only supported on Android.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,8 +75,8 @@ Dio _createDioClient() {
|
|||||||
BaseOptions(
|
BaseOptions(
|
||||||
baseUrl:
|
baseUrl:
|
||||||
'http://lb-test-mobile-banking-app-192209417.ap-south-1.elb.amazonaws.com:8080', //test
|
'http://lb-test-mobile-banking-app-192209417.ap-south-1.elb.amazonaws.com:8080', //test
|
||||||
//'http://lb-kccb-mobile-banking-app-848675342.ap-south-1.elb.amazonaws.com', //prod
|
//'http://lb-kccb-mobile-banking-app-848675342.ap-south-1.elb.amazonaws.com', //prod
|
||||||
//'https://kccbmbnk.net', //prod small
|
//'https://kccbmbnk.net', //prod small
|
||||||
connectTimeout: const Duration(seconds: 60),
|
connectTimeout: const Duration(seconds: 60),
|
||||||
receiveTimeout: const Duration(seconds: 60),
|
receiveTimeout: const Duration(seconds: 60),
|
||||||
headers: {
|
headers: {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:kmobile/api/services/send_sms_service.dart';
|
import 'package:kmobile/api/services/send_sms_service.dart';
|
||||||
import 'package:permission_handler/permission_handler.dart';
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
@@ -12,7 +11,8 @@ class SmsVerificationHelper {
|
|||||||
context: context,
|
context: context,
|
||||||
builder: (context) => AlertDialog(
|
builder: (context) => AlertDialog(
|
||||||
title: const Text("Permission Required"),
|
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."),
|
content: const Text(
|
||||||
|
"SMS and Phone permissions are required for device verification. Please enable them in your app settings to continue."),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
child: const Text("Cancel"),
|
child: const Text("Cancel"),
|
||||||
@@ -39,17 +39,24 @@ class SmsVerificationHelper {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text("It seems your device is restricting this app from sending SMS messages, which is required for verification. Please follow these steps to enable it:\n"),
|
Text(
|
||||||
Text("1. Open your device Settings.", style: TextStyle(fontWeight: FontWeight.bold)),
|
"It seems your device is restricting this app from sending SMS messages, which is required for verification. Please follow these steps to enable it:\n"),
|
||||||
|
Text("1. Open your device Settings.",
|
||||||
|
style: TextStyle(fontWeight: FontWeight.bold)),
|
||||||
Text("2. Go to 'Apps' or 'Apps & notifications'."),
|
Text("2. Go to 'Apps' or 'Apps & notifications'."),
|
||||||
Text("3. Find and tap on this app ('KMobile')."),
|
Text("3. Find and tap on this app ('KMobile')."),
|
||||||
Text("4. Tap on the three dots (⋮) in the top right corner."),
|
Text("4. Tap on the three dots (⋮) in the top right corner."),
|
||||||
Text("5. Select 'Allow restricted settings' and confirm. This is crucial to allow SMS permission."),
|
Text(
|
||||||
|
"5. Select 'Allow restricted settings' and confirm. This is crucial to allow SMS permission."),
|
||||||
Text("6. Now you have two options to allow SMS permission:"),
|
Text("6. Now you have two options to allow SMS permission:"),
|
||||||
Text(" a. Tap on 'Permissions', then find 'SMS' is set to 'Allow'."),
|
Text(
|
||||||
Text(" b. Alternatively, you can return to the KMobile app, and the SMS permission pop-up should appear again, allowing you to grant it directly."),
|
" a. Tap on 'Permissions', then find 'SMS' is set to 'Allow'."),
|
||||||
Text("\nSome devices have an additional setting for 'Premium SMS'. If the above doesn't work, look for a 'Premium SMS access' setting (you can search for it in your Settings app) and set it to 'Always Allow' for this app.\n"),
|
Text(
|
||||||
Text("After you've enabled the permission, please come back to the app."),
|
" b. Alternatively, you can return to the KMobile app, and the SMS permission pop-up should appear again, allowing you to grant it directly."),
|
||||||
|
Text(
|
||||||
|
"\nSome devices have an additional setting for 'Premium SMS'. If the above doesn't work, look for a 'Premium SMS access' setting (you can search for it in your Settings app) and set it to 'Always Allow' for this app.\n"),
|
||||||
|
Text(
|
||||||
|
"After you've enabled the permission, please come back to the app."),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -63,15 +70,6 @@ class SmsVerificationHelper {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _showSnackBar(BuildContext context, String message) {
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
SnackBar(
|
|
||||||
content: Text(message),
|
|
||||||
duration: const Duration(seconds: 3),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<String?> initiateSmsSequence({
|
Future<String?> initiateSmsSequence({
|
||||||
required BuildContext context,
|
required BuildContext context,
|
||||||
}) async {
|
}) async {
|
||||||
@@ -85,7 +83,9 @@ class SmsVerificationHelper {
|
|||||||
switch (status) {
|
switch (status) {
|
||||||
case PermissionStatusResult.granted:
|
case PermissionStatusResult.granted:
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
const SnackBar(content: Text("Permissions Granted! Proceeding..."), duration: Duration(seconds: 2)),
|
const SnackBar(
|
||||||
|
content: Text("Permissions Granted! Proceeding..."),
|
||||||
|
duration: Duration(seconds: 2)),
|
||||||
);
|
);
|
||||||
hasPermission = true; // This will break the loop
|
hasPermission = true; // This will break the loop
|
||||||
break;
|
break;
|
||||||
@@ -96,7 +96,8 @@ class SmsVerificationHelper {
|
|||||||
context: context,
|
context: context,
|
||||||
builder: (context) => AlertDialog(
|
builder: (context) => AlertDialog(
|
||||||
title: const Text("Permission Required"),
|
title: const Text("Permission Required"),
|
||||||
content: const Text("This app requires SMS and Phone permissions to verify your device. Please grant the permissions to continue."),
|
content: const Text(
|
||||||
|
"This app requires SMS and Phone permissions to verify your device. Please grant the permissions to continue."),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
child: const Text("Cancel"),
|
child: const Text("Cancel"),
|
||||||
@@ -136,9 +137,12 @@ class SmsVerificationHelper {
|
|||||||
var uuid = const Uuid();
|
var uuid = const Uuid();
|
||||||
String uniqueId = uuid.v4();
|
String uniqueId = uuid.v4();
|
||||||
String smsMessage = uniqueId;
|
String smsMessage = uniqueId;
|
||||||
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(content: Text("Attempting to send verification SMS... (${4 - retries})"), duration: const Duration(seconds: 2)),
|
SnackBar(
|
||||||
|
content:
|
||||||
|
Text("Attempting to send verification SMS... (${4 - retries})"),
|
||||||
|
duration: const Duration(seconds: 2)),
|
||||||
);
|
);
|
||||||
|
|
||||||
bool isSmsSent = await _smsService.sendVerificationSms(
|
bool isSmsSent = await _smsService.sendVerificationSms(
|
||||||
@@ -148,21 +152,25 @@ class SmsVerificationHelper {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (isSmsSent) {
|
if (isSmsSent) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
const SnackBar(content: Text("SMS sent successfully!"), duration: Duration(seconds: 2)),
|
const SnackBar(
|
||||||
|
content: Text("SMS sent successfully!"),
|
||||||
|
duration: Duration(seconds: 2)),
|
||||||
);
|
);
|
||||||
return uniqueId;
|
return uniqueId;
|
||||||
} else {
|
} else {
|
||||||
retries--;
|
retries--;
|
||||||
if (retries > 0) {
|
if (retries > 0) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
const SnackBar(content: Text("SMS failed to send. Retrying in 5 seconds..."), duration: Duration(seconds: 4)),
|
const SnackBar(
|
||||||
|
content: Text("SMS failed to send. Retrying in 5 seconds..."),
|
||||||
|
duration: Duration(seconds: 4)),
|
||||||
);
|
);
|
||||||
await Future.delayed(const Duration(seconds: 5));
|
await Future.delayed(const Duration(seconds: 5));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If all retries fail
|
// If all retries fail
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ class _VerificationScreenState extends State<VerificationScreen> {
|
|||||||
setState(() {
|
setState(() {
|
||||||
_statusMessage = "SMS sent. Waiting for network delivery...";
|
_statusMessage = "SMS sent. Waiting for network delivery...";
|
||||||
});
|
});
|
||||||
|
|
||||||
// Adding a 10-second delay to account for SMS network latency.
|
// Adding a 10-second delay to account for SMS network latency.
|
||||||
await Future.delayed(const Duration(seconds: 10));
|
await Future.delayed(const Duration(seconds: 10));
|
||||||
|
|
||||||
@@ -101,7 +101,6 @@ class _VerificationScreenState extends State<VerificationScreen> {
|
|||||||
|
|
||||||
// Pop with success result
|
// Pop with success result
|
||||||
Navigator.of(context).pop(true);
|
Navigator.of(context).pop(true);
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_statusMessage = e.toString();
|
_statusMessage = e.toString();
|
||||||
@@ -111,7 +110,8 @@ class _VerificationScreenState extends State<VerificationScreen> {
|
|||||||
}
|
}
|
||||||
} else if (mounted) {
|
} else if (mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_statusMessage = "SMS sending failed. Please check permissions and try again.";
|
_statusMessage =
|
||||||
|
"SMS sending failed. Please check permissions and try again.";
|
||||||
_isVerifying = false;
|
_isVerifying = false;
|
||||||
_verificationFailed = true;
|
_verificationFailed = true;
|
||||||
});
|
});
|
||||||
@@ -131,13 +131,12 @@ class _VerificationScreenState extends State<VerificationScreen> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
if (_isVerifying)
|
if (_isVerifying) const CircularProgressIndicator(),
|
||||||
const CircularProgressIndicator(),
|
|
||||||
if (!_isVerifying && _verificationFailed)
|
if (!_isVerifying && _verificationFailed)
|
||||||
const Icon(Icons.error_outline, color: Colors.red, size: 50),
|
const Icon(Icons.error_outline, color: Colors.red, size: 50),
|
||||||
if (!_isVerifying && !_verificationFailed)
|
if (!_isVerifying && !_verificationFailed)
|
||||||
const Icon(Icons.check_circle_outline, color: Colors.green, size: 50),
|
const Icon(Icons.check_circle_outline,
|
||||||
|
color: Colors.green, size: 50),
|
||||||
const SizedBox(height: 32),
|
const SizedBox(height: 32),
|
||||||
Text(
|
Text(
|
||||||
_statusMessage,
|
_statusMessage,
|
||||||
@@ -163,4 +162,4 @@ class _VerificationScreenState extends State<VerificationScreen> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user