diff --git a/dialer/ios/Runner/Info.plist b/dialer/ios/Runner/Info.plist
index d6f23c4..366d0f5 100644
--- a/dialer/ios/Runner/Info.plist
+++ b/dialer/ios/Runner/Info.plist
@@ -45,5 +45,8 @@
UIApplicationSupportsIndirectInputEvents
+ NSContactsUsageDescription
+ Contacts for calls/string>
+
diff --git a/dialer/lib/features/history/history_page.dart b/dialer/lib/features/history/history_page.dart
index 034b286..4c38d15 100644
--- a/dialer/lib/features/history/history_page.dart
+++ b/dialer/lib/features/history/history_page.dart
@@ -1,36 +1,152 @@
+// history_page.dart
+
import 'package:flutter/material.dart';
+import 'package:flutter_contacts/flutter_contacts.dart';
+import 'package:intl/intl.dart'; // For date formatting
+import 'package:dialer/features/contacts/contact_state.dart';
+class History {
+ final Contact contact;
+ final DateTime date;
+ final String callType; // 'incoming' or 'outgoing'
+ final String callStatus; // 'missed' or 'answered'
+ final int attempts;
-List histories = [
- History("Hello"),
-];
+ History(
+ this.contact,
+ this.date,
+ this.callType,
+ this.callStatus,
+ this.attempts,
+ );
+}
class HistoryPage extends StatefulWidget {
- const HistoryPage({super.key});
+ const HistoryPage({Key? key}) : super(key: key);
@override
_HistoryPageState createState() => _HistoryPageState();
}
class _HistoryPageState extends State {
+ List histories = [];
+ bool loading = true;
+
+ @override
+ void didChangeDependencies() {
+ super.didChangeDependencies();
+ if (loading) {
+ _buildHistories();
+ }
+ }
+
+ Future _buildHistories() async {
+ final contactState = ContactState.of(context);
+ if (contactState.loading) {
+ // Wait for contacts to be loaded
+ await Future.doWhile(() async {
+ await Future.delayed(const Duration(milliseconds: 100));
+ return contactState.loading;
+ });
+ }
+ List contacts = contactState.contacts;
+
+ // Ensure there are enough contacts
+ if (contacts.isEmpty) {
+ setState(() {
+ loading = false;
+ });
+ return;
+ }
+
+ // Build histories using the contacts
+ setState(() {
+ histories = List.generate(
+ contacts.length >= 10 ? 10 : contacts.length,
+ (index) => History(
+ contacts[index],
+ DateTime.now().subtract(Duration(hours: (index + 1) * 2)),
+ index % 2 == 0 ? 'outgoing' : 'incoming',
+ index % 3 == 0 ? 'missed' : 'answered',
+ index % 3 + 1,
+ ),
+ );
+ loading = false;
+ });
+ }
+
@override
Widget build(BuildContext context) {
+ final contactState = ContactState.of(context);
+
+ if (loading || contactState.loading) {
+ return Scaffold(
+ backgroundColor: Colors.black,
+ appBar: AppBar(
+ title: const Text('History'),
+ ),
+ body: const Center(
+ child: CircularProgressIndicator(),
+ ),
+ );
+ }
+
+ if (histories.isEmpty) {
+ return Scaffold(
+ backgroundColor: Colors.black,
+ appBar: AppBar(
+ title: const Text('History'),
+ ),
+ body: const Center(
+ child: Text(
+ 'No call history available.',
+ style: TextStyle(color: Colors.white),
+ ),
+ ),
+ );
+ }
+
return Scaffold(
backgroundColor: Colors.black,
+ appBar: AppBar(
+ title: const Text('History'),
+ ),
body: ListView.builder(
itemCount: histories.length,
itemBuilder: (context, index) {
- return null;
-
- //
+ final history = histories[index];
+ final contact = history.contact;
+
+ return ListTile(
+ leading: (contact.thumbnail != null && contact.thumbnail!.isNotEmpty)
+ ? CircleAvatar(
+ backgroundImage: MemoryImage(contact.thumbnail!),
+ )
+ : CircleAvatar(
+ child: Text(
+ contact.displayName.isNotEmpty
+ ? contact.displayName[0]
+ : '?',
+ ),
+ ),
+ title: Text(
+ contact.displayName,
+ style: const TextStyle(color: Colors.white),
+ ),
+ subtitle: Text(
+ '${history.callType} - ${history.callStatus} - ${DateFormat('MMM dd, hh:mm a').format(history.date)}',
+ style: const TextStyle(color: Colors.grey),
+ ),
+ trailing: Text(
+ '${history.attempts}x',
+ style: const TextStyle(color: Colors.white),
+ ),
+ onTap: () {
+ // Handle tap event if needed
+ },
+ );
},
),
);
}
}
-
-class History {
- final String text;
-
- History(this.text);
-}
diff --git a/dialer/lib/features/home/home_page.dart b/dialer/lib/features/home/home_page.dart
index 09ded0c..f16de1f 100644
--- a/dialer/lib/features/home/home_page.dart
+++ b/dialer/lib/features/home/home_page.dart
@@ -4,6 +4,7 @@ import 'package:dialer/features/favorites/favorites_page.dart';
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';
class _MyHomePageState extends State
with SingleTickerProviderStateMixin {
@@ -14,7 +15,7 @@ class _MyHomePageState extends State
@override
void initState() {
super.initState();
- _tabController = TabController(length: 3, vsync: this, initialIndex: 1);
+ _tabController = TabController(length: 4, vsync: this, initialIndex: 1);
_tabController.addListener(_handleTabIndex);
_fetchContacts();
}
@@ -83,7 +84,7 @@ class _MyHomePageState extends State
return SearchBar(
controller: controller,
padding: MaterialStateProperty.all(
- EdgeInsets.only(
+ const EdgeInsets.only(
top: 6.0,
bottom: 6.0,
left: 16.0,
@@ -141,6 +142,7 @@ class _MyHomePageState extends State
FavoritePage(),
HistoryPage(),
ContactPage(),
+ SettingsPage(), // Add your SettingsPage here
],
),
Positioned(
@@ -184,6 +186,10 @@ class _MyHomePageState extends State
icon: Icon(_tabController.index == 2
? Icons.contacts
: Icons.contacts_outlined)),
+ Tab(
+ icon: Icon(_tabController.index == 3 // Corrected index
+ ? Icons.settings
+ : Icons.settings_outlined)),
],
labelColor: Colors.white,
unselectedLabelColor: const Color.fromARGB(255, 158, 158, 158),
diff --git a/dialer/lib/features/settings/call/settingsCall.dart b/dialer/lib/features/settings/call/settingsCall.dart
new file mode 100644
index 0000000..a052b29
--- /dev/null
+++ b/dialer/lib/features/settings/call/settingsCall.dart
@@ -0,0 +1,92 @@
+import 'package:flutter/material.dart';
+
+class SettingsCallPage extends StatefulWidget {
+ const SettingsCallPage({super.key});
+
+ @override
+ _SettingsCallPageState createState() => _SettingsCallPageState();
+}
+
+class _SettingsCallPageState extends State {
+ bool _enableVoicemail = true;
+ bool _enableCallRecording = false;
+ String _ringtone = 'Default';
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ backgroundColor: Colors.black,
+ appBar: AppBar(
+ title: const Text('Calling settings'),
+ ),
+ body: ListView(
+ children: [
+ SwitchListTile(
+ title: const Text('Enable Voicemail', style: TextStyle(color: Colors.white)),
+ value: _enableVoicemail,
+ onChanged: (bool value) {
+ setState(() {
+ _enableVoicemail = value;
+ });
+ },
+ ),
+ SwitchListTile(
+ title: const Text('Enable call Recording', style: TextStyle(color: Colors.white)),
+ value: _enableCallRecording,
+ onChanged: (bool value) {
+ setState(() {
+ _enableCallRecording = value;
+ });
+ },
+ ),
+ ListTile(
+ title: const Text('Ringtone', style: TextStyle(color: Colors.white)),
+ subtitle: Text(_ringtone, style: const TextStyle(color: Colors.grey)),
+ trailing: const Icon(Icons.arrow_forward_ios, color: Colors.white),
+ onTap: () {
+ _selectRingtone(context);
+ },
+ ),
+ ],
+ ),
+ );
+ }
+
+ void _selectRingtone(BuildContext context) {
+ showDialog(
+ context: context,
+ builder: (BuildContext context) {
+ return SimpleDialog(
+ title: const Text('Select Ringtone'),
+ children: [
+ SimpleDialogOption(
+ onPressed: () {
+ Navigator.pop(context, 'Default');
+ },
+ child: const Text('Default'),
+ ),
+ SimpleDialogOption(
+ onPressed: () {
+ Navigator.pop(context, 'Classic');
+ },
+ child: const Text('Classic'),
+ ),
+ SimpleDialogOption(
+ onPressed: () {
+ Navigator.pop(context, 'Beep');
+ },
+ child: const Text('Beep'),
+ ),
+ // Add more ringtone options
+ ],
+ );
+ },
+ ).then((value) {
+ if (value != null) {
+ setState(() {
+ _ringtone = value;
+ });
+ }
+ });
+ }
+}
diff --git a/dialer/lib/features/settings/key/delete_key_pair.dart b/dialer/lib/features/settings/key/delete_key_pair.dart
new file mode 100644
index 0000000..b38276f
--- /dev/null
+++ b/dialer/lib/features/settings/key/delete_key_pair.dart
@@ -0,0 +1,68 @@
+// delete_key_pair.dart
+
+import 'package:flutter/material.dart';
+
+class SuppressionPaireClesPage extends StatelessWidget {
+ const SuppressionPaireClesPage({super.key});
+
+ void _deleteKeyPair(BuildContext context) {
+ // key deletion logic (not implemented here)
+ // ...
+
+ // Show confirmation message
+ ScaffoldMessenger.of(context).showSnackBar(
+ const SnackBar(
+ content: Text('La paire de clés a été supprimée.'),
+ ),
+ );
+
+ // Navigate back or update the UI as needed
+ Navigator.pop(context);
+ }
+
+ void _showConfirmationDialog(BuildContext context) {
+ showDialog(
+ context: context,
+ builder: (BuildContext context) {
+ return AlertDialog(
+ title: const Text('Confirmer la Suppression'),
+ content: const Text(
+ 'Êtes-vous sûr de vouloir supprimer la paire de clés ? Cette action est irréversible.'),
+ actions: [
+ TextButton(
+ child: const Text('Annuler'),
+ onPressed: () {
+ Navigator.of(context).pop();
+ },
+ ),
+ TextButton(
+ child: const Text('Supprimer'),
+ onPressed: () {
+ Navigator.of(context).pop();
+ _deleteKeyPair(context);
+ },
+ ),
+ ],
+ );
+ },
+ );
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ backgroundColor: Colors.black,
+ appBar: AppBar(
+ title: const Text('Suppression d\'une Paire de Clés'),
+ ),
+ body: Center(
+ child: ElevatedButton(
+ onPressed: () {
+ _showConfirmationDialog(context);
+ },
+ child: const Text('Supprimer la Paire de Clés'),
+ ),
+ ),
+ );
+ }
+}
diff --git a/dialer/lib/features/settings/key/export_private_key.dart b/dialer/lib/features/settings/key/export_private_key.dart
new file mode 100644
index 0000000..1878a2b
--- /dev/null
+++ b/dialer/lib/features/settings/key/export_private_key.dart
@@ -0,0 +1,112 @@
+// export_private_key.dart
+
+import 'package:flutter/material.dart';
+import 'dart:typed_data';
+import 'dart:convert';
+import 'package:pointycastle/export.dart' as crypto;
+import 'package:file_picker/file_picker.dart';
+
+class ExportationClePriveePage extends StatefulWidget {
+ const ExportationClePriveePage({super.key});
+
+ @override
+ _ExportationClePriveePageState createState() => _ExportationClePriveePageState();
+}
+
+class _ExportationClePriveePageState extends State {
+ final TextEditingController _passwordController = TextEditingController();
+
+ Future _exportPrivateKey() async {
+ // Replace with your actual private key retrieval logic
+ final String privateKeyPem = 'Votre clé privée ici';
+
+ // Get the password from the user input
+ final password = _passwordController.text;
+ if (password.isEmpty) {
+ // Show error message
+ return;
+ }
+
+ // Encrypt the private key using AES-256
+ final encryptedData = _encryptPrivateKey(privateKeyPem, password);
+
+ // Let the user pick a file location
+ final outputFile = await FilePicker.platform.saveFile(
+ dialogTitle: 'Enregistrer la clé privée chiffrée',
+ fileName: 'private_key_encrypted.aes',
+ );
+
+ if (outputFile != null) {
+ // Write the encrypted data to the file
+ // Use appropriate file I/O methods (not shown here)
+ // ...
+ // Show a confirmation dialog or message
+ showDialog(
+ context: context,
+ builder: (context) => AlertDialog(
+ title: const Text('Clé Exportée'),
+ content: const Text('La clé privée chiffrée a été exportée avec succès.'),
+ actions: [
+ TextButton(
+ onPressed: () => Navigator.pop(context),
+ child: const Text('OK'),
+ ),
+ ],
+ ),
+ );
+ }
+ }
+
+ Uint8List _encryptPrivateKey(String privateKey, String password) {
+ // Encryption logic using AES-256
+ final key = crypto.PBKDF2KeyDerivator(crypto.HMac(crypto.SHA256Digest(), 64))
+ .process(Uint8List.fromList(utf8.encode(password)));
+
+ final params = crypto.PaddedBlockCipherParameters(
+ crypto.ParametersWithIV(crypto.KeyParameter(key), Uint8List(16)), // Initialization Vector
+ null,
+ );
+ final cipher = crypto.PaddedBlockCipher('AES/CBC/PKCS7');
+ cipher.init(true, params);
+
+ final input = Uint8List.fromList(utf8.encode(privateKey));
+ final output = cipher.process(input);
+
+ return output;
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ backgroundColor: Colors.black,
+ appBar: AppBar(
+ title: const Text('Exportation de la Clé Privée'),
+ ),
+ body: Padding(
+ padding: const EdgeInsets.all(16.0),
+ child: Column(
+ children: [
+ const Text(
+ 'Entrez un mot de passe pour chiffrer la clé privée:',
+ style: TextStyle(color: Colors.white),
+ ),
+ TextField(
+ controller: _passwordController,
+ obscureText: true,
+ style: const TextStyle(color: Colors.white),
+ decoration: const InputDecoration(
+ hintText: 'Mot de passe',
+ hintStyle: TextStyle(color: Colors.grey),
+ ),
+ ),
+ const SizedBox(height: 20),
+ ElevatedButton(
+ onPressed: _exportPrivateKey,
+ child: const Text('Exporter la Clé Privée Chiffrée'),
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/dialer/lib/features/settings/key/generate_new_key_pair.dart b/dialer/lib/features/settings/key/generate_new_key_pair.dart
new file mode 100644
index 0000000..0f8e08d
--- /dev/null
+++ b/dialer/lib/features/settings/key/generate_new_key_pair.dart
@@ -0,0 +1,123 @@
+import 'package:flutter/material.dart';
+import 'package:pointycastle/export.dart' as crypto;
+import 'dart:math';
+import 'dart:convert';
+import 'dart:typed_data';
+import 'package:asn1lib/asn1lib.dart';
+
+class GenerationNouvellePaireClesPage extends StatelessWidget {
+ const GenerationNouvellePaireClesPage({super.key});
+
+ Future