diff --git a/dialer/lib/features/call/call_page.dart b/dialer/lib/features/call/call_page.dart new file mode 100644 index 0000000..2508556 --- /dev/null +++ b/dialer/lib/features/call/call_page.dart @@ -0,0 +1,217 @@ +import 'dart:typed_data'; +import 'package:flutter/material.dart'; +import 'package:dialer/services/obfuscate_service.dart'; +import 'package:dialer/widgets/username_color_generator.dart'; + +class CallPage extends StatefulWidget { + final String displayName; + final Uint8List? thumbnail; + + const CallPage({super.key, required this.displayName, this.thumbnail}); + + @override + _CallPageState createState() => _CallPageState(); +} + +class _CallPageState extends State { + final ObfuscateService _obfuscateService = ObfuscateService(); + bool isMuted = false; + bool isSpeakerOn = false; + bool isKeypadVisible = false; + bool icingProtocolOk = true; + + void _toggleMute() { + setState(() { + isMuted = !isMuted; + }); + } + + void _toggleSpeaker() { + setState(() { + isSpeakerOn = !isSpeakerOn; + }); + } + + void _toggleKeypad() { + setState(() { + isKeypadVisible = !isKeypadVisible; + }); + } + + void _toggleIcingProtocol() { + setState(() { + icingProtocolOk = !icingProtocolOk; + }); + } + + void _hangUp() { + Navigator.pop(context); + } + + @override + Widget build(BuildContext context) { + final double avatarRadius = isKeypadVisible ? 30.0 : 60.0; + final double nameFontSize = isKeypadVisible ? 20.0 : 32.0; + final double statusFontSize = isKeypadVisible ? 16.0 : 20.0; + + return Scaffold( + body: Container( + color: Colors.black, + child: SafeArea( + child: Column( + children: [ + // Fixed size header area + Column( + mainAxisSize: MainAxisSize.min, + children: [ + const SizedBox(height: 20), + ObfuscatedAvatar( + imageBytes: widget.thumbnail, + radius: avatarRadius, + backgroundColor: generateColorFromName(widget.displayName), + fallbackInitial: widget.displayName, + ), + const SizedBox(height: 10), + Text( + _obfuscateService.obfuscateData(widget.displayName), + style: TextStyle( + fontSize: nameFontSize, + color: Colors.white, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 5), + Text( + 'Calling...', + style: TextStyle(fontSize: statusFontSize, color: Colors.white70), + ), + const SizedBox(height: 10), + ], + ), + + // Scrollable middle section + Expanded( + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + // Keypad if visible + if (isKeypadVisible) + Container( + margin: const EdgeInsets.symmetric(horizontal: 40), + padding: const EdgeInsets.all(10), + decoration: BoxDecoration( + color: Colors.black45, + borderRadius: BorderRadius.circular(10), + ), + child: GridView.count( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + crossAxisCount: 3, + mainAxisSpacing: 10, + crossAxisSpacing: 10, + 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 Center( + child: Text( + label, + style: const TextStyle( + fontSize: 24, color: Colors.white), + ), + ); + }), + ), + ), + + // Icing protocol status + GestureDetector( + onTap: _toggleIcingProtocol, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + icingProtocolOk ? Icons.lock : Icons.lock_open, + color: icingProtocolOk ? Colors.green : Colors.red, + ), + const SizedBox(width: 8), + Text( + 'Icing protocol: ${icingProtocolOk ? "ok" : "ko"}', + style: TextStyle( + color: icingProtocolOk ? Colors.green : Colors.red, + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + ), + ), + + // Control buttons + Padding( + padding: const EdgeInsets.symmetric(horizontal: 32.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + IconButton( + onPressed: _toggleMute, + icon: Icon( + isMuted ? Icons.mic_off : Icons.mic, + color: Colors.white, + size: 32, + ), + ), + IconButton( + onPressed: _toggleKeypad, + icon: Icon( + Icons.dialpad, + color: isKeypadVisible ? Colors.amber : Colors.white, + size: 32, + ), + ), + IconButton( + onPressed: _toggleSpeaker, + icon: Icon( + isSpeakerOn ? Icons.volume_up : Icons.volume_off, + color: isSpeakerOn ? Colors.amber : Colors.white, + size: 32, + ), + ), + ], + ), + ), + ], + ), + ), + ), + + // Fixed size footer with hang up button + Padding( + padding: const EdgeInsets.symmetric(vertical: 16.0), + child: IconButton( + onPressed: _hangUp, + icon: const Icon( + Icons.call_end, + color: Colors.red, + size: 48, + ), + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/dialer/lib/features/contacts/widgets/contact_modal.dart b/dialer/lib/features/contacts/widgets/contact_modal.dart index 632d8be..5d56f7b 100644 --- a/dialer/lib/features/contacts/widgets/contact_modal.dart +++ b/dialer/lib/features/contacts/widgets/contact_modal.dart @@ -5,6 +5,7 @@ import 'package:url_launcher/url_launcher.dart'; import 'package:dialer/widgets/username_color_generator.dart'; import '../../../services/block_service.dart'; import '../../../services/contact_service.dart'; +import '../../../features/call/call_page.dart'; class ContactModal extends StatefulWidget { final Contact contact; @@ -263,6 +264,18 @@ class _ContactModalState extends State { _launchPhoneDialer(phoneNumber); } }, + onLongPress: () { + // Navigate to the beautiful calling page demo + Navigator.push( + context, + MaterialPageRoute( + builder: (_) => CallPage( + displayName: widget.contact.displayName, + thumbnail: widget.contact.thumbnail, + ), + ), + ); + }, ), ListTile( leading: const Icon(Icons.message, color: Colors.blue),