import 'dart:async'; import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:dialer/services/call_service.dart'; import 'package:dialer/services/obfuscate_service.dart'; import 'package:dialer/widgets/username_color_generator.dart'; class CallPage extends StatefulWidget { final String displayName; final String phoneNumber; final Uint8List? thumbnail; const CallPage({ super.key, required this.displayName, required this.phoneNumber, this.thumbnail, }); @override _CallPageState createState() => _CallPageState(); } class _CallPageState extends State { final ObfuscateService _obfuscateService = ObfuscateService(); final CallService _callService = CallService(); bool isMuted = false; bool isSpeakerOn = false; bool isKeypadVisible = false; bool icingProtocolOk = true; String _typedDigits = ""; Timer? _callTimer; int _callSeconds = 0; String _callStatus = "Calling..."; StreamSubscription? _callStateSubscription; @override void initState() { super.initState(); _listenToCallState(); } @override void dispose() { _callTimer?.cancel(); _callStateSubscription?.cancel(); super.dispose(); } void _listenToCallState() { _callStateSubscription = _callService.callStateStream.listen((state) { final eventTime = DateTime.now().millisecondsSinceEpoch; print('CallPage: [${eventTime}ms] Call state changed to $state'); if (mounted) { setState(() { if (state == "active") { _callStatus = "00:00"; _startCallTimer(); } else if (state == "disconnected" || state == "disconnecting") { _callTimer?.cancel(); _callStatus = "Call Ended"; } else { _callStatus = "Calling..."; } }); } }); } void _startCallTimer() { _callTimer?.cancel(); _callTimer = Timer.periodic(const Duration(seconds: 1), (timer) { if (mounted) { setState(() { _callSeconds++; final minutes = (_callSeconds ~/ 60).toString().padLeft(2, '0'); final seconds = (_callSeconds % 60).toString().padLeft(2, '0'); _callStatus = '$minutes:$seconds'; print('CallPage: [${DateTime.now().millisecondsSinceEpoch}ms] Timer updated, duration: $_callStatus'); }); } }); } void _addDigit(String digit) { setState(() { _typedDigits += digit; }); } void _toggleMute() { setState(() { isMuted = !isMuted; }); } void _toggleSpeaker() { setState(() { isSpeakerOn = !isSpeakerOn; }); } void _toggleKeypad() { setState(() { isKeypadVisible = !isKeypadVisible; }); } void _toggleIcingProtocol() { setState(() { icingProtocolOk = !icingProtocolOk; }); } void _hangUp() async { try { final hangUpStart = DateTime.now().millisecondsSinceEpoch; print('CallPage: [${hangUpStart}ms] Initiating hangUp'); final result = await _callService.hangUpCall(context); print('CallPage: [${DateTime.now().millisecondsSinceEpoch}ms] Hang up result: $result'); } catch (e) { print('CallPage: [${DateTime.now().millisecondsSinceEpoch}ms] Error hanging up: $e'); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text("Error hanging up: $e")), ); } } } @override Widget build(BuildContext context) { final double avatarRadius = isKeypadVisible ? 45.0 : 45.0; final double nameFontSize = isKeypadVisible ? 24.0 : 24.0; final double statusFontSize = isKeypadVisible ? 16.0 : 16.0; print('CallPage: Building UI, _callStatus: $_callStatus, route: ${ModalRoute.of(context)?.settings.name ?? "unknown"}'); return Scaffold( body: Container( color: Colors.black, child: SafeArea( child: Column( children: [ Container( padding: const EdgeInsets.symmetric(vertical: 8.0), child: Column( mainAxisSize: MainAxisSize.min, children: [ const SizedBox(height: 35), ObfuscatedAvatar( imageBytes: widget.thumbnail, radius: avatarRadius, backgroundColor: generateColorFromName(widget.displayName), fallbackInitial: widget.displayName, ), const SizedBox(height: 4), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( icingProtocolOk ? Icons.lock : Icons.lock_open, color: icingProtocolOk ? Colors.green : Colors.red, size: 16, ), const SizedBox(width: 4), Text( 'Icing protocol: ${icingProtocolOk ? "ok" : "ko"}', style: TextStyle( color: icingProtocolOk ? Colors.green : Colors.red, fontSize: 12, fontWeight: FontWeight.bold, ), ), ], ), const SizedBox(height: 4), Text( _obfuscateService.obfuscateData(widget.displayName), style: TextStyle( fontSize: nameFontSize, color: Colors.white, fontWeight: FontWeight.bold, ), ), Text( widget.phoneNumber, style: TextStyle( fontSize: statusFontSize, color: Colors.white70), ), Text( _callStatus, style: TextStyle( fontSize: statusFontSize, color: Colors.white70), ), ], ), ), Expanded( child: Column( children: [ if (isKeypadVisible) ...[ const Spacer(flex: 2), Padding( padding: const EdgeInsets.symmetric(horizontal: 20.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( child: Text( _typedDigits, maxLines: 1, textAlign: TextAlign.right, overflow: TextOverflow.ellipsis, style: const TextStyle( fontSize: 24, color: Colors.white, fontWeight: FontWeight.bold, ), ), ), IconButton( padding: EdgeInsets.zero, onPressed: _toggleKeypad, icon: const Icon(Icons.close, color: Colors.white), ), ], ), ), Container( height: MediaQuery.of(context).size.height * 0.35, margin: const EdgeInsets.symmetric(horizontal: 20), child: GridView.count( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), crossAxisCount: 3, childAspectRatio: 1.3, mainAxisSpacing: 8, crossAxisSpacing: 8, children: List.generate(12, (index) { String label; if (index < 9) { label = '${index + 1}'; } else if (index == 9) { label = '*'; } else if (index == 10) { label = '0'; } else { label = '#'; } return GestureDetector( onTap: () => _addDigit(label), child: Container( decoration: const BoxDecoration( shape: BoxShape.circle, color: Colors.transparent, ), child: Center( child: Text( label, style: const TextStyle( fontSize: 32, color: Colors.white), ), ), ), ); }), ), ), const Spacer(flex: 1), ] else ...[ const Spacer(), Padding( padding: const EdgeInsets.symmetric(horizontal: 32.0), child: Column( mainAxisSize: MainAxisSize.min, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ Column( mainAxisSize: MainAxisSize.min, children: [ IconButton( onPressed: _toggleMute, icon: Icon( isMuted ? Icons.mic_off : Icons.mic, color: Colors.white, size: 32, ), ), Text( isMuted ? 'Unmute' : 'Mute', style: const TextStyle( color: Colors.white, fontSize: 14), ), ], ), Column( mainAxisSize: MainAxisSize.min, children: [ IconButton( onPressed: _toggleKeypad, icon: const Icon( Icons.dialpad, color: Colors.white, size: 32, ), ), const Text( 'Keypad', style: TextStyle( color: Colors.white, fontSize: 14), ), ], ), Column( mainAxisSize: MainAxisSize.min, children: [ IconButton( onPressed: _toggleSpeaker, icon: Icon( isSpeakerOn ? Icons.volume_up : Icons.volume_off, color: isSpeakerOn ? Colors.amber : Colors.white, size: 32, ), ), const Text( 'Speaker', style: TextStyle( color: Colors.white, fontSize: 14), ), ], ), ], ), const SizedBox(height: 20), Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ Column( mainAxisSize: MainAxisSize.min, children: [ IconButton( onPressed: () {}, icon: const Icon( Icons.person_add, color: Colors.white, size: 32, ), ), const Text( 'Add Contact', style: TextStyle( color: Colors.white, fontSize: 14), ), ], ), Column( mainAxisSize: MainAxisSize.min, children: [ IconButton( onPressed: () {}, icon: const Icon( Icons.sim_card, color: Colors.white, size: 32, ), ), const Text( 'Change SIM', style: TextStyle( color: Colors.white, fontSize: 14), ), ], ), ], ), ], ), ), const Spacer(flex: 3), ], ], ), ), Padding( padding: const EdgeInsets.only(bottom: 16.0), child: GestureDetector( onTap: _hangUp, child: Container( padding: const EdgeInsets.all(12), decoration: const BoxDecoration( color: Colors.red, shape: BoxShape.circle, ), child: const Icon( Icons.call_end, color: Colors.white, size: 32, ), ), ), ), ], ), ), ), ); } }