Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes issue #26 #33

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ if (flutterVersionName == null) {

android {
namespace = "com.nankai.openpeerchat_flutter"
compileSdk = flutter.compileSdkVersion
compileSdk 34
ndkVersion = flutter.ndkVersion

compileOptions {
Expand Down
2 changes: 2 additions & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
<uses-permission android:minSdkVersion="31" android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:minSdkVersion="31" android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:minSdkVersion="32" android:name="android.permission.NEARBY_WIFI_DEVICES" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!-- Optional: only required for FILE payloads -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<application
Expand Down
27 changes: 27 additions & 0 deletions lib/classes/audio_playback.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import 'package:flutter_sound/flutter_sound.dart';
class AudioPlayer {
final FlutterSoundPlayer _player = FlutterSoundPlayer();
bool _isInitialized=false;

Future<void> initPlayer() async {
if(!_isInitialized){
await _player.openPlayer();
_isInitialized=true;
}
}

Future<void> playAudio(String filePath) async {
await _player.startPlayer(fromURI: filePath);
}

Future<void> stopAudio() async {
await _player.stopPlayer();
}

Future<void> dispose() async {
if(_isInitialized){
await _player.closePlayer();
_isInitialized=false;
}
}
}
34 changes: 34 additions & 0 deletions lib/classes/audio_recording.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import 'dart:io';
import 'package:flutter_sound/flutter_sound.dart';
import 'package:path_provider/path_provider.dart';

class AudioRecorder {
final FlutterSoundRecorder _recorder = FlutterSoundRecorder();
bool _isRecording = false;

Future<void> initRecorder() async {
await _recorder.openRecorder();
}

Future<String?> startRecording() async {
if (!_isRecording) {
Directory tempDir = await getTemporaryDirectory();
String filePath = '${tempDir.path}/audio_${DateTime.now().millisecondsSinceEpoch}.aac';
await _recorder.startRecorder(toFile: filePath);
_isRecording = true;
return filePath;
}
return null;
}

Future<void> stopRecording() async {
if (_isRecording) {
await _recorder.stopRecorder();
_isRecording = false;
}
}

Future<void> dispose() async {
await _recorder.closeRecorder();
}
}
110 changes: 110 additions & 0 deletions lib/components/message_panel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,20 @@ import '../classes/payload.dart';
import '../database/database_helper.dart';
import '../encyption/rsa.dart';
import 'view_file.dart';
import '../classes/audio_playback.dart';
import '../classes/audio_recording.dart';
import 'package:permission_handler/permission_handler.dart';

/// This component is used in the ChatPage.
/// It is the message bar where the message is typed on and sent to
/// connected devices.

Future<bool> requestPermissions() async {
var micStatus = await Permission.microphone.request();
var storageStatus = await Permission.storage.request();
return micStatus.isGranted && storageStatus.isGranted;
}

class MessagePanel extends StatefulWidget {
const MessagePanel({Key? key, required this.converser}) : super(key: key);
final String converser;
Expand All @@ -27,8 +36,17 @@ class MessagePanel extends StatefulWidget {

class _MessagePanelState extends State<MessagePanel> {
TextEditingController myController = TextEditingController();
final AudioRecorder _audioRecorder = AudioRecorder();
final AudioPlayer _audioPlayer = AudioPlayer();
String? _recordingFilePath;

File _selectedFile = File('');

void initState() {
super.initState();
_audioRecorder.initRecorder();
}

@override
Widget build(BuildContext context) {
return Padding(
Expand All @@ -55,13 +73,105 @@ class _MessagePanelState extends State<MessagePanel> {
Icons.send,
),
),
IconButton(
icon: Icon(Icons.mic),
onPressed: _startRecording,
),
IconButton(
icon: Icon(Icons.stop),
onPressed: _stopRecording,
),
IconButton(
icon: Icon(Icons.send),
onPressed: () => _sendAudioMessage(context),
),
],
),
),
),
);
}

void _startRecording() async {
if (await requestPermissions()) {
print("***********************************");
String? filePath = await _audioRecorder.startRecording();
setState(() {
_recordingFilePath = filePath;
});
}
}

void _stopRecording() async {
await _audioRecorder.stopRecording();
}

void _playAudio() {
if (_recordingFilePath != null) {
_audioPlayer.playAudio(_recordingFilePath!);
}
}

void _sendAudioMessage(BuildContext context) async {
// Ensure a recording exists
if (_recordingFilePath == null) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('No audio recorded to send.')),
);
return;
}

var msgId = nanoid(21);

String fileName = _recordingFilePath!.split('/').last;

// Encode audio metadata for the message
String data = jsonEncode({
"sender": Global.myName,
"type": "audio",
"fileName": fileName,
"filePath": _recordingFilePath,
});

String date = DateTime.now().toUtc().toString();

// Save the message in cache
Global.cache[msgId] = Payload(
msgId,
Global.myName,
widget.converser,
data,
date,
);

// Insert the message into the database
insertIntoMessageTable(
Payload(
msgId,
Global.myName,
widget.converser,
data,
date,
),
);

// Send the message to the conversation
Provider.of<Global>(context, listen: false).sentToConversations(
Msg(data, "sent", date, msgId),
widget.converser,
);

// Notify user and reset the recording state
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Audio message sent!')),
);

setState(() {
_recordingFilePath = null; // Clear the recording path after sending
});
}


void _sendMessage(BuildContext context) {
var msgId = nanoid(21);
if (myController.text.isEmpty) {
Expand Down
40 changes: 39 additions & 1 deletion lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,41 @@ import 'classes/global.dart';
import 'encyption/key_storage.dart';
import 'encyption/rsa.dart';

final ThemeData lightTheme = ThemeData(
brightness: Brightness.light,
primaryColor: Colors.blue,
scaffoldBackgroundColor: Colors.white,
textTheme: TextTheme(
displayLarge: TextStyle(
fontSize: 24.0,
fontWeight: FontWeight.bold,
color: Colors.black,
),
bodyLarge: TextStyle(
fontSize: 16.0,
color: Colors.black87,
),
),
);

final ThemeData darkTheme = ThemeData(
brightness: Brightness.dark,
primaryColor: Colors.grey[900],
hintColor: Colors.blueAccent,
scaffoldBackgroundColor: Colors.grey[850],
textTheme: TextTheme(
displayLarge: TextStyle(
fontSize: 24.0,
fontWeight: FontWeight.bold,
color: Colors.white,
),
bodyLarge: TextStyle(
fontSize: 16.0,
color: Colors.white70,
),
),
);

void main() async {
WidgetsFlutterBinding.ensureInitialized();

Expand Down Expand Up @@ -48,10 +83,13 @@ class MyApp extends StatelessWidget {

@override
Widget build(BuildContext context) {
return const MaterialApp(
return MaterialApp(
debugShowCheckedModeBanner: false,
onGenerateRoute: generateRoute,
initialRoute: '/',
theme: lightTheme,
darkTheme: darkTheme,
themeMode: ThemeMode.system,
);
}
}
Expand Down
Loading