diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 90fbbef..dd5575f 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -44,4 +44,7 @@ + + + diff --git a/lib/classes/contactClass.dart b/lib/classes/contactClass.dart new file mode 100644 index 0000000..005ef24 --- /dev/null +++ b/lib/classes/contactClass.dart @@ -0,0 +1,26 @@ +// contactClass.dart + +class Contact { + final String name; + final String phoneNumber; + final bool isFavorite; + final bool isLocked; + final String? publicKey; + + Contact( + this.name, + this.phoneNumber, { + this.isFavorite = false, + this.isLocked = false, + this.publicKey, + }); +} + +// Sample contacts list +List contacts = [ + Contact('Alice', '1234567890'), + Contact('Bob', '0987654321', isFavorite: true, publicKey: 'ABCD...WXYZ'), + Contact('Charlie', '5555555555', isLocked: true), + // Add more contacts as needed +]; + diff --git a/lib/classes/displayAvatar.dart b/lib/classes/displayAvatar.dart new file mode 100644 index 0000000..934a0c8 --- /dev/null +++ b/lib/classes/displayAvatar.dart @@ -0,0 +1,31 @@ +// displayAvatar.dart + +import 'package:dialer/classes/contactClass.dart'; +import 'package:flutter/material.dart'; + +class DisplayAvatar extends StatelessWidget { + final Contact contact; + + const DisplayAvatar({super.key, required this.contact}); + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + CircleAvatar( + child: Text(contact.name[0]), + ), + if (contact.isLocked) + const Positioned( + right: 0, + bottom: 0, + child: Icon( + Icons.lock, + color: Colors.white, + size: 20.0, + ), + ), + ], + ); + } +} diff --git a/lib/classes/displayContact.dart b/lib/classes/displayContact.dart new file mode 100644 index 0000000..cd24f6c --- /dev/null +++ b/lib/classes/displayContact.dart @@ -0,0 +1,83 @@ +// DisplayContact.dart + +import 'package:dialer/features/composition/composition.dart'; +import 'package:dialer/classes/contactClass.dart'; +import 'package:dialer/classes/displayAvatar.dart'; +import 'package:flutter/material.dart'; +import 'package:dialer/features/history/history_page.dart'; + +class DisplayContact extends StatelessWidget { + final Contact contact; + final History? history; + + const DisplayContact({ + super.key, + required this.contact, + this.history, + }); + + String getTimeElapsed(DateTime date) { + final now = DateTime.now(); + final difference = now.difference(date); + if (difference.inDays > 0) { + return '${difference.inDays} days ago'; + } else if (difference.inHours > 0) { + return '${difference.inHours} hours ago'; + } else if (difference.inMinutes > 0) { + return '${difference.inMinutes} minutes ago'; + } else { + return 'Just now'; + } + } + + void _openVisualPage(BuildContext context) { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const CompositionPage(), + ), + ); + } + + @override + Widget build(BuildContext context) { + return ListTile( + leading: DisplayAvatar(contact: contact), + title: Text(contact.name), + subtitle: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(contact.phoneNumber), + if (contact.publicKey != null) + Text( + 'key: ${contact.publicKey!.substring(0, 2)}...${contact.publicKey!.substring(contact.publicKey!.length - 2)}', + ), + if (history != null) ...[ + Row( + children: [ + Icon( + history!.callType == 'incoming' + ? Icons.call_received + : Icons.call_made, + size: 16, + color: history!.callStatus == 'missed' ? Colors.red : Colors.green, + ), + const SizedBox(width: 4), + Text('${history!.callStatus}, attempts: ${history!.attempts}'), + ], + ), + Text(getTimeElapsed(history!.date)), + ], + ], + ), + trailing: InkWell( + onTap: () => _openVisualPage(context), + child: const Icon(Icons.call), + ), + isThreeLine: true, + onTap: () { + // Add code here to handle contact tap + }, + ); + } +} diff --git a/lib/features/Settings/call/settingsCall.dart b/lib/features/Settings/call/settingsCall.dart new file mode 100644 index 0000000..497c4ec --- /dev/null +++ b/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/lib/features/Settings/key/delete_keyPair.dart b/lib/features/Settings/key/delete_keyPair.dart new file mode 100644 index 0000000..2ef7a1b --- /dev/null +++ b/lib/features/Settings/key/delete_keyPair.dart @@ -0,0 +1,68 @@ +// delete_keyPair.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/lib/features/Settings/key/export_privateKey.dart b/lib/features/Settings/key/export_privateKey.dart new file mode 100644 index 0000000..27d1fa7 --- /dev/null +++ b/lib/features/Settings/key/export_privateKey.dart @@ -0,0 +1,112 @@ +// export_privateKey.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/lib/features/Settings/key/generate_newKeyPair.dart b/lib/features/Settings/key/generate_newKeyPair.dart new file mode 100644 index 0000000..0f8e08d --- /dev/null +++ b/lib/features/Settings/key/generate_newKeyPair.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> _generateKeyPair() async { + // key generation logic using pointycastle + final keyParams = crypto.RSAKeyGeneratorParameters( + BigInt.parse('65537'), + 2048, + 64, + ); + + final secureRandom = crypto.FortunaRandom(); + + // Seed the random number generator + final random = Random.secure(); + final seeds = List.generate(32, (_) => random.nextInt(256)); + secureRandom.seed(crypto.KeyParameter(Uint8List.fromList(seeds))); + + final rngParams = crypto.ParametersWithRandom(keyParams, secureRandom); + final keyGenerator = crypto.RSAKeyGenerator(); + keyGenerator.init(rngParams); + + final pair = keyGenerator.generateKeyPair(); + final publicKey = pair.publicKey as crypto.RSAPublicKey; + final privateKey = pair.privateKey as crypto.RSAPrivateKey; + + // Convert keys to PEM format + final publicKeyPem = _encodePublicKeyToPemPKCS1(publicKey); + final privateKeyPem = _encodePrivateKeyToPemPKCS1(privateKey); + + // Save keys securely (not implemented here) + + return {'publicKey': publicKeyPem, 'privateKey': privateKeyPem}; + } + + String _encodePublicKeyToPemPKCS1(crypto.RSAPublicKey publicKey) { + final bytes = _encodePublicKeyToDer(publicKey); + return _formatPem(bytes, 'RSA PUBLIC KEY'); + } + + String _encodePrivateKeyToPemPKCS1(crypto.RSAPrivateKey privateKey) { + final bytes = _encodePrivateKeyToDer(privateKey); + return _formatPem(bytes, 'RSA PRIVATE KEY'); + } + + Uint8List _encodePublicKeyToDer(crypto.RSAPublicKey publicKey) { + final algorithmSeq = ASN1Sequence(); + algorithmSeq.add(ASN1ObjectIdentifier.fromName('rsaEncryption')); + algorithmSeq.add(ASN1Null()); + + final publicKeySeq = ASN1Sequence(); + publicKeySeq.add(ASN1Integer(publicKey.modulus!)); + publicKeySeq.add(ASN1Integer(publicKey.exponent!)); + + final publicKeyBitString = ASN1BitString(Uint8List.fromList(publicKeySeq.encodedBytes)); + + final topLevelSeq = ASN1Sequence(); + topLevelSeq.add(algorithmSeq); + topLevelSeq.add(publicKeyBitString); + + return Uint8List.fromList(topLevelSeq.encodedBytes); + } + + Uint8List _encodePrivateKeyToDer(crypto.RSAPrivateKey privateKey) { + final privateKeySeq = ASN1Sequence(); + privateKeySeq.add(ASN1Integer(BigInt.from(0))); // Version + privateKeySeq.add(ASN1Integer(privateKey.n!)); + privateKeySeq.add(ASN1Integer(privateKey.exponent!)); + privateKeySeq.add(ASN1Integer(privateKey.d!)); + privateKeySeq.add(ASN1Integer(privateKey.p!)); + privateKeySeq.add(ASN1Integer(privateKey.q!)); + privateKeySeq.add(ASN1Integer(privateKey.d! % (privateKey.p! - BigInt.one))); + privateKeySeq.add(ASN1Integer(privateKey.d! % (privateKey.q! - BigInt.one))); + privateKeySeq.add(ASN1Integer(privateKey.q!.modInverse(privateKey.p!))); + + return Uint8List.fromList(privateKeySeq.encodedBytes); + } + + String _formatPem(Uint8List bytes, String label) { + final base64 = base64Encode(bytes); + final chunks = RegExp('.{1,64}').allMatches(base64).map((m) => m.group(0)!); + return '-----BEGIN $label-----\n${chunks.join('\n')}\n-----END $label-----'; + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.black, + appBar: AppBar( + title: const Text('Génération d\'une Nouvelle Paire de Clés'), + ), + body: Center( + child: ElevatedButton( + onPressed: () async { + final keys = await _generateKeyPair(); + // Display a confirmation dialog or message + showDialog( + context: context, + builder: (context) => AlertDialog( + title: const Text('Clés Générées'), + content: const Text('La nouvelle paire de clés a été générée avec succès.'), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context), + child: const Text('OK'), + ), + ], + ), + ); + }, + child: const Text('Générer une Nouvelle Paire de Clés'), + ), + ), + ); + } +} diff --git a/lib/features/Settings/key/manage_keysPage.dart b/lib/features/Settings/key/manage_keysPage.dart new file mode 100644 index 0000000..9fc1010 --- /dev/null +++ b/lib/features/Settings/key/manage_keysPage.dart @@ -0,0 +1,83 @@ +// manage_keysPage.dart + +import 'package:flutter/material.dart'; +import 'package:dialer/features/Settings/key/show_publicKeyText.dart'; +import 'package:dialer/features/Settings/key/show_publicKeyQR.dart'; +import 'package:dialer/features/Settings/key/generate_newKeyPair.dart'; +import 'package:dialer/features/Settings/key/export_privateKey.dart'; +import 'package:dialer/features/Settings/key/delete_keyPair.dart'; + +class GestionDeClesPage extends StatelessWidget { + const GestionDeClesPage({super.key}); + + void _navigateToOption(BuildContext context, String option) { + switch (option) { + case 'Affichage de la clé publique en texte': + Navigator.push( + context, + MaterialPageRoute(builder: (context) => const AffichageClePubliqueTextePage()), + ); + break; + case 'Affichage de la clé publique en QR code': + Navigator.push( + context, + MaterialPageRoute(builder: (context) => const AffichageClePubliqueQRCodePage()), + ); + break; + case 'Génération d\'une nouvelle paire de clés': + Navigator.push( + context, + MaterialPageRoute(builder: (context) => const GenerationNouvellePaireClesPage()), + ); + break; + case 'Exportation de la clé privée en fichier chiffré par mot de passe (AES 256)': + Navigator.push( + context, + MaterialPageRoute(builder: (context) => const ExportationClePriveePage()), + ); + break; + case 'Suppression d\'une paire de clés, POPUP d\'avertissement': + Navigator.push( + context, + MaterialPageRoute(builder: (context) => const SuppressionPaireClesPage()), + ); + break; + default: + // Handle default or unknown options + break; + } + } + + @override + Widget build(BuildContext context) { + final keyManagementOptions = [ + 'Affichage de la clé publique en texte', + 'Affichage de la clé publique en QR code', + 'Génération d\'une nouvelle paire de clés', + 'Exportation de la clé privée en fichier chiffré par mot de passe (AES 256)', + 'Suppression d\'une paire de clés, POPUP d\'avertissement', + ]; + + return Scaffold( + backgroundColor: Colors.black, + appBar: AppBar( + title: const Text('Gestion de clés'), + ), + body: ListView.builder( + itemCount: keyManagementOptions.length, + itemBuilder: (context, index) { + return ListTile( + title: Text( + keyManagementOptions[index], + style: const TextStyle(color: Colors.white), + ), + trailing: const Icon(Icons.arrow_forward_ios, color: Colors.white), + onTap: () { + _navigateToOption(context, keyManagementOptions[index]); + }, + ); + }, + ), + ); + } +} diff --git a/lib/features/Settings/key/show_publicKeyQR.dart b/lib/features/Settings/key/show_publicKeyQR.dart new file mode 100644 index 0000000..bfc9853 --- /dev/null +++ b/lib/features/Settings/key/show_publicKeyQR.dart @@ -0,0 +1,27 @@ +import 'package:flutter/material.dart'; +import 'package:pretty_qr_code/pretty_qr_code.dart'; + +class AffichageClePubliqueQRCodePage extends StatelessWidget { + const AffichageClePubliqueQRCodePage({super.key}); + + @override + Widget build(BuildContext context) { + // Replace with your actual public key retrieval logic + final String publicKey = 'Votre clé publique ici'; + + return Scaffold( + backgroundColor: Colors.black, + appBar: AppBar( + title: const Text('Clé Publique en QR Code'), + ), + body: Center( + child: PrettyQr( + data: publicKey, + size: 250, + roundEdges: true, + elementColor: Colors.white, + ), + ), + ); + } +} diff --git a/lib/features/Settings/key/show_publicKeyText.dart b/lib/features/Settings/key/show_publicKeyText.dart new file mode 100644 index 0000000..996b7e7 --- /dev/null +++ b/lib/features/Settings/key/show_publicKeyText.dart @@ -0,0 +1,30 @@ +// show_publicKeyText.dart + +import 'package:flutter/material.dart'; + +class AffichageClePubliqueTextePage extends StatelessWidget { + const AffichageClePubliqueTextePage({super.key}); + + @override + Widget build(BuildContext context) { + // Replace with your actual public key retrieval logic + final String publicKey = 'Votre clé publique ici'; + + return Scaffold( + backgroundColor: Colors.black, + appBar: AppBar( + title: const Text('Clé Publique en Texte'), + ), + body: Center( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: SelectableText( + publicKey, + style: const TextStyle(color: Colors.white), + textAlign: TextAlign.center, + ), + ), + ), + ); + } +} diff --git a/lib/features/Settings/settings.dart b/lib/features/Settings/settings.dart new file mode 100644 index 0000000..ad5764a --- /dev/null +++ b/lib/features/Settings/settings.dart @@ -0,0 +1,68 @@ +// settings.dart + +import 'package:flutter/material.dart'; +import 'package:dialer/features/Settings/call/settingsCall.dart'; +import 'package:dialer/features/Settings/sim/settingsAccounts.dart'; +import 'package:dialer/features/Settings/key/manage_keysPage.dart'; // Import the new page + +class SettingsPage extends StatelessWidget { + const SettingsPage({super.key}); + + void _navigateToSettings(BuildContext context, String setting) { + switch (setting) { + case 'Calling Settings': + Navigator.push( + context, + MaterialPageRoute(builder: (context) => const SettingsCallPage()), + ); + break; + case 'Page des comptes téléphoniques': + Navigator.push( + context, + MaterialPageRoute(builder: (context) => const SettingsAccountsPage()), + ); + break; + case 'Gestion de clés': + Navigator.push( + context, + MaterialPageRoute(builder: (context) => const GestionDeClesPage()), + ); + break; + // Add more cases for other settings pages + default: + // Handle default or unknown settings + break; + } + } + + @override + Widget build(BuildContext context) { + final settingsOptions = [ + 'Calling Settings', + 'Page des comptes téléphoniques', + 'Gestion de clés', // Add the new option here + ]; + + return Scaffold( + backgroundColor: Colors.black, + appBar: AppBar( + title: const Text('Settings'), + ), + body: ListView.builder( + itemCount: settingsOptions.length, + itemBuilder: (context, index) { + return ListTile( + title: Text( + settingsOptions[index], + style: const TextStyle(color: Colors.white), + ), + trailing: const Icon(Icons.arrow_forward_ios, color: Colors.white), + onTap: () { + _navigateToSettings(context, settingsOptions[index]); + }, + ); + }, + ), + ); + } +} diff --git a/lib/features/Settings/sim/chooseSim.dart b/lib/features/Settings/sim/chooseSim.dart new file mode 100644 index 0000000..0cc1186 --- /dev/null +++ b/lib/features/Settings/sim/chooseSim.dart @@ -0,0 +1,87 @@ +// chooseSim.dart + +import 'package:flutter/material.dart'; +import 'package:sim_data/sim_data.dart'; +import 'package:permission_handler/permission_handler.dart'; + +class ChooseSimPage extends StatefulWidget { + const ChooseSimPage({super.key}); + + @override + _ChooseSimPageState createState() => _ChooseSimPageState(); +} + +class _ChooseSimPageState extends State { + List _simCards = []; + int? _selectedSimIndex; + + @override + void initState() { + super.initState(); + _fetchSimCards(); + } + + Future _fetchSimCards() async { + if (await Permission.phone.request().isGranted) { + try { + final simData = await SimDataPlugin.getSimData(); + setState(() { + _simCards = simData.cards; + _selectedSimIndex = 0; + }); + } catch (e) { + // Handle error + print('Error fetching SIM data: $e'); + } + } else { + // Permission denied + print('Phone permission denied'); + } + } + + void _onSimSelected(int? index) { + if (index != null) { + setState(() { + _selectedSimIndex = index; + }); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.black, + appBar: AppBar( + title: const Text('Choisir la SIM'), + ), + body: _simCards.isEmpty + ? const Center( + child: Text( + 'Aucune carte SIM trouvée', + style: TextStyle(color: Colors.white), + ), + ) + : ListView.builder( + itemCount: _simCards.length, + itemBuilder: (context, index) { + final sim = _simCards[index]; + return ListTile( + title: Text( + 'SIM ${index + 1}', + style: const TextStyle(color: Colors.white), + ), + subtitle: Text( + 'Opérateur: ${sim.carrierName}', + style: const TextStyle(color: Colors.grey), + ), + trailing: Radio( + value: index, + groupValue: _selectedSimIndex, + onChanged: _onSimSelected, + ), + ); + }, + ), + ); + } +} diff --git a/lib/features/Settings/sim/settingsAccounts.dart b/lib/features/Settings/sim/settingsAccounts.dart new file mode 100644 index 0000000..a4dc5c9 --- /dev/null +++ b/lib/features/Settings/sim/settingsAccounts.dart @@ -0,0 +1,59 @@ +// settingsAccounts.dart + +import 'package:flutter/material.dart'; +import 'package:dialer/features/Settings/sim/chooseSim.dart'; +import 'package:dialer/features/Settings/sim/simParameters.dart'; + +class SettingsAccountsPage extends StatelessWidget { + const SettingsAccountsPage({super.key}); + + void _navigateToAccountOption(BuildContext context, String option) { + switch (option) { + case 'Choisir la SIM': + Navigator.push( + context, + MaterialPageRoute(builder: (context) => const ChooseSimPage()), + ); + break; + case 'Paramètre SIM': + Navigator.push( + context, + MaterialPageRoute(builder: (context) => const SimParametersPage()), + ); + break; + // Handle more options if needed + default: + break; + } + } + + @override + Widget build(BuildContext context) { + final accountOptions = [ + 'Choisir la SIM', + 'Paramètre SIM', + ]; + + return Scaffold( + backgroundColor: Colors.black, + appBar: AppBar( + title: const Text('Page des comptes téléphoniques'), + ), + body: ListView.builder( + itemCount: accountOptions.length, + itemBuilder: (context, index) { + return ListTile( + title: Text( + accountOptions[index], + style: const TextStyle(color: Colors.white), + ), + trailing: const Icon(Icons.arrow_forward_ios, color: Colors.white), + onTap: () { + _navigateToAccountOption(context, accountOptions[index]); + }, + ); + }, + ), + ); + } +} diff --git a/lib/features/Settings/sim/simParameters.dart b/lib/features/Settings/sim/simParameters.dart new file mode 100644 index 0000000..f19d625 --- /dev/null +++ b/lib/features/Settings/sim/simParameters.dart @@ -0,0 +1,85 @@ +// simParameters.dart + +import 'package:flutter/material.dart'; +import 'package:sim_data/sim_data.dart'; +import 'package:permission_handler/permission_handler.dart'; + +class SimParametersPage extends StatefulWidget { + const SimParametersPage({super.key}); + + @override + _SimParametersPageState createState() => _SimParametersPageState(); +} + +class _SimParametersPageState extends State { + List _simCards = []; + + @override + void initState() { + super.initState(); + _fetchSimParameters(); + } + + Future _fetchSimParameters() async { + if (await Permission.phone.request().isGranted) { + try { + final simData = await SimDataPlugin.getSimData(); + setState(() { + _simCards = simData.cards; + }); + } catch (e) { + // Handle error + print('Error fetching SIM data: $e'); + } + } else { + // Permission denied + print('Phone permission denied'); + } + } + + Widget _buildSimInfo(SimCard sim, int index) { + return Card( + color: Colors.grey[850], + child: ListTile( + title: Text( + 'SIM ${index + 1}', + style: const TextStyle(color: Colors.white), + ), + subtitle: Text( + ''' +Opérateur: ${sim.carrierName} +Pays: ${sim.countryCode ?? 'N/A'} +MCC: ${sim.mcc ?? 'N/A'} +MNC: ${sim.mnc ?? 'N/A'} +Slot Index: ${sim.slotIndex ?? 'N/A'} +Display Name: ${sim.displayName ?? 'N/A'} + ''', + style: const TextStyle(color: Colors.grey), + ), + ), + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.black, + appBar: AppBar( + title: const Text('Paramètre SIM'), + ), + body: _simCards.isEmpty + ? const Center( + child: Text( + 'Aucune carte SIM trouvée', + style: TextStyle(color: Colors.white), + ), + ) + : ListView.builder( + itemCount: _simCards.length, + itemBuilder: (context, index) { + return _buildSimInfo(_simCards[index], index); + }, + ), + ); + } +} diff --git a/lib/features/favorites/favorites_page.dart b/lib/features/favorites/favorites_page.dart index 60f8e07..6cb07ba 100644 --- a/lib/features/favorites/favorites_page.dart +++ b/lib/features/favorites/favorites_page.dart @@ -1,3 +1,7 @@ +// favorites.dart + +import 'package:dialer/classes/contactClass.dart'; +import 'package:dialer/classes/displayContact.dart'; import 'package:flutter/material.dart'; class FavoritePage extends StatefulWidget { @@ -10,12 +14,25 @@ class FavoritePage extends StatefulWidget { class _FavoritePageState extends State { @override Widget build(BuildContext context) { + // Filter favorite contacts + final favoriteContacts = + contacts.where((contact) => contact.isFavorite).toList(); + return Scaffold( backgroundColor: Colors.black, appBar: AppBar( title: const Text('Favorites'), ), - body: Text("Hello") + body: ListView.builder( + itemCount: contacts.length, + itemBuilder: (context, index) { + if (contacts[index].isFavorite) { + return DisplayContact(contact: contacts[index]); + } else { + return const SizedBox.shrink(); + } + }, + ), ); } } diff --git a/lib/features/history/history_page.dart b/lib/features/history/history_page.dart index fd030ae..4186016 100644 --- a/lib/features/history/history_page.dart +++ b/lib/features/history/history_page.dart @@ -1,8 +1,48 @@ -import 'package:flutter/material.dart'; +// history.dart +import 'package:flutter/material.dart'; +import 'package:dialer/classes/contactClass.dart'; +import 'package:dialer/classes/displayContact.dart'; + +class History { + final Contact contact; + final DateTime date; + final String callType; // 'incoming' or 'outgoing' + final String callStatus; // 'missed' or 'answered' + final int attempts; + + History( + this.contact, + this.date, + this.callType, + this.callStatus, + this.attempts, + ); +} List histories = [ - History("Hello"), + History( + contacts[0], + DateTime.now().subtract(const Duration(hours: 2)), + 'outgoing', + 'answered', + 1, + ), + History( + contacts[1], + DateTime.now().subtract(const Duration(hours: 8)), + 'incoming', + 'missed', + 2, + ), + History( + contacts[2], + DateTime.now().subtract(const Duration(days: 1, hours: 3)), + 'outgoing', + 'missed', + 1, + ), + // Add more histories as needed ]; class HistoryPage extends StatefulWidget { @@ -23,17 +63,12 @@ class _HistoryPageState extends State { body: ListView.builder( itemCount: histories.length, itemBuilder: (context, index) { - return null; - - // + return DisplayContact( + contact: histories[index].contact, + history: histories[index], + ); }, ), ); } } - -class History { - final String text; - - History(this.text); -} diff --git a/lib/features/home/home_page.dart b/lib/features/home/home_page.dart index 1fcb4c8..de78f30 100644 --- a/lib/features/home/home_page.dart +++ b/lib/features/home/home_page.dart @@ -3,6 +3,14 @@ import 'package:flutter/material.dart'; import 'package:dialer/features/contacts/contact_page.dart'; // Import ContactPage import 'package:dialer/features/favorites/favorites_page.dart'; // Import FavoritePage import 'package:dialer/features/history/history_page.dart'; // Import HistoryPage +import 'package:dialer/features/Settings/settings.dart'; + +class MyHomePage extends StatefulWidget { + const MyHomePage({super.key}); + + @override + _MyHomePageState createState() => _MyHomePageState(); +} class _MyHomePageState extends State with SingleTickerProviderStateMixin { late TabController _tabController; @@ -10,9 +18,9 @@ class _MyHomePageState extends State with SingleTickerProviderStateM @override void initState() { super.initState(); - _tabController = TabController(length: 4, vsync: this, initialIndex: 1); + // Update the length to 5 + _tabController = TabController(length: 5, vsync: this, initialIndex: 1); _tabController.addListener(_handleTabIndex); - } @override @@ -27,71 +35,55 @@ class _MyHomePageState extends State with SingleTickerProviderStateM } @override -Widget build(BuildContext context) { - return Scaffold( - backgroundColor: Colors.black, - body: Stack( - children: [ - TabBarView( - controller: _tabController, - children: const [ - FavoritePage(), - HistoryPage(), - ContactPage(), - CompositionPage(), - ], - ), - if (_tabController.index != 3) - Positioned( - right: 20, - bottom: 20, - child: FloatingActionButton( - onPressed: () { - - _tabController.animateTo(3); - }, - backgroundColor: Colors.blue, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(45), - ), - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: const [ - Icon(Icons.dialpad, color: Colors.white), - ], + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.black, + body: Stack( + children: [ + TabBarView( + controller: _tabController, + children: const [ + FavoritePage(), + HistoryPage(), + ContactPage(), + CompositionPage(), + SettingsPage(), // Ensure this matches the number of tabs + ], + ), + if (_tabController.index != 3) + Positioned( + right: 20, + bottom: 20, + child: FloatingActionButton( + onPressed: () { + _tabController.animateTo(3); + }, + backgroundColor: Colors.blue, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(45), + ), + child: const Icon(Icons.dialpad, color: Colors.white), ), ), - ), - ), - ], - ), - bottomNavigationBar: Container( - color: Colors.black, - child: TabBar( - controller: _tabController, - tabs: [ - Tab(icon: Icon(_tabController.index == 0 ? Icons.star : Icons.star_border)), - Tab(icon: Icon(_tabController.index == 1 ? Icons.access_time_filled : Icons.access_time_outlined)), - Tab(icon: Icon(_tabController.index == 2 ? Icons.contacts : Icons.contacts_outlined)), - Tab(icon: Icon(_tabController.index == 3 ? Icons.create : Icons.create_outlined)), ], - labelColor: Colors.white, - unselectedLabelColor: Colors.grey, - indicatorSize: TabBarIndicatorSize.label, - indicatorColor: Colors.white, ), - ), - ); -} - -} - -class MyHomePage extends StatefulWidget { - const MyHomePage({super.key}); - - @override - _MyHomePageState createState() => _MyHomePageState(); + bottomNavigationBar: Container( + color: Colors.black, + child: TabBar( + controller: _tabController, + tabs: [ + Tab(icon: Icon(_tabController.index == 0 ? Icons.star : Icons.star_border)), + Tab(icon: Icon(_tabController.index == 1 ? Icons.access_time_filled : Icons.access_time_outlined)), + Tab(icon: Icon(_tabController.index == 2 ? Icons.contacts : Icons.contacts_outlined)), + Tab(icon: Icon(_tabController.index == 3 ? Icons.dialpad : Icons.dialpad_outlined)), + Tab(icon: Icon(_tabController.index == 4 ? Icons.settings : Icons.settings_outlined)), // Settings icon + ], + labelColor: Colors.white, + unselectedLabelColor: Colors.grey, + indicatorSize: TabBarIndicatorSize.label, + indicatorColor: Colors.white, + ), + ), + ); + } } diff --git a/pubspec.yaml b/pubspec.yaml index 94c0f54..2dd24ea 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: dialer -description: "Icing Dialer" +description: "A new Flutter project." # The following line prevents the package from being accidentally published to # pub.dev using `flutter pub publish`. This is preferred for private packages. publish_to: 'none' # Remove this line if you wish to publish to pub.dev @@ -38,6 +38,11 @@ dependencies: flutter_contacts: ^1.1.9+2 permission_handler: ^10.2.0 # For handling permissions cached_network_image: ^3.2.3 # For caching contact images + sim_data: ^0.0.2 + pretty_qr_code: ^3.3.0 + pointycastle: ^3.4.0 + file_picker: ^5.2.5 + asn1lib: ^1.0.0 dev_dependencies: flutter_test: