first commit
This commit is contained in:
100
receiver_app_new/lib/services/otp_controller.dart
Normal file
100
receiver_app_new/lib/services/otp_controller.dart
Normal file
@@ -0,0 +1,100 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'package:call_log/call_log.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
|
||||
class OtpController {
|
||||
DateTime? _startTime;
|
||||
Timer? _pollTimer;
|
||||
String? _detectedOtp;
|
||||
bool _isPolling = false;
|
||||
|
||||
// Callbacks
|
||||
Function(String status)? onStatusChanged;
|
||||
Function(String otp)? onOtpDetected;
|
||||
Function()? onTimeout;
|
||||
|
||||
/// Start polling call log for missed calls
|
||||
Future<bool> startWaiting(String phone) async {
|
||||
if (Platform.isIOS) {
|
||||
// iOS: cannot read call log, skip to manual entry
|
||||
onStatusChanged?.call('manual_ios');
|
||||
return false;
|
||||
}
|
||||
|
||||
// Request phone permissions (usually covers call log on many Android versions)
|
||||
var status = await Permission.phone.request();
|
||||
|
||||
if (!status.isGranted) {
|
||||
onStatusChanged?.call('permission_denied');
|
||||
return false;
|
||||
}
|
||||
|
||||
_startTime = DateTime.now();
|
||||
_isPolling = true;
|
||||
onStatusChanged?.call('waiting');
|
||||
|
||||
// Poll every 1.5 seconds
|
||||
_pollTimer = Timer.periodic(const Duration(milliseconds: 1500), (_) {
|
||||
_checkCallLog(phone);
|
||||
});
|
||||
|
||||
// Timeout after 120 seconds
|
||||
Future.delayed(const Duration(seconds: 120), () {
|
||||
if (_isPolling && _detectedOtp == null) {
|
||||
stopWaiting();
|
||||
onTimeout?.call();
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Check call log for missed calls since _startTime
|
||||
Future<void> _checkCallLog(String phone) async {
|
||||
try {
|
||||
final Iterable<CallLogEntry> entries = await CallLog.query(
|
||||
dateFrom: _startTime!.millisecondsSinceEpoch,
|
||||
);
|
||||
|
||||
for (var entry in entries) {
|
||||
if (entry.callType == CallType.missed && entry.number != null) {
|
||||
final otp = extractOTP(entry.number!);
|
||||
if (otp.length == 4) {
|
||||
_detectedOtp = otp;
|
||||
stopWaiting();
|
||||
onStatusChanged?.call('call_detected');
|
||||
onOtpDetected?.call(otp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Silently continue polling
|
||||
}
|
||||
}
|
||||
|
||||
/// Extract OTP from caller number (last 4 digits)
|
||||
String extractOTP(String callerNumber) {
|
||||
final digitsOnly = callerNumber.replaceAll(RegExp(r'[^\d]'), '');
|
||||
if (digitsOnly.length >= 4) {
|
||||
return digitsOnly.substring(digitsOnly.length - 4);
|
||||
}
|
||||
return digitsOnly;
|
||||
}
|
||||
|
||||
/// Stop polling
|
||||
void stopWaiting() {
|
||||
_isPolling = false;
|
||||
_pollTimer?.cancel();
|
||||
_pollTimer = null;
|
||||
}
|
||||
|
||||
/// Dispose resources
|
||||
void dispose() {
|
||||
stopWaiting();
|
||||
onStatusChanged = null;
|
||||
onOtpDetected = null;
|
||||
onTimeout = null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user