Compare commits
1 Commits
dev
...
poc_sym_en
Author | SHA1 | Date | |
---|---|---|---|
5975977a52 |
@ -24,7 +24,7 @@ android {
|
|||||||
applicationId = "com.example.dialer"
|
applicationId = "com.example.dialer"
|
||||||
// You can update the following values to match your application needs.
|
// You can update the following values to match your application needs.
|
||||||
// For more information, see: https://flutter.dev/to/review-gradle-config.
|
// For more information, see: https://flutter.dev/to/review-gradle-config.
|
||||||
minSdk = flutter.minSdkVersion
|
minSdk = 23
|
||||||
targetSdk = flutter.targetSdkVersion
|
targetSdk = flutter.targetSdkVersion
|
||||||
versionCode = flutter.versionCode
|
versionCode = flutter.versionCode
|
||||||
versionName = flutter.versionName
|
versionName = flutter.versionName
|
||||||
|
@ -5,6 +5,9 @@
|
|||||||
<uses-permission android:name="android.permission.SEND_SMS" />
|
<uses-permission android:name="android.permission.SEND_SMS" />
|
||||||
<uses-permission android:name="android.permission.READ_BLOCKED_NUMBERS" />
|
<uses-permission android:name="android.permission.READ_BLOCKED_NUMBERS" />
|
||||||
<uses-permission android:name="android.permission.WRITE_BLOCKED_NUMBERS" />
|
<uses-permission android:name="android.permission.WRITE_BLOCKED_NUMBERS" />
|
||||||
|
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
<uses-feature android:name="android.hardware.camera" android:required="false" />
|
<uses-feature android:name="android.hardware.camera" android:required="false" />
|
||||||
<!-- The INTERNET permission is required for development. Specifically,
|
<!-- The INTERNET permission is required for development. Specifically,
|
||||||
the Flutter tool needs it to communicate with the running application
|
the Flutter tool needs it to communicate with the running application
|
||||||
|
@ -2,3 +2,4 @@ org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryErro
|
|||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
android.enableJetifier=true
|
android.enableJetifier=true
|
||||||
dev.steenbakker.mobile_scanner.useUnbundled=true
|
dev.steenbakker.mobile_scanner.useUnbundled=true
|
||||||
|
org.gradle.java.home=/usr/lib/jvm/java-17-openjdk-amd64
|
||||||
|
@ -3,6 +3,10 @@ import 'package:flutter_contacts/flutter_contacts.dart';
|
|||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
import '../../widgets/contact_service.dart';
|
import '../../widgets/contact_service.dart';
|
||||||
import '../contacts/widgets/add_contact_button.dart';
|
import '../contacts/widgets/add_contact_button.dart';
|
||||||
|
import '../../services/audio_handler.dart';
|
||||||
|
import '../../widgets/listen_replay_button.dart';
|
||||||
|
import '../../widgets/encrypt_audio_button.dart';
|
||||||
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
|
|
||||||
class CompositionPage extends StatefulWidget {
|
class CompositionPage extends StatefulWidget {
|
||||||
const CompositionPage({super.key});
|
const CompositionPage({super.key});
|
||||||
@ -17,12 +21,32 @@ class _CompositionPageState extends State<CompositionPage> {
|
|||||||
List<Contact> _filteredContacts = [];
|
List<Contact> _filteredContacts = [];
|
||||||
final ContactService _contactService = ContactService();
|
final ContactService _contactService = ContactService();
|
||||||
|
|
||||||
|
// Initialize AudioHandler
|
||||||
|
final AudioHandler _audioHandler = AudioHandler();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
_requestPermissions();
|
||||||
_fetchContacts();
|
_fetchContacts();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _requestPermissions() async {
|
||||||
|
var statuses = await [
|
||||||
|
Permission.microphone,
|
||||||
|
Permission.contacts,
|
||||||
|
Permission.storage,
|
||||||
|
].request();
|
||||||
|
|
||||||
|
if (statuses[Permission.microphone] != PermissionStatus.granted ||
|
||||||
|
statuses[Permission.contacts] != PermissionStatus.granted ||
|
||||||
|
statuses[Permission.storage] != PermissionStatus.granted) {
|
||||||
|
// Handle permission denial
|
||||||
|
// For simplicity, just print a message
|
||||||
|
print('Permissions not granted');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _fetchContacts() async {
|
Future<void> _fetchContacts() async {
|
||||||
_allContacts = await _contactService.fetchContacts();
|
_allContacts = await _contactService.fetchContacts();
|
||||||
_filteredContacts = _allContacts;
|
_filteredContacts = _allContacts;
|
||||||
@ -85,6 +109,12 @@ class _CompositionPageState extends State<CompositionPage> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_audioHandler.stopListening();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
@ -268,6 +298,21 @@ class _CompositionPageState extends State<CompositionPage> {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
// New Buttons: Listen/Replay and Encrypt
|
||||||
|
Positioned(
|
||||||
|
bottom: 80.0, // Adjust as needed to position above AddContactButton
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
ListenReplayButton(audioHandler: _audioHandler),
|
||||||
|
const SizedBox(width: 20),
|
||||||
|
EncryptAudioButton(audioHandler: _audioHandler),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
111
dialer/lib/widgets/audio_handler.dart
Normal file
111
dialer/lib/widgets/audio_handler.dart
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
// lib/services/audio_handler.dart
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
import 'package:flutter_audio_capture/flutter_audio_capture.dart';
|
||||||
|
import 'package:encrypt/encrypt.dart' as encrypt;
|
||||||
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||||
|
|
||||||
|
class AudioHandler {
|
||||||
|
final FlutterAudioCapture _audioCapture = FlutterAudioCapture();
|
||||||
|
final FlutterSecureStorage _secureStorage = FlutterSecureStorage();
|
||||||
|
StreamController<Uint8List> _audioStreamController = StreamController.broadcast();
|
||||||
|
Timer? _delayTimer;
|
||||||
|
final int delaySeconds = 3;
|
||||||
|
|
||||||
|
// Encryption variables
|
||||||
|
encrypt.Encrypter? _encrypter;
|
||||||
|
encrypt.Key? _key;
|
||||||
|
bool _isEncryptionEnabled = false;
|
||||||
|
|
||||||
|
// Buffer for delay
|
||||||
|
List<Uint8List> _buffer = [];
|
||||||
|
|
||||||
|
// Constructor
|
||||||
|
AudioHandler();
|
||||||
|
|
||||||
|
// Start listening to the microphone
|
||||||
|
Future<void> startListening() async {
|
||||||
|
await _audioCapture.start(_onAudioData, _onError, sampleRate: 44100, bufferSize: 3000);
|
||||||
|
|
||||||
|
// Initialize the delay timer
|
||||||
|
_delayTimer = Timer.periodic(Duration(seconds: delaySeconds), (_) {
|
||||||
|
if (_buffer.isNotEmpty) {
|
||||||
|
// Replay the audio
|
||||||
|
Uint8List replayData = _buffer.removeAt(0);
|
||||||
|
_audioStreamController.add(replayData);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop listening
|
||||||
|
Future<void> stopListening() async {
|
||||||
|
await _audioCapture.stop();
|
||||||
|
_delayTimer?.cancel();
|
||||||
|
_buffer.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stream for UI to listen and replay audio
|
||||||
|
Stream<Uint8List> get audioStream => _audioStreamController.stream;
|
||||||
|
|
||||||
|
// Callback for audio data
|
||||||
|
void _onAudioData(dynamic obj) async {
|
||||||
|
Uint8List data = Uint8List.fromList(obj.cast<int>());
|
||||||
|
|
||||||
|
// Encrypt the data if encryption is enabled
|
||||||
|
if (_isEncryptionEnabled && _encrypter != null) {
|
||||||
|
final iv = encrypt.IV.fromSecureRandom(12); // Use a proper IV in production
|
||||||
|
final encrypted = _encrypter!.encryptBytes(data, iv: iv);
|
||||||
|
data = encrypted.bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add data to buffer for delayed replay
|
||||||
|
_buffer.add(data);
|
||||||
|
|
||||||
|
// Optionally, you can handle encrypted data here
|
||||||
|
// For example, send it over the network or save to a file
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error handling
|
||||||
|
void _onError(Object e) {
|
||||||
|
print('Audio Capture Error: $e');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable encryption
|
||||||
|
Future<void> enableEncryption() async {
|
||||||
|
if (_isEncryptionEnabled) return;
|
||||||
|
|
||||||
|
// Check if key exists
|
||||||
|
String? storedKey = await _secureStorage.read(key: 'encryption_key');
|
||||||
|
if (storedKey == null) {
|
||||||
|
// Generate a new key
|
||||||
|
final newKey = _generateSecureKey();
|
||||||
|
await _secureStorage.write(key: 'encryption_key', value: newKey);
|
||||||
|
_key = encrypt.Key.fromUtf8(newKey);
|
||||||
|
} else {
|
||||||
|
_key = encrypt.Key.fromUtf8(storedKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
_encrypter = encrypt.Encrypter(
|
||||||
|
encrypt.AES(
|
||||||
|
_key!,
|
||||||
|
mode: encrypt.AESMode.gcm,
|
||||||
|
padding: null,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
_isEncryptionEnabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable encryption
|
||||||
|
void disableEncryption() {
|
||||||
|
_encrypter = null;
|
||||||
|
_isEncryptionEnabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate a secure key
|
||||||
|
String _generateSecureKey() {
|
||||||
|
// Implement a secure random key generator
|
||||||
|
// For demonstration, using a fixed key (DO NOT use in production)
|
||||||
|
return 'my32lengthsupersecretnooneknows1'; // 32 chars for AES-256
|
||||||
|
}
|
||||||
|
}
|
40
dialer/lib/widgets/encrypt_audio_button.dart
Normal file
40
dialer/lib/widgets/encrypt_audio_button.dart
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
// lib/widgets/encrypt_audio_button.dart
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import '../services/audio_handler.dart';
|
||||||
|
|
||||||
|
class EncryptAudioButton extends StatefulWidget {
|
||||||
|
final AudioHandler audioHandler;
|
||||||
|
|
||||||
|
const EncryptAudioButton({Key? key, required this.audioHandler}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_EncryptAudioButtonState createState() => _EncryptAudioButtonState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _EncryptAudioButtonState extends State<EncryptAudioButton> {
|
||||||
|
bool _isEncrypted = false;
|
||||||
|
|
||||||
|
void _toggleEncryption() {
|
||||||
|
setState(() {
|
||||||
|
_isEncrypted = !_isEncrypted;
|
||||||
|
if (_isEncrypted) {
|
||||||
|
widget.audioHandler.enableEncryption();
|
||||||
|
} else {
|
||||||
|
widget.audioHandler.disableEncryption();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
_isEncrypted ? Icons.lock : Icons.lock_open,
|
||||||
|
color: Colors.blueAccent,
|
||||||
|
),
|
||||||
|
onPressed: _toggleEncryption,
|
||||||
|
tooltip: _isEncrypted ? 'Disable Encryption' : 'Enable Encryption',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
65
dialer/lib/widgets/listen_replay_button.dart
Normal file
65
dialer/lib/widgets/listen_replay_button.dart
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
// lib/widgets/listen_replay_button.dart
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import '../services/audio_handler.dart';
|
||||||
|
import 'package:audioplayers/audioplayers.dart';
|
||||||
|
|
||||||
|
class ListenReplayButton extends StatefulWidget {
|
||||||
|
final AudioHandler audioHandler;
|
||||||
|
|
||||||
|
const ListenReplayButton({Key? key, required this.audioHandler}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_ListenReplayButtonState createState() => _ListenReplayButtonState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ListenReplayButtonState extends State<ListenReplayButton> {
|
||||||
|
bool _isListening = false;
|
||||||
|
final AudioPlayer _audioPlayer = AudioPlayer();
|
||||||
|
|
||||||
|
void _toggleListening() async {
|
||||||
|
if (_isListening) {
|
||||||
|
await widget.audioHandler.stopListening();
|
||||||
|
} else {
|
||||||
|
await widget.audioHandler.startListening();
|
||||||
|
// Listen to the audio stream and replay it
|
||||||
|
widget.audioHandler.audioStream.listen((data) async {
|
||||||
|
// Convert Uint8List to a playable format
|
||||||
|
// For simplicity, we'll write to a temporary file and play it
|
||||||
|
// In production, consider using a more efficient method
|
||||||
|
String filePath = '/tmp/temp_audio.aac'; // Adjust path as needed
|
||||||
|
|
||||||
|
// Use path_provider to get a valid directory
|
||||||
|
// Add dependency: path_provider: ^2.0.11
|
||||||
|
// Alternatively, use a package that allows in-memory playback
|
||||||
|
|
||||||
|
// Placeholder: Play directly from bytes is not supported by audioplayers
|
||||||
|
// You may need to implement native playback or use another package
|
||||||
|
// For demonstration, we'll skip actual playback
|
||||||
|
print('Replaying audio data: ${data.length} bytes');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
_isListening = !_isListening;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_audioPlayer.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
_isListening ? Icons.stop : Icons.mic,
|
||||||
|
color: Colors.orangeAccent,
|
||||||
|
),
|
||||||
|
onPressed: _toggleListening,
|
||||||
|
tooltip: _isListening ? 'Stop Listening' : 'Listen and Replay',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -27,7 +27,7 @@ android {
|
|||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
targetSdkVersion 33
|
targetSdkVersion 33
|
||||||
minSdkVersion 21
|
minSdkVersion 23
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
lintOptions {
|
lintOptions {
|
||||||
|
@ -30,7 +30,7 @@ android {
|
|||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "com.example.mobile_number_example"
|
applicationId "com.example.mobile_number_example"
|
||||||
minSdkVersion 21
|
minSdkVersion 23
|
||||||
targetSdkVersion 33
|
targetSdkVersion 33
|
||||||
versionCode flutterVersionCode.toInteger()
|
versionCode flutterVersionCode.toInteger()
|
||||||
versionName flutterVersionName
|
versionName flutterVersionName
|
||||||
|
@ -41,7 +41,7 @@ dependencies:
|
|||||||
cached_network_image: ^3.2.3 # For caching contact images
|
cached_network_image: ^3.2.3 # For caching contact images
|
||||||
qr_flutter: ^4.1.0
|
qr_flutter: ^4.1.0
|
||||||
android_intent_plus: ^5.2.0
|
android_intent_plus: ^5.2.0
|
||||||
camera: ^0.10.0+2
|
camera: ^0.11.0+2
|
||||||
mobile_scanner: ^6.0.2
|
mobile_scanner: ^6.0.2
|
||||||
pretty_qr_code: ^3.3.0
|
pretty_qr_code: ^3.3.0
|
||||||
pointycastle: ^3.4.0
|
pointycastle: ^3.4.0
|
||||||
@ -49,10 +49,14 @@ dependencies:
|
|||||||
asn1lib: ^1.0.0
|
asn1lib: ^1.0.0
|
||||||
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: ^10.0.0-beta.4
|
||||||
mobile_number:
|
mobile_number:
|
||||||
path: packages/mobile_number
|
path: packages/mobile_number
|
||||||
|
|
||||||
|
flutter_audio_capture: ^1.1.8
|
||||||
|
encrypt: ^5.0.3
|
||||||
|
audioplayers: ^6.1.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
Loading…
Reference in New Issue
Block a user