From 9177b5a42b3c3f91063d33b6ff56ecba17b81fdd Mon Sep 17 00:00:00 2001 From: AlexisDanlos <91090088+AlexisDanlos@users.noreply.github.com> Date: Wed, 15 Jan 2025 17:34:54 +0100 Subject: [PATCH 1/3] feat: Add Voicemail feature with playback functionality --- dialer/lib/features/home/home_page.dart | 11 +- .../features/voicemail/voicemail_page.dart | 196 ++++++++++++++++++ dialer/pubspec.yaml | 1 + 3 files changed, 206 insertions(+), 2 deletions(-) create mode 100644 dialer/lib/features/voicemail/voicemail_page.dart diff --git a/dialer/lib/features/home/home_page.dart b/dialer/lib/features/home/home_page.dart index dbfc84f..aa143e2 100644 --- a/dialer/lib/features/home/home_page.dart +++ b/dialer/lib/features/home/home_page.dart @@ -5,6 +5,7 @@ import 'package:dialer/features/history/history_page.dart'; import 'package:dialer/features/composition/composition.dart'; import 'package:flutter_contacts/flutter_contacts.dart'; import 'package:dialer/features/settings/settings.dart'; +import 'package:dialer/features/voicemail/voicemail_page.dart'; import '../../widgets/contact_service.dart'; class _MyHomePageState extends State @@ -18,8 +19,8 @@ class _MyHomePageState extends State @override void initState() { super.initState(); - // Set the TabController length to 3 - _tabController = TabController(length: 3, vsync: this, initialIndex: 1); + // Set the TabController length to 4 + _tabController = TabController(length: 4, vsync: this, initialIndex: 1); _tabController.addListener(_handleTabIndex); _fetchContacts(); } @@ -173,6 +174,7 @@ class _MyHomePageState extends State FavoritesPage(), HistoryPage(), ContactPage(), + VoicemailPage(), ], ), Positioned( @@ -216,6 +218,11 @@ class _MyHomePageState extends State icon: Icon(_tabController.index == 2 ? Icons.contacts : Icons.contacts_outlined)), + Tab( + icon: Icon(_tabController.index == 3 + ? Icons.voicemail + : Icons.voicemail_outlined), + ), ], labelColor: Colors.white, unselectedLabelColor: const Color.fromARGB(255, 158, 158, 158), diff --git a/dialer/lib/features/voicemail/voicemail_page.dart b/dialer/lib/features/voicemail/voicemail_page.dart new file mode 100644 index 0000000..6d60892 --- /dev/null +++ b/dialer/lib/features/voicemail/voicemail_page.dart @@ -0,0 +1,196 @@ +import 'package:flutter/material.dart'; +import 'package:audioplayers/audioplayers.dart'; +// ...existing code... + +class VoicemailPage extends StatefulWidget { + const VoicemailPage({Key? key}) : super(key: key); + + @override + State createState() => _VoicemailPageState(); +} + +class _VoicemailPageState extends State { + bool _expanded = false; + bool _isPlaying = false; + Duration _duration = Duration.zero; + Duration _position = Duration.zero; + late AudioPlayer _audioPlayer; + + @override + void initState() { + super.initState(); + _audioPlayer = AudioPlayer(); + _audioPlayer.onDurationChanged.listen((Duration d) { + setState(() => _duration = d); + }); + _audioPlayer.onPositionChanged.listen((Duration p) { + setState(() => _position = p); + }); + _audioPlayer.onPlayerComplete.listen((event) { + setState(() { + _isPlaying = false; + _position = Duration.zero; + }); + }); + } + + Future _togglePlayPause() async { + if (_isPlaying) { + await _audioPlayer.pause(); + } else { + await _audioPlayer.play(UrlSource('voicemail.mp3')); + } + setState(() => _isPlaying = !_isPlaying); + } + + @override + void dispose() { + _audioPlayer.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.black, + // appBar: AppBar( + // // title: const Text('Voicemail'), + // backgroundColor: Colors.black, + // ), + body: ListView( + children: [ + GestureDetector( + onTap: () { + setState(() { + _expanded = !_expanded; + }); + }, + child: AnimatedContainer( + duration: const Duration(milliseconds: 300), + margin: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: const Color.fromARGB(255, 30, 30, 30), + borderRadius: BorderRadius.circular(12.0), + ), + padding: const EdgeInsets.all(16), + child: _expanded + ? Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + const CircleAvatar( + backgroundImage: AssetImage( + 'assets/default_contact_picture.png'), + ), + const SizedBox(width: 12), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: const [ + Text( + 'John Doe', + style: TextStyle(color: Colors.white), + ), + Text( + 'Wed 3:00 PM - 1:20 min', + style: TextStyle(color: Colors.grey), + ), + ], + ), + ], + ), + const SizedBox(height: 16), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + IconButton( + icon: Icon( + _isPlaying ? Icons.pause : Icons.play_arrow, + color: Colors.white, + ), + onPressed: _togglePlayPause, + ), + SizedBox( + width: 200, + child: Slider( + min: 0, + max: _duration.inSeconds.toDouble(), + value: _position.inSeconds.toDouble(), + onChanged: (value) async { + final newPos = Duration(seconds: value.toInt()); + await _audioPlayer.seek(newPos); + }, + activeColor: Colors.blue, + inactiveColor: Colors.grey, + ), + ), + ], + ), + const SizedBox(height: 16), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: const [ + Icon(Icons.call, color: Colors.green), + SizedBox(width: 8), + Text('Call', style: TextStyle(color: Colors.white)), + ], + ), + const SizedBox(height: 12), + Row( + children: const [ + Icon(Icons.message, color: Colors.blue), + SizedBox(width: 8), + Text('Text', style: TextStyle(color: Colors.white)), + ], + ), + const SizedBox(height: 12), + Row( + children: const [ + Icon(Icons.block, color: Colors.red), + SizedBox(width: 8), + Text('Block', style: TextStyle(color: Colors.white)), + ], + ), + const SizedBox(height: 12), + Row( + children: const [ + Icon(Icons.share, color: Colors.white), + SizedBox(width: 8), + Text('Share', style: TextStyle(color: Colors.white)), + ], + ), + ], + ), + ], + ) + : Row( + children: [ + const CircleAvatar( + backgroundImage: + AssetImage('assets/default_contact_picture.png'), + ), + const SizedBox(width: 12), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: const [ + Text( + 'John Doe', + style: TextStyle(color: Colors.white), + ), + Text( + 'Wed 3:00 PM - 1:20 min', + style: TextStyle(color: Colors.grey), + ), + ], + ), + ], + ), + ), + ), + ], + ), + ); + } +} diff --git a/dialer/pubspec.yaml b/dialer/pubspec.yaml index 6e36f8c..fb44fc7 100644 --- a/dialer/pubspec.yaml +++ b/dialer/pubspec.yaml @@ -50,6 +50,7 @@ dependencies: intl_utils: ^2.0.7 url_launcher: ^6.3.1 flutter_secure_storage: ^9.0.0 + audioplayers: ^6.1.0 mobile_number: path: packages/mobile_number -- 2.45.2 From 6b5fef4b93652c8346af9d28f8eb102622d7dc53 Mon Sep 17 00:00:00 2001 From: stcb <21@stcb.cc> Date: Wed, 5 Feb 2025 23:36:11 +0200 Subject: [PATCH 2/3] CLEAN + fix unexistant asset --- dialer/lib/features/home/home_page.dart | 1 + .../features/voicemail/voicemail_page.dart | 23 +++++++++++++++---- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/dialer/lib/features/home/home_page.dart b/dialer/lib/features/home/home_page.dart index 24f1183..2c9b56f 100644 --- a/dialer/lib/features/home/home_page.dart +++ b/dialer/lib/features/home/home_page.dart @@ -7,6 +7,7 @@ import 'package:dialer/features/composition/composition.dart'; import 'package:flutter_contacts/flutter_contacts.dart'; import 'package:dialer/features/settings/settings.dart'; import '../../services/contact_service.dart'; +import 'package:dialer/features/voicemail/voicemail_page.dart'; const bool _privacyMode = bool.fromEnvironment('privacy-mode', defaultValue: false); diff --git a/dialer/lib/features/voicemail/voicemail_page.dart b/dialer/lib/features/voicemail/voicemail_page.dart index 6d60892..1fadac8 100644 --- a/dialer/lib/features/voicemail/voicemail_page.dart +++ b/dialer/lib/features/voicemail/voicemail_page.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:audioplayers/audioplayers.dart'; -// ...existing code... class VoicemailPage extends StatefulWidget { const VoicemailPage({Key? key}) : super(key: key); @@ -80,8 +79,15 @@ class _VoicemailPageState extends State { Row( children: [ const CircleAvatar( - backgroundImage: AssetImage( - 'assets/default_contact_picture.png'), + radius: 28, + backgroundColor: Colors.amber, + child: Text( + "JD", + style: TextStyle( + color: Colors.deepOrange, + fontSize: 28, + ), + ), ), const SizedBox(width: 12), Column( @@ -168,8 +174,15 @@ class _VoicemailPageState extends State { : Row( children: [ const CircleAvatar( - backgroundImage: - AssetImage('assets/default_contact_picture.png'), + radius: 28, + backgroundColor: Colors.amber, + child: Text( + "JD", + style: TextStyle( + color: Colors.deepOrange, + fontSize: 28, + ), + ), ), const SizedBox(width: 12), Column( -- 2.45.2 From bf775d0f9b1742944f5f52e589122d9027c90f27 Mon Sep 17 00:00:00 2001 From: stcb <21@stcb.cc> Date: Wed, 5 Feb 2025 23:49:07 +0200 Subject: [PATCH 3/3] Revert to obfuscate service --- dialer/lib/features/home/home_page.dart | 38 ++++++++----------------- 1 file changed, 12 insertions(+), 26 deletions(-) diff --git a/dialer/lib/features/home/home_page.dart b/dialer/lib/features/home/home_page.dart index 2c9b56f..97e2c88 100644 --- a/dialer/lib/features/home/home_page.dart +++ b/dialer/lib/features/home/home_page.dart @@ -9,16 +9,6 @@ import 'package:dialer/features/settings/settings.dart'; import '../../services/contact_service.dart'; import 'package:dialer/features/voicemail/voicemail_page.dart'; -const bool _privacyMode = bool.fromEnvironment('privacy-mode', defaultValue: false); - -String _maskName(String name) { - if (!_privacyMode) return name; - final parts = name.split(' '); - return parts.map((part) { - if (part.length < 2) return part; - return '${part[0]}${'*' * (part.length - 1)}'; - }).join(' '); -} class _MyHomePageState extends State with SingleTickerProviderStateMixin { @@ -104,7 +94,7 @@ class _MyHomePageState extends State return SearchBar( controller: controller, padding: - MaterialStateProperty.all( + WidgetStateProperty.all( const EdgeInsets.only( top: 6.0, bottom: 6.0, @@ -116,10 +106,10 @@ class _MyHomePageState extends State controller.openView(); _onSearchChanged(''); }, - backgroundColor: MaterialStateProperty.all( + backgroundColor: WidgetStateProperty.all( const Color.fromARGB(255, 30, 30, 30)), hintText: 'Search contacts', - hintStyle: MaterialStateProperty.all( + hintStyle: WidgetStateProperty.all( const TextStyle(color: Colors.grey, fontSize: 16.0), ), leading: const Icon( @@ -128,7 +118,7 @@ class _MyHomePageState extends State size: 24.0, ), shape: - MaterialStateProperty.all( + WidgetStateProperty.all( RoundedRectangleBorder( borderRadius: BorderRadius.circular(12.0), ), @@ -141,7 +131,14 @@ class _MyHomePageState extends State suggestionsBuilder: (BuildContext context, SearchController controller) { return _contactSuggestions.map((contact) { - return _buildSuggestionTile(contact, controller); + return ListTile( + key: ValueKey(contact.id), + title: Text(_obfuscateService.obfuscateData(contact.displayName), + style: const TextStyle(color: Colors.white)), + onTap: () { + controller.closeView(contact.displayName); + }, + ); }).toList(); }, ), @@ -237,17 +234,6 @@ class _MyHomePageState extends State ), ); } - - Widget _buildSuggestionTile(Contact contact, SearchController controller) { - final maskedName = _maskName(contact.displayName); - return ListTile( - key: ValueKey(contact.id), - title: Text(maskedName, style: const TextStyle(color: Colors.white)), - onTap: () { - controller.closeView(maskedName); - }, - ); - } } class MyHomePage extends StatefulWidget { -- 2.45.2