diff --git a/dialer/lib/features/composition/composition.dart b/dialer/lib/features/composition/composition.dart index b807ce4..6fb9962 100644 --- a/dialer/lib/features/composition/composition.dart +++ b/dialer/lib/features/composition/composition.dart @@ -4,7 +4,6 @@ import 'package:url_launcher/url_launcher.dart'; import '../../services/contact_service.dart'; import '../../services/obfuscate_service.dart'; import '../../services/call_service.dart'; -import '../contacts/widgets/add_contact_button.dart'; class CompositionPage extends StatefulWidget { const CompositionPage({super.key}); @@ -18,11 +17,7 @@ class _CompositionPageState extends State { List _allContacts = []; List _filteredContacts = []; final ContactService _contactService = ContactService(); - - // Instantiate the ObfuscateService final ObfuscateService _obfuscateService = ObfuscateService(); - - // Instantiate the CallService final CallService _callService = CallService(); @override @@ -40,8 +35,13 @@ class _CompositionPageState extends State { void _filterContacts() { setState(() { _filteredContacts = _allContacts.where((contact) { - final phoneMatch = contact.phones.any((phone) => - phone.number.replaceAll(RegExp(r'\D'), '').contains(dialedNumber)); + bool phoneMatch = contact.phones.any((phone) { + final rawPhoneNumber = phone.number; + final strippedPhoneNumber = rawPhoneNumber.replaceAll(RegExp(r'\D'), ''); + final strippedDialedNumber = dialedNumber.replaceAll(RegExp(r'\D'), ''); + return rawPhoneNumber.contains(dialedNumber) || + strippedPhoneNumber.contains(strippedDialedNumber); + }); final nameMatch = contact.displayName .toLowerCase() .contains(dialedNumber.toLowerCase()); @@ -57,6 +57,13 @@ class _CompositionPageState extends State { }); } + void _onPlusPress() { + setState(() { + dialedNumber += '+'; + _filterContacts(); + }); + } + void _onDeletePress() { setState(() { if (dialedNumber.isNotEmpty) { @@ -73,7 +80,6 @@ class _CompositionPageState extends State { }); } - // Function to call a contact's number using the CallService void _makeCall(String phoneNumber) async { try { await _callService.makeGsmCall(context, phoneNumber: phoneNumber); @@ -82,10 +88,12 @@ class _CompositionPageState extends State { }); } catch (e) { debugPrint("Error making call: $e"); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Failed to make call: $e')), + ); } } - // Function to send an SMS to a contact's number void _launchSms(String phoneNumber) async { final uri = Uri(scheme: 'sms', path: phoneNumber); if (await canLaunchUrl(uri)) { @@ -95,6 +103,20 @@ class _CompositionPageState extends State { } } + void _addContact() async { + if (await FlutterContacts.requestPermission()) { + final newContact = Contact() + ..phones = [Phone(dialedNumber.isNotEmpty ? dialedNumber : '')]; + final updatedContact = await FlutterContacts.openExternalInsert(newContact); + if (updatedContact != null) { + _fetchContacts(); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Contact added successfully!')), + ); + } + } + } + @override Widget build(BuildContext context) { return Scaffold( @@ -103,7 +125,6 @@ class _CompositionPageState extends State { children: [ Column( children: [ - // Top half: Display contacts matching dialed number Expanded( flex: 2, child: Container( @@ -115,57 +136,51 @@ class _CompositionPageState extends State { children: [ Expanded( child: ListView( - children: _filteredContacts.isNotEmpty - ? _filteredContacts.map((contact) { - final phoneNumber = contact.phones.isNotEmpty - ? contact.phones.first.number - : 'No phone number'; - return ListTile( - title: Text( - _obfuscateService.obfuscateData(contact.displayName), - style: const TextStyle(color: Colors.white), + children: [ + ..._filteredContacts.map((contact) { + final phoneNumber = contact.phones.isNotEmpty + ? contact.phones.first.number + : 'No phone number'; + return ListTile( + title: Text( + _obfuscateService.obfuscateData(contact.displayName), + style: const TextStyle(color: Colors.white), + ), + subtitle: Text( + _obfuscateService.obfuscateData(phoneNumber), + style: const TextStyle(color: Colors.grey), + ), + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + icon: Icon(Icons.phone, color: Colors.green[300], size: 20), + onPressed: () => _makeCall(phoneNumber), + ), + IconButton( + icon: Icon(Icons.message, color: Colors.blue[300], size: 20), + onPressed: () => _launchSms(phoneNumber), + ), + ], + ), + onTap: () {}, + ); + }).toList(), + ListTile( + title: const Text( + 'Add a contact', + style: TextStyle(color: Colors.white), ), - subtitle: Text( - _obfuscateService.obfuscateData(phoneNumber), - style: const TextStyle(color: Colors.grey), - ), - trailing: Row( - mainAxisSize: MainAxisSize.min, - children: [ - // Call button (Now using CallService) - IconButton( - icon: Icon(Icons.phone, - color: Colors.green[300], - size: 20), - onPressed: () { - _makeCall(phoneNumber); // Make a call using CallService - }, - ), - // Message button - IconButton( - icon: Icon(Icons.message, - color: Colors.blue[300], - size: 20), - onPressed: () { - _launchSms(phoneNumber); - }, - ), - ], - ), - onTap: () { - // Handle contact selection if needed - }, - ); - }).toList() - : [], + trailing: Icon(Icons.add, color: Colors.grey[600]), + onTap: _addContact, + ), + ], ), ), ], ), ), ), - - // Bottom half: Dialpad and Dialed number display with erase button Expanded( flex: 2, child: Container( @@ -173,7 +188,6 @@ class _CompositionPageState extends State { child: Column( mainAxisAlignment: MainAxisAlignment.start, children: [ - // Display dialed number with erase button Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -182,61 +196,57 @@ class _CompositionPageState extends State { alignment: Alignment.center, child: Text( dialedNumber, - style: const TextStyle( - fontSize: 24, color: Colors.white), + style: const TextStyle(fontSize: 24, color: Colors.white), overflow: TextOverflow.ellipsis, ), ), ), - IconButton( - onPressed: _onClearPress, - icon: const Icon(Icons.backspace, - color: Colors.white), + GestureDetector( + onTap: _onDeletePress, + onLongPress: _onClearPress, + child: const Padding( + padding: EdgeInsets.all(8.0), + child: Icon(Icons.backspace, color: Colors.white), + ), ), ], ), const SizedBox(height: 10), - - // Dialpad Expanded( child: SingleChildScrollView( child: Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ Row( - mainAxisAlignment: - MainAxisAlignment.spaceEvenly, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ - _buildDialButton('1'), - _buildDialButton('2'), - _buildDialButton('3'), + _buildDialButton('1', Colors.white), + _buildDialButton('2', Colors.white), + _buildDialButton('3', Colors.white), ], ), Row( - mainAxisAlignment: - MainAxisAlignment.spaceEvenly, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ - _buildDialButton('4'), - _buildDialButton('5'), - _buildDialButton('6'), + _buildDialButton('4', Colors.white), + _buildDialButton('5', Colors.white), + _buildDialButton('6', Colors.white), ], ), Row( - mainAxisAlignment: - MainAxisAlignment.spaceEvenly, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ - _buildDialButton('7'), - _buildDialButton('8'), - _buildDialButton('9'), + _buildDialButton('7', Colors.white), + _buildDialButton('8', Colors.white), + _buildDialButton('9', Colors.white), ], ), Row( - mainAxisAlignment: - MainAxisAlignment.spaceEvenly, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ - _buildDialButton('*'), - _buildDialButton('0'), - _buildDialButton('#'), + _buildDialButton('*', const Color.fromRGBO(117, 117, 117, 1)), + _buildDialButtonWithPlus('0'), + _buildDialButton('#', const Color.fromRGBO(117, 117, 117, 1)), ], ), ], @@ -249,26 +259,28 @@ class _CompositionPageState extends State { ), ], ), - - // Add Contact Button Positioned( bottom: 20.0, left: 0, right: 0, child: Center( - child: AddContactButton(), + child: ElevatedButton( + onPressed: dialedNumber.isNotEmpty ? () => _makeCall(dialedNumber) : null, + style: ElevatedButton.styleFrom( + backgroundColor: Colors.green[700], + shape: const CircleBorder(), + padding: const EdgeInsets.all(20), + ), + child: const Icon(Icons.phone, color: Colors.white, size: 30), + ), ), ), - - // Top Row with Back Arrow Positioned( top: 40.0, left: 16.0, child: IconButton( icon: const Icon(Icons.arrow_back, color: Colors.white), - onPressed: () { - Navigator.pop(context); - }, + onPressed: () => Navigator.pop(context), ), ), ], @@ -276,7 +288,7 @@ class _CompositionPageState extends State { ); } - Widget _buildDialButton(String number) { + Widget _buildDialButton(String number, Color textColor) { return ElevatedButton( onPressed: () => _onNumberPress(number), style: ElevatedButton.styleFrom( @@ -286,11 +298,38 @@ class _CompositionPageState extends State { ), child: Text( number, - style: const TextStyle( - fontSize: 24, - color: Colors.white, - ), + style: TextStyle(fontSize: 24, color: textColor), ), ); } -} + + Widget _buildDialButtonWithPlus(String number) { + return Stack( + alignment: Alignment.center, + children: [ + GestureDetector( + onLongPress: _onPlusPress, + child: ElevatedButton( + onPressed: () => _onNumberPress(number), + style: ElevatedButton.styleFrom( + backgroundColor: Colors.black, + shape: const CircleBorder(), + padding: const EdgeInsets.all(16), + ), + child: Text( + number, + style: const TextStyle(fontSize: 24, color: Colors.white), + ), + ), + ), + Positioned( + bottom: 8, + child: Text( + '+', + style: TextStyle(fontSize: 12, color: Colors.grey[600]), + ), + ), + ], + ); + } +} \ No newline at end of file