Skip to content
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

This file was deleted.

11 changes: 11 additions & 0 deletions android/build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
buildscript {
ext.kotlin_version = '2.0.10'

repositories {
google()
mavenCentral()
}

dependencies {
// Add the Android Gradle Plugin classpath
classpath 'com.android.tools.build:gradle:8.1.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}

allprojects {
Expand Down
1 change: 1 addition & 0 deletions android/gradle.properties
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
org.gradle.jvmargs=-Xmx4G -XX:+HeapDumpOnOutOfMemoryError
android.useAndroidX=true
android.enableJetifier=true
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
3 changes: 2 additions & 1 deletion android/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-all.zip

2 changes: 2 additions & 0 deletions lib/classes/global.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ class Global extends ChangeNotifier {
static Map<String, dynamic> cache = {};
static final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();

static var profileNameStream;


void sentToConversations(Msg msg, String converser, {bool addToTable = true}) {

Expand Down
153 changes: 125 additions & 28 deletions lib/components/message_panel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'dart:convert';
import 'dart:io';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter_sound/flutter_sound.dart'; // ADDED
import 'package:flutter/services.dart';
import 'package:nanoid/nanoid.dart';
import 'package:permission_handler/permission_handler.dart';
Expand All @@ -15,10 +16,6 @@ import '../encyption/rsa.dart';
import 'audio_service.dart';
import 'view_file.dart';

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

class MessagePanel extends StatefulWidget {
const MessagePanel({Key? key, required this.converser, this.onMessageSent}) : super(key: key);
final String converser;final VoidCallback? onMessageSent;
Expand All @@ -30,13 +27,35 @@ class MessagePanel extends StatefulWidget {
class _MessagePanelState extends State<MessagePanel> {
TextEditingController myController = TextEditingController();
File _selectedFile = File('');

FlutterSoundRecorder? _recorder; // ADDED
bool _isRecording = false; // ADDED
String? _recordedFilePath; // ADDED
late final AudioService _audioService;
bool _isRecording = false;
String? _currentRecordingPath;


@override
void initState() {
super.initState();
_recorder = FlutterSoundRecorder(); // ADDED
_initializeRecorder(); // ADDED
}

@override
void dispose() {
_recorder?.closeRecorder(); // ADDED
_recorder = null; // ADDED
super.dispose();
}

// ADDED: Initialize audio recorder
Future<void> _initializeRecorder() async {
await _recorder?.openRecorder();
await _recorder?.setSubscriptionDuration(const Duration(milliseconds: 100));
}

_audioService = AudioService();
_initializeAudio();
}
Expand Down Expand Up @@ -102,10 +121,39 @@ class _MessagePanelState extends State<MessagePanel> {
}



@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(8.0),

child: TextFormField(
maxLines: null,
controller: myController,
decoration: InputDecoration(
icon: const Icon(Icons.person),
hintText: 'Send Message?',
labelText: 'Send Message',
suffixIcon: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
onPressed: () => _navigateToFilePreviewPage(context),
icon: const Icon(Icons.attach_file),
),
IconButton(
onPressed: _toggleRecording, // ADDED
icon: Icon(
_isRecording ? Icons.mic_off : Icons.mic, // ADDED
color: _isRecording ? Colors.red : null, // ADDED
),
),
IconButton(
onPressed: () => _sendMessage(context),
icon: const Icon(
Icons.send,
),
decoration: BoxDecoration(
color: Theme.of(context).scaffoldBackgroundColor,
boxShadow: [
Expand Down Expand Up @@ -140,6 +188,7 @@ class _MessagePanelState extends State<MessagePanel> {
decoration: BoxDecoration(
color: _isRecording ? Colors.red.withOpacity(0.1) : null,
shape: BoxShape.circle,

),
child: Icon(
_isRecording ? Icons.mic : Icons.mic_none,
Expand All @@ -161,7 +210,6 @@ class _MessagePanelState extends State<MessagePanel> {
if (myController.text.isEmpty) {
return;
}
// Encode the message to base64

String data = jsonEncode({
"sender": Global.myName,
Expand Down Expand Up @@ -189,7 +237,6 @@ class _MessagePanelState extends State<MessagePanel> {
);

RSAPublicKey publicKey = Global.myPublicKey!;
// Encrypt the message
Uint8List encryptedMessage = rsaEncrypt(
publicKey, Uint8List.fromList(utf8.encode(myController.text)));

Expand All @@ -204,21 +251,76 @@ class _MessagePanelState extends State<MessagePanel> {
widget.converser,
);

// refreshMessages();
myController.clear();
}

/// This function is used to navigate to the file preview page and check the file size.
// ADDED: Start and stop audio recording
Future<void> _toggleRecording() async {
if (_isRecording) {
final path = await _recorder?.stopRecorder();
setState(() {
_isRecording = false;
_recordedFilePath = path;
});
if (path != null) {
_sendAudioMessage(context, path); // Send the recorded audio
}
} else {
await _recorder?.startRecorder(
codec: Codec.aacMP4,
toFile: 'audio_${DateTime.now().millisecondsSinceEpoch}.m4a',
);
setState(() {
_isRecording = true;
});
}
}

// ADDED: Send recorded audio as a message
void _sendAudioMessage(BuildContext context, String filePath) {
var msgId = nanoid(21);
String fileName = filePath.split('/').last;

String data = jsonEncode({
"sender": Global.myName,
"type": "audio",
"fileName": fileName,
"filePath": filePath,
});

String date = DateTime.now().toUtc().toString();
Global.cache[msgId] = Payload(
msgId,
Global.myName,
widget.converser,
data,
date,
);
insertIntoMessageTable(
Payload(
msgId,
Global.myName,
widget.converser,
data,
date,
),
);

Provider.of<Global>(context, listen: false).sentToConversations(
Msg(data, "sent", date, msgId),
widget.converser,
);
}

// Existing file picker and sender logic
void _navigateToFilePreviewPage(BuildContext context) async {
//max size of file is 30 MB
double sizeKbs = 0;
const int maxSizeKbs = 30 * 1024;
FilePickerResult? result = await FilePicker.platform.pickFiles();
if(result != null) {
if (result != null) {
sizeKbs = result.files.single.size / 1024;
}


if (sizeKbs > maxSizeKbs) {
if (!context.mounted) return;
showDialog(
Expand All @@ -230,10 +332,8 @@ class _MessagePanelState extends State<MessagePanel> {
mainAxisSize: MainAxisSize.min,
children: [
ListTile(
//file size in MB
title: Text('File Size: ${(sizeKbs / 1024).ceil()} MB'),
subtitle: const Text(
'File size should not exceed 30 MB'),
subtitle: const Text('File size should not exceed 30 MB'),
),
],
),
Expand All @@ -251,7 +351,6 @@ class _MessagePanelState extends State<MessagePanel> {
return;
}

//this function is used to open the file preview dialog
if (result != null) {
setState(() {
_selectedFile = File(result.files.single.path!);
Expand All @@ -266,12 +365,11 @@ class _MessagePanelState extends State<MessagePanel> {
mainAxisSize: MainAxisSize.min,
children: [
ListTile(

title: Text('File Name: ${_selectedFile.path
.split('/')
.last}', overflow: TextOverflow.ellipsis,),
subtitle: Text(
'File Size: ${(sizeKbs / 1024).floor()} MB'),
title: Text(
'File Name: ${_selectedFile.path.split('/').last}',
overflow: TextOverflow.ellipsis,
),
subtitle: Text('File Size: ${(sizeKbs / 1024).floor()} MB'),
),
ElevatedButton(
onPressed: () => FilePreview.openFile(_selectedFile.path),
Expand All @@ -280,7 +378,6 @@ class _MessagePanelState extends State<MessagePanel> {
],
),
actions: [

TextButton(
onPressed: () {
Navigator.of(context).pop();
Expand All @@ -289,10 +386,9 @@ class _MessagePanelState extends State<MessagePanel> {
),
IconButton(
onPressed: () {
Navigator.pop(context);
_sendFileMessage(context, _selectedFile);

},
Navigator.pop(context);
_sendFileMessage(context, _selectedFile);
},
icon: const Icon(
Icons.send,
),
Expand All @@ -304,6 +400,8 @@ class _MessagePanelState extends State<MessagePanel> {
}
}

void _sendFileMessage(BuildContext context, File file) async {


void _sendVoiceMessage(File audioFile) async {
final String msgId = nanoid(21);
Expand Down Expand Up @@ -331,6 +429,7 @@ class _MessagePanelState extends State<MessagePanel> {

/// This function is used to send the file message.
void _sendFileMessage(BuildContext context, File file) async{

var msgId = nanoid(21);

String fileName = _selectedFile.path.split('/').last;
Expand Down Expand Up @@ -365,7 +464,5 @@ class _MessagePanelState extends State<MessagePanel> {
Msg(data, "sent", date, msgId),
widget.converser,
);

}

}
Loading