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 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 _checkCallLog(String phone) async { try { final Iterable 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; } }