feat: Add Voicemail feature with playback functionality (#32)
All checks were successful
/ mirror (push) Successful in 4s
All checks were successful
/ mirror (push) Successful in 4s
Page messagerie vocale Co-authored-by: AlexisDanlos <91090088+AlexisDanlos@users.noreply.github.com> Co-authored-by: stcb <21@stcb.cc> Reviewed-on: #32 Co-authored-by: alexis <alexis.danlos@epitech.eu> Co-committed-by: alexis <alexis.danlos@epitech.eu>
This commit is contained in:
parent
f3f5c70620
commit
84329cb4d0
@ -7,6 +7,8 @@ import 'package:dialer/features/composition/composition.dart';
|
|||||||
import 'package:flutter_contacts/flutter_contacts.dart';
|
import 'package:flutter_contacts/flutter_contacts.dart';
|
||||||
import 'package:dialer/features/settings/settings.dart';
|
import 'package:dialer/features/settings/settings.dart';
|
||||||
import '../../services/contact_service.dart';
|
import '../../services/contact_service.dart';
|
||||||
|
import 'package:dialer/features/voicemail/voicemail_page.dart';
|
||||||
|
|
||||||
|
|
||||||
class _MyHomePageState extends State<MyHomePage>
|
class _MyHomePageState extends State<MyHomePage>
|
||||||
with SingleTickerProviderStateMixin {
|
with SingleTickerProviderStateMixin {
|
||||||
@ -19,8 +21,8 @@ class _MyHomePageState extends State<MyHomePage>
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
// Set the TabController length to 3
|
// Set the TabController length to 4
|
||||||
_tabController = TabController(length: 3, vsync: this, initialIndex: 1);
|
_tabController = TabController(length: 4, vsync: this, initialIndex: 1);
|
||||||
_tabController.addListener(_handleTabIndex);
|
_tabController.addListener(_handleTabIndex);
|
||||||
_fetchContacts();
|
_fetchContacts();
|
||||||
}
|
}
|
||||||
@ -92,7 +94,7 @@ class _MyHomePageState extends State<MyHomePage>
|
|||||||
return SearchBar(
|
return SearchBar(
|
||||||
controller: controller,
|
controller: controller,
|
||||||
padding:
|
padding:
|
||||||
MaterialStateProperty.all<EdgeInsetsGeometry>(
|
WidgetStateProperty.all<EdgeInsetsGeometry>(
|
||||||
const EdgeInsets.only(
|
const EdgeInsets.only(
|
||||||
top: 6.0,
|
top: 6.0,
|
||||||
bottom: 6.0,
|
bottom: 6.0,
|
||||||
@ -104,10 +106,10 @@ class _MyHomePageState extends State<MyHomePage>
|
|||||||
controller.openView();
|
controller.openView();
|
||||||
_onSearchChanged('');
|
_onSearchChanged('');
|
||||||
},
|
},
|
||||||
backgroundColor: MaterialStateProperty.all(
|
backgroundColor: WidgetStateProperty.all(
|
||||||
const Color.fromARGB(255, 30, 30, 30)),
|
const Color.fromARGB(255, 30, 30, 30)),
|
||||||
hintText: 'Search contacts',
|
hintText: 'Search contacts',
|
||||||
hintStyle: MaterialStateProperty.all(
|
hintStyle: WidgetStateProperty.all(
|
||||||
const TextStyle(color: Colors.grey, fontSize: 16.0),
|
const TextStyle(color: Colors.grey, fontSize: 16.0),
|
||||||
),
|
),
|
||||||
leading: const Icon(
|
leading: const Icon(
|
||||||
@ -116,7 +118,7 @@ class _MyHomePageState extends State<MyHomePage>
|
|||||||
size: 24.0,
|
size: 24.0,
|
||||||
),
|
),
|
||||||
shape:
|
shape:
|
||||||
MaterialStateProperty.all<RoundedRectangleBorder>(
|
WidgetStateProperty.all<RoundedRectangleBorder>(
|
||||||
RoundedRectangleBorder(
|
RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(12.0),
|
borderRadius: BorderRadius.circular(12.0),
|
||||||
),
|
),
|
||||||
@ -129,7 +131,7 @@ class _MyHomePageState extends State<MyHomePage>
|
|||||||
suggestionsBuilder:
|
suggestionsBuilder:
|
||||||
(BuildContext context, SearchController controller) {
|
(BuildContext context, SearchController controller) {
|
||||||
return _contactSuggestions.map((contact) {
|
return _contactSuggestions.map((contact) {
|
||||||
return ListTile(
|
return ListTile(
|
||||||
key: ValueKey(contact.id),
|
key: ValueKey(contact.id),
|
||||||
title: Text(_obfuscateService.obfuscateData(contact.displayName),
|
title: Text(_obfuscateService.obfuscateData(contact.displayName),
|
||||||
style: const TextStyle(color: Colors.white)),
|
style: const TextStyle(color: Colors.white)),
|
||||||
@ -174,6 +176,7 @@ class _MyHomePageState extends State<MyHomePage>
|
|||||||
FavoritesPage(),
|
FavoritesPage(),
|
||||||
HistoryPage(),
|
HistoryPage(),
|
||||||
ContactPage(),
|
ContactPage(),
|
||||||
|
VoicemailPage(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Positioned(
|
Positioned(
|
||||||
@ -217,6 +220,11 @@ class _MyHomePageState extends State<MyHomePage>
|
|||||||
icon: Icon(_tabController.index == 2
|
icon: Icon(_tabController.index == 2
|
||||||
? Icons.contacts
|
? Icons.contacts
|
||||||
: Icons.contacts_outlined)),
|
: Icons.contacts_outlined)),
|
||||||
|
Tab(
|
||||||
|
icon: Icon(_tabController.index == 3
|
||||||
|
? Icons.voicemail
|
||||||
|
: Icons.voicemail_outlined),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
labelColor: Colors.white,
|
labelColor: Colors.white,
|
||||||
unselectedLabelColor: const Color.fromARGB(255, 158, 158, 158),
|
unselectedLabelColor: const Color.fromARGB(255, 158, 158, 158),
|
||||||
|
209
dialer/lib/features/voicemail/voicemail_page.dart
Normal file
209
dialer/lib/features/voicemail/voicemail_page.dart
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:audioplayers/audioplayers.dart';
|
||||||
|
|
||||||
|
class VoicemailPage extends StatefulWidget {
|
||||||
|
const VoicemailPage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<VoicemailPage> createState() => _VoicemailPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _VoicemailPageState extends State<VoicemailPage> {
|
||||||
|
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<void> _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(
|
||||||
|
radius: 28,
|
||||||
|
backgroundColor: Colors.amber,
|
||||||
|
child: Text(
|
||||||
|
"JD",
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.deepOrange,
|
||||||
|
fontSize: 28,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
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(
|
||||||
|
radius: 28,
|
||||||
|
backgroundColor: Colors.amber,
|
||||||
|
child: Text(
|
||||||
|
"JD",
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.deepOrange,
|
||||||
|
fontSize: 28,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
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),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -49,6 +49,7 @@ dependencies:
|
|||||||
intl_utils: ^2.0.7
|
intl_utils: ^2.0.7
|
||||||
url_launcher: ^6.3.1
|
url_launcher: ^6.3.1
|
||||||
flutter_secure_storage: ^9.0.0
|
flutter_secure_storage: ^9.0.0
|
||||||
|
audioplayers: ^6.1.0
|
||||||
mobile_number:
|
mobile_number:
|
||||||
path: packages/mobile_number
|
path: packages/mobile_number
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user