Files
flash-call-otp/receiver_app_new/lib/services/otp_controller.dart
2026-05-23 16:17:20 +03:00

101 lines
2.5 KiB
Dart

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;
}
}