chore(android) clean up android files sending

This commit is contained in:
ksenia312
2024-02-03 20:05:19 +01:00
parent 695067fc4e
commit 32a9f2a923
8 changed files with 283 additions and 214 deletions
+31 -11
View File
@@ -395,12 +395,17 @@ class AppService extends ChangeNotifier {
final filesListener = NearbyServiceFilesListener( final filesListener = NearbyServiceFilesListener(
onData: (event) async { onData: (event) async {
final files = <File>[]; final files = <File>[];
final directory = Directory(
'storage/emulated/0/Download',
);
for (final nearbyFile in event) { for (final nearbyFile in event) {
final content = nearbyFile.info;
final downloadsDir = Directory('storage/emulated/0/Download');
final newFile = await nearbyFile.file.copy( final newFile = await nearbyFile.file.copy(
'${downloadsDir.path}/${DateTime.now().microsecondsSinceEpoch}.${content.extension}', '${directory.path}/${DateTime.now().microsecondsSinceEpoch}.${nearbyFile.info.extension}',
); );
if (!await newFile.exists()) {
await newFile.create();
}
files.add(newFile); files.add(newFile);
} }
onFilesSaved?.call(files); onFilesSaved?.call(files);
@@ -436,22 +441,27 @@ class AppService extends ChangeNotifier {
if (connectedDevice == null) return; if (connectedDevice == null) return;
_nearbyService.send( _nearbyService.send(
OutgoingNearbyMessage( OutgoingNearbyMessage(
content: NearbyMessageFileRequest( content: NearbyMessageFilesRequest(
files: [...paths.map((e) => NearbyFileInfo(path: e))], files: [
...paths.map((e) => NearbyFileInfo(path: e)),
],
), ),
receiver: connectedDevice!.info, receiver: connectedDevice!.info,
), ),
); );
} }
void sendFilesAccept(NearbyMessageFileRequest request) { void sendFilesResponse(
NearbyMessageFilesRequest request, {
required bool response,
}) {
if (connectedDevice == null) return; if (connectedDevice == null) return;
_nearbyService.send( _nearbyService.send(
OutgoingNearbyMessage( OutgoingNearbyMessage(
receiver: connectedDevice!.info, receiver: connectedDevice!.info,
content: NearbyMessageFileResponse.fromRequest( content: NearbyMessageFilesResponse.fromRequest(
request, request,
response: true, response: response,
), ),
), ),
); );
@@ -827,17 +837,27 @@ class _ConnectedBody extends StatelessWidget {
subtitle: senderSubtitle, subtitle: senderSubtitle,
); );
}, },
onFileRequest: (content) { onFilesRequest: (content) {
ActionDialog.show( ActionDialog.show(
context, context,
title: 'Files request. Files count: ${content.files.length}', title: 'Files request. Files count: ${content.files.length}',
subtitle: senderSubtitle, subtitle: senderSubtitle,
).then((value) { ).then((value) {
if (value == true) { if (value is bool) {
context.read<AppService>().sendFilesAccept(content); context.read<AppService>().sendFilesResponse(
content,
response: value,
);
} }
}); });
}, },
onFilesResponse: (content) {
AppShackBar.show(
Scaffold.of(context).context,
content.response ? 'Request is accepted!' : 'Request was denied :(',
subtitle: senderSubtitle,
);
},
); );
} }
+20 -4
View File
@@ -6,7 +6,9 @@ import 'package:nearby_service/nearby_service.dart';
/// communication channel. /// communication channel.
/// ///
/// From the communication channel, you usually get /// From the communication channel, you usually get
/// the [NearbyMessageFileRequest] request first. After that, you get [NearbyFile]. /// the [NearbyMessageFilesRequest] request first.
/// After that, you can send positive [NearbyMessageFilesResponse] and
/// get the list of [NearbyFile].
/// ///
class NearbyFile { class NearbyFile {
/// ///
@@ -44,8 +46,14 @@ class NearbyFile {
} }
} }
///
/// Quick info about the file
///
class NearbyFileInfo { class NearbyFileInfo {
const NearbyFileInfo({required this.path, this.sizeBytes}); ///
/// Contains the file [path] to get the file from it.
///
const NearbyFileInfo({required this.path});
factory NearbyFileInfo.fromJson(Map<String, dynamic>? json) { factory NearbyFileInfo.fromJson(Map<String, dynamic>? json) {
return NearbyFileInfo( return NearbyFileInfo(
@@ -53,8 +61,10 @@ class NearbyFileInfo {
); );
} }
///
/// Path of the representing file
///
final String path; final String path;
final int? sizeBytes;
@override @override
bool operator ==(Object other) => bool operator ==(Object other) =>
@@ -72,14 +82,20 @@ class NearbyFileInfo {
}; };
} }
///
/// Quick access to the file [name]
///
String get name { String get name {
try { try {
return path.split('/').last; return path.split('/').last;
} catch (e) { } catch (e) {
throw NearbyServiceException('Can\'t get fileName from $path'); return path;
} }
} }
///
/// Quick access to the file [extension]
///
String get extension { String get extension {
try { try {
return name.split('.').last; return name.split('.').last;
+91 -53
View File
@@ -5,13 +5,14 @@ import 'package:nearby_service/src/utils/random.dart';
/// Type of the message. /// Type of the message.
/// ///
/// If [text], it will be a text message. /// If [text], it will be a text message.
/// If [fileRequest], it will be a file request. After accepting request, /// If [filesRequest], it will be a files pack request.
/// user can get file stream from connected device. /// After accepting the request,
/// user can get files bytes stream from connected device.
/// ///
enum NearbyMessageContentType { enum NearbyMessageContentType {
text, text,
fileRequest, filesRequest,
fileResponse; filesResponse;
/// ///
/// Checks if this is [NearbyMessageContentType.text] /// Checks if this is [NearbyMessageContentType.text]
@@ -21,17 +22,17 @@ enum NearbyMessageContentType {
} }
/// ///
/// Checks if this is [NearbyMessageContentType.fileRequest] /// Checks if this is [NearbyMessageContentType.filesRequest]
/// ///
bool get isFileRequest { bool get isFilesRequest {
return this == NearbyMessageContentType.fileRequest; return this == NearbyMessageContentType.filesRequest;
} }
/// ///
/// Checks if this is [NearbyMessageContentType.fileResponse] /// Checks if this is [NearbyMessageContentType.filesResponse]
/// ///
bool get isFileResponse { bool get isFilesResponse {
return this == NearbyMessageContentType.fileResponse; return this == NearbyMessageContentType.filesResponse;
} }
} }
@@ -43,20 +44,21 @@ abstract class NearbyMessageContent {
const NearbyMessageContent(this._type); const NearbyMessageContent(this._type);
/// ///
/// Contains the conditional logic of creating [NearbyMessageFileRequest] /// Contains the conditional logic of creating [NearbyMessageFilesRequest],
/// or [NearbyMessageTextContent] by `type` field of [json]. /// [NearbyMessageFilesResponse] or [NearbyMessageTextContent]
/// by `type` field of [json].
/// ///
factory NearbyMessageContent.fromJson(Map<String, dynamic>? json) { factory NearbyMessageContent.fromJson(Map<String, dynamic>? json) {
try { try {
final type = NearbyMessageContentType.values.firstWhere( final type = NearbyMessageContentType.values.firstWhere(
(e) => e.name == json?['type'], (e) => e.name == json?['type'],
); );
if (type.isFileRequest) { if (type.isText) {
return NearbyMessageFileRequest.fromJson(json);
} else if (type.isText) {
return NearbyMessageTextContent.fromJson(json); return NearbyMessageTextContent.fromJson(json);
} else if (type.isFileResponse) { } else if (type.isFilesRequest) {
return NearbyMessageFileResponse.fromJson(json); return NearbyMessageFilesRequest.fromJson(json);
} else if (type.isFilesResponse) {
return NearbyMessageFilesResponse.fromJson(json);
} else { } else {
throw NearbyServiceException.unsupportedDecoding(json); throw NearbyServiceException.unsupportedDecoding(json);
} }
@@ -76,23 +78,23 @@ abstract class NearbyMessageContent {
/// * The [onText] callback returns this instance of [NearbyMessageContent], /// * The [onText] callback returns this instance of [NearbyMessageContent],
/// cast as [NearbyMessageTextContent] if is a text. /// cast as [NearbyMessageTextContent] if is a text.
/// ///
/// * The [onFileRequest] callback returns this instance of [NearbyMessageContent], /// * The [onFilesRequest] callback returns this instance of [NearbyMessageContent],
/// cast as [NearbyMessageFileRequest] if is a file request. /// cast as [NearbyMessageFilesRequest] if is a files pack request.
/// ///
/// * The [onFileResponse] callback returns this instance of [NearbyMessageContent], /// * The [onFilesResponse] callback returns this instance of [NearbyMessageContent],
/// cast as [NearbyMessageFileResponse] if is a file response. /// cast as [NearbyMessageFilesResponse] if is a files pack response.
/// ///
T? byType<T>({ T? byType<T>({
T Function(NearbyMessageTextContent)? onText, T Function(NearbyMessageTextContent)? onText,
T Function(NearbyMessageFileRequest)? onFileRequest, T Function(NearbyMessageFilesRequest)? onFilesRequest,
T Function(NearbyMessageFileResponse)? onFileResponse, T Function(NearbyMessageFilesResponse)? onFilesResponse,
}) { }) {
if (this is NearbyMessageTextContent && onText != null) { if (this is NearbyMessageTextContent && onText != null) {
return onText(this as NearbyMessageTextContent); return onText(this as NearbyMessageTextContent);
} else if (this is NearbyMessageFileRequest && onFileRequest != null) { } else if (this is NearbyMessageFilesRequest && onFilesRequest != null) {
return onFileRequest(this as NearbyMessageFileRequest); return onFilesRequest(this as NearbyMessageFilesRequest);
} else if (this is NearbyMessageFileResponse && onFileResponse != null) { } else if (this is NearbyMessageFilesResponse && onFilesResponse != null) {
return onFileResponse(this as NearbyMessageFileResponse); return onFilesResponse(this as NearbyMessageFilesResponse);
} }
return null; return null;
} }
@@ -157,15 +159,31 @@ class NearbyMessageTextContent extends NearbyMessageContent {
} }
} }
abstract class NearbyMessageFilesContent extends NearbyMessageContent { ///
/// Sealed class for files content in NearbyMessage.
///
sealed class NearbyMessageFilesContent extends NearbyMessageContent {
///
/// Here [_type] = [NearbyMessageContentType.filesResponse] or
/// [_type] = [NearbyMessageContentType.filesRequest]
///
/// Also [NearbyMessageFilesContent] contains [id] of the files pack and
/// list of [NearbyFileInfo] to determine the files.
///
const NearbyMessageFilesContent( const NearbyMessageFilesContent(
super.type, { super.type, {
required this.id, required this.id,
required this.files, required this.files,
}); });
///
/// Info about the files to be sent or received.
///
final List<NearbyFileInfo> files; final List<NearbyFileInfo> files;
///
/// ID of this files pack
///
final String id; final String id;
@override @override
@@ -179,6 +197,13 @@ abstract class NearbyMessageFilesContent extends NearbyMessageContent {
}; };
} }
@override
bool get isValid =>
files.isNotEmpty &&
files.every(
(element) => element.path.isNotEmpty,
);
@override @override
bool operator ==(Object other) => bool operator ==(Object other) =>
identical(this, other) || identical(this, other) ||
@@ -197,31 +222,31 @@ abstract class NearbyMessageFilesContent extends NearbyMessageContent {
} }
/// ///
/// Nearby message File content. Used for file sending requests. /// Nearby message File Request. Used for file sending requests.
/// Does not contain file bytes! /// Does not contain files' bytes!
/// ///
class NearbyMessageFileRequest extends NearbyMessageFilesContent { class NearbyMessageFilesRequest extends NearbyMessageFilesContent {
const NearbyMessageFileRequest._({ const NearbyMessageFilesRequest._({
required super.id, required super.id,
required super.files, required super.files,
}) : super( }) : super(
NearbyMessageContentType.fileRequest, NearbyMessageContentType.filesRequest,
); );
/// ///
/// Basic constructor with [files] to be sent or received. /// Basic constructor with [files] to be sent or received.
/// ///
NearbyMessageFileRequest({required super.files}) NearbyMessageFilesRequest({required super.files})
: super( : super(
NearbyMessageContentType.fileRequest, NearbyMessageContentType.filesRequest,
id: RandomUtils.instance.nextInt(1000000, 9999999).toString(), id: RandomUtils.instance.nextInt(1000000, 9999999).toString(),
); );
/// ///
/// Gets [NearbyMessageFileRequest] from [json]. /// Gets [NearbyMessageFilesRequest] from [json].
/// ///
factory NearbyMessageFileRequest.fromJson(Map<String, dynamic>? json) { factory NearbyMessageFilesRequest.fromJson(Map<String, dynamic>? json) {
return NearbyMessageFileRequest._( return NearbyMessageFilesRequest._(
id: json?['id'] ?? '', id: json?['id'] ?? '',
files: [ files: [
...?(json?['files'] as List?)?.map( ...?(json?['files'] as List?)?.map(
@@ -231,37 +256,50 @@ class NearbyMessageFileRequest extends NearbyMessageFilesContent {
); );
} }
@override
bool get isValid => files.every((element) => element.path.isNotEmpty);
@override @override
String toString() { String toString() {
return 'NearbyMessageFileRequest{id: $id, files: $files}'; return 'NearbyMessageFileRequest{id: $id, files: $files}';
} }
} }
class NearbyMessageFileResponse extends NearbyMessageFilesContent { ///
NearbyMessageFileResponse({ /// Nearby message File Response. Used for file sending responses.
/// Does not contain files' bytes!
///
class NearbyMessageFilesResponse extends NearbyMessageFilesContent {
///
/// Used to send a response to a previously received request.
/// Provide [id] and [files] from [NearbyMessageFilesRequest] or
/// Use the [NearbyMessageFilesResponse.fromRequest] factory to generate a
/// response.
///
NearbyMessageFilesResponse({
required super.id, required super.id,
required super.files, required super.files,
required this.response, required this.response,
}) : super( }) : super(
NearbyMessageContentType.fileResponse, NearbyMessageContentType.filesResponse,
); );
factory NearbyMessageFileResponse.fromRequest( ///
NearbyMessageFileRequest request, { /// Factory to quickly create a response to [NearbyMessageFilesRequest].
///
factory NearbyMessageFilesResponse.fromRequest(
NearbyMessageFilesRequest request, {
required bool response, required bool response,
}) { }) {
return NearbyMessageFileResponse( return NearbyMessageFilesResponse(
id: request.id, id: request.id,
files: request.files, files: request.files,
response: response, response: response,
); );
} }
factory NearbyMessageFileResponse.fromJson(Map<String, dynamic>? json) { ///
return NearbyMessageFileResponse( /// Gets [NearbyMessageFilesResponse] from [Map]
///
factory NearbyMessageFilesResponse.fromJson(Map<String, dynamic>? json) {
return NearbyMessageFilesResponse(
id: json?['id'] ?? '', id: json?['id'] ?? '',
files: [ files: [
...?(json?['files'] as List?)?.map( ...?(json?['files'] as List?)?.map(
@@ -272,11 +310,11 @@ class NearbyMessageFileResponse extends NearbyMessageFilesContent {
); );
} }
///
/// The main response to the received [NearbyMessageFilesRequest].
///
final bool response; final bool response;
@override
bool get isValid => files.every((element) => element.path.isNotEmpty);
@override @override
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
return { return {
@@ -289,4 +327,4 @@ class NearbyMessageFileResponse extends NearbyMessageFilesContent {
String toString() { String toString() {
return 'NearbyMessageFileResponse{response: $response, id: $id, files: $files}'; return 'NearbyMessageFileResponse{response: $response, id: $id, files: $files}';
} }
} }
@@ -1,12 +1,14 @@
part of 'nearby_socket_service.dart'; part of 'nearby_socket_service.dart';
class FileSocketsManager { class FileSocketsManager {
FileSocketsManager(this._network, this._service); FileSocketsManager(this._network, this._service, this._pingManager);
final NearbyServiceNetwork _network; final NearbyServiceNetwork _network;
final NearbyAndroidService _service; final NearbyAndroidService _service;
final _filesSockets = <FilesSocket>[]; final NearbySocketPingManager _pingManager;
final _serverWaitingContents = <String, HttpRequest>{};
final _filesSockets = <String, FilesSocket>{};
final _serverWaitingRequests = <String, HttpRequest>{};
NearbyServiceFilesListener? _filesListener; NearbyServiceFilesListener? _filesListener;
@@ -16,9 +18,9 @@ class FileSocketsManager {
void onWsRequest(HttpRequest request) { void onWsRequest(HttpRequest request) {
final type = NearbySocketType.fromRequest(request); final type = NearbySocketType.fromRequest(request);
final fileId = NearbyFileId.fromRequest(request); final filesPackId = NearbyFilesPackId.fromRequest(request);
if (type == NearbySocketType.file && fileId != null) { if (type == NearbySocketType.file && filesPackId != null) {
_serverWaitingContents[fileId] = request; _serverWaitingRequests[filesPackId] = request;
} }
} }
@@ -27,114 +29,124 @@ class FileSocketsManager {
required NearbyAndroidCommunicationChannelData androidData, required NearbyAndroidCommunicationChannelData androidData,
required bool isReceived, required bool isReceived,
}) async { }) async {
final info = await _service.getConnectionInfo(); final shouldStartSocket = content.byType(
if (info != null && info.groupFormed) { onFilesResponse: (response) => response.response,
if (info.isGroupOwner) { onFilesRequest: (_) => true,
await _handleServerFileContent(content); ) ??
if (content is NearbyMessageFileResponse && isReceived) { false;
await _tryTransferData(content);
} if (shouldStartSocket) {
} else { final info = await _service.getConnectionInfo();
await _handleClientFileContent( if (info != null && info.groupFormed) {
content, if (info.isGroupOwner) {
connectionData: androidData, await _startFilesServer(content);
ownerIpAddress: info.ownerIpAddress, if (content is NearbyMessageFilesResponse && isReceived) {
); await _tryTransferData(content);
if (content is NearbyMessageFileRequest && !isReceived) { }
await _tryTransferData(content); } else {
await _connectToFilesSocket(
content,
connectionData: androidData,
ownerIpAddress: info.ownerIpAddress,
);
if (content is NearbyMessageFilesRequest && !isReceived) {
await _tryTransferData(content);
}
} }
} }
} else {
_filesSockets.remove(content.id);
} }
} }
Future<void> closeAll() async { Future<void> closeAll() async {
for (final fileSocket in _filesSockets) { for (final fileSocket in _filesSockets.values) {
await fileSocket.close(); await fileSocket.close();
} }
_filesSockets.clear(); _filesSockets.clear();
_filesListener = null; _filesListener = null;
} }
Future<void> _handleClientFileContent( Future<void> _connectToFilesSocket(
NearbyMessageFilesContent content, { NearbyMessageFilesContent content, {
required NearbyAndroidCommunicationChannelData connectionData, required NearbyAndroidCommunicationChannelData connectionData,
required String ownerIpAddress, required String ownerIpAddress,
}) async { }) async {
try { try {
final shouldStartFileSocket = content.byType( final response = await _network.pingServer(
onFileResponse: (response) => response.response, address: ownerIpAddress,
onFileRequest: (request) => true, port: connectionData.port,
) ?? );
false; if (await _pingManager.checkPong(response)) {
if (shouldStartFileSocket) { await _tryStartFileSocket(
final socket = await _network.connectToSocket( content,
ownerIpAddress: ownerIpAddress, onCreateSocket: () => _network.connectToSocket(
port: connectionData.port, ownerIpAddress: ownerIpAddress,
socketType: NearbySocketType.file, port: connectionData.port,
headers: { socketType: NearbySocketType.file,
NearbyFileId.key: content.id, headers: {
}, NearbyFilesPackId.key: content.id,
},
),
);
} else {
Logger.debug(
'Files server is unavailable, reconnect in ${connectionData.clientReconnectInterval}s',
);
await Future.delayed(
connectionData.clientReconnectInterval,
() => _connectToFilesSocket(
content,
connectionData: connectionData,
ownerIpAddress: ownerIpAddress,
),
); );
if (socket != null) {
_filesSockets.add(
FilesSocket.startListening(
content: content,
socket: socket,
listener: _filesListener,
onDestroy: _filesSockets.remove,
),
);
Logger.info(
'The file socket was created for the files pack ${content.id}',
);
} else {
await Future.delayed(
connectionData.clientReconnectInterval,
() => _handleClientFileContent(
content,
connectionData: connectionData,
ownerIpAddress: ownerIpAddress,
),
);
}
} }
} catch (e) { } catch (e) {
Logger.error(e); Logger.error(e);
} }
} }
Future<void> _handleServerFileContent( Future<void> _startFilesServer(
NearbyMessageFilesContent content, NearbyMessageFilesContent content,
) async { ) async {
MapEntry<String, HttpRequest>? request; final request = _serverWaitingRequests[content.id];
try {
request = _serverWaitingContents.entries.firstWhere(
(element) => element.key == content.id,
);
} catch (e) {
request = null;
}
if (request != null) { if (request != null) {
Logger.debug('Found cached server file request ${request.key}'); Logger.debug('Found cached server file request ${content.id}');
_filesSockets.add(
FilesSocket.startListening( final result = await _tryStartFileSocket(
content: content, content,
socket: await WebSocketTransformer.upgrade(request.value), onCreateSocket: () => WebSocketTransformer.upgrade(request),
listener: _filesListener,
onDestroy: _filesSockets.remove,
),
); );
_serverWaitingContents.remove(content.id); if (result) {
Logger.info('Created a socket for the files pack ${content.id}'); _serverWaitingRequests.remove(content.id);
}
} }
} }
Future<bool> _tryStartFileSocket(
NearbyMessageFilesContent content, {
required Future<WebSocket?> Function() onCreateSocket,
}) async {
final socket = await onCreateSocket();
if (socket != null) {
_filesSockets[content.id] = FilesSocket.startListening(
content: content,
socket: socket,
listener: _filesListener,
onDestroy: _filesSockets.remove,
);
Logger.info('Created a socket for the files pack ${content.id}');
return true;
}
return false;
}
Future<void> _tryTransferData(NearbyMessageFilesContent content) async { Future<void> _tryTransferData(NearbyMessageFilesContent content) async {
Logger.debug('Start transferring the files pack ${content.id}'); final filesSocket = _filesSockets[content.id];
final filesSocket = _find(content.id);
if (filesSocket != null) { if (filesSocket != null) {
Logger.debug('Start transferring the files pack ${content.id}');
for (var i = 0; i < content.files.length; i++) { for (var i = 0; i < content.files.length; i++) {
try { try {
final fileInfo = content.files[i]; final fileInfo = content.files[i];
@@ -161,22 +173,6 @@ class FileSocketsManager {
required FilesSocket filesSocket, required FilesSocket filesSocket,
required File file, required File file,
}) { }) {
return file.openRead().listen( return file.openRead().listen(filesSocket.sendData);
(data) {
filesSocket.sendData(data);
},
);
}
FilesSocket? _find(String id) {
FilesSocket? fileSocket;
try {
fileSocket = _filesSockets.firstWhere(
(element) => element.content.id == id,
);
} catch (e) {
fileSocket = null;
}
return fileSocket;
} }
} }
@@ -24,7 +24,11 @@ class NearbySocketService {
final NearbyAndroidService _service; final NearbyAndroidService _service;
final _pingManager = NearbySocketPingManager(); final _pingManager = NearbySocketPingManager();
final _network = NearbyServiceNetwork(); final _network = NearbyServiceNetwork();
late final _fileSocketsManager = FileSocketsManager(_network, _service); late final _fileSocketsManager = FileSocketsManager(
_network,
_service,
_pingManager,
);
final state = ValueNotifier(CommunicationChannelState.notConnected); final state = ValueNotifier(CommunicationChannelState.notConnected);
@@ -77,7 +81,7 @@ class NearbySocketService {
} }
/// ///
/// Add [OutgoingNearbyMessage]'s JSON representation to [_socket]. /// Adds [OutgoingNearbyMessage]'s JSON representation to the [_socket].
/// ///
Future<bool> send(OutgoingNearbyMessage message) async { Future<bool> send(OutgoingNearbyMessage message) async {
if (message.isValid) { if (message.isValid) {
@@ -199,36 +203,22 @@ class NearbySocketService {
Logger.debug('Starting socket subscription'); Logger.debug('Starting socket subscription');
if (_connectedDeviceId != null) { if (_connectedDeviceId != null) {
_messagesSubscription = _socket?.listen( _messagesSubscription = _socket
(event) async { ?.map(MessagesStreamMapper.toMessage)
// if (fileLoaders != null) { .where((e) => e != null)
// if (event is List<int>) { .cast<ReceivedNearbyMessage>()
// fileLoaders!.add(event); .map((e) => MessagesStreamMapper.replaceId(e, _connectedDeviceId!))
// } else if (event == fileLoaders?.finishCommand) { .listen(
// final file = await fileLoaders!.getFile().whenComplete( (message) async {
// () {
// fileLoaders = null;
// },
// );
// socketListener.onFile?.call(file);
// }
// } else {
try { try {
final message = MessagesStreamMapper.toMessage(event); if (message.content is NearbyMessageFilesContent) {
if (message != null) { _fileSocketsManager.handleFileMessageContent(
final newMessage = MessagesStreamMapper.replaceId( message.content as NearbyMessageFilesContent,
message, androidData: _androidData,
_connectedDeviceId!, isReceived: true,
); );
if (newMessage.content is NearbyMessageFilesContent) {
_fileSocketsManager.handleFileMessageContent(
newMessage.content as NearbyMessageFilesContent,
androidData: _androidData,
isReceived: true,
);
}
socketListener.onData(newMessage);
} }
socketListener.onData(message);
} catch (e) { } catch (e) {
Logger.error(e); Logger.error(e);
} }
@@ -101,10 +101,10 @@ enum NearbySocketType {
} }
} }
class NearbyFileId { class NearbyFilesPackId {
static const key = 'FileID'; static const key = 'FilesPackID';
static String? fromRequest(HttpRequest request) { static String? fromRequest(HttpRequest request) {
return request.headers.value(NearbyFileId.key); return request.headers.value(NearbyFilesPackId.key);
} }
} }
+5
View File
@@ -38,4 +38,9 @@ class NearbyServiceException implements Exception {
} }
final Object? error; final Object? error;
@override
String toString() {
return 'NearbyServiceException{error: $error}';
}
} }
+21 -17
View File
@@ -9,25 +9,12 @@ import 'package:path_provider/path_provider.dart';
class FilesSocket { class FilesSocket {
FilesSocket.startListening({ FilesSocket.startListening({
required this.content, required this.content,
required this.listener,
required this.onDestroy,
required WebSocket socket, required WebSocket socket,
required NearbyServiceFilesListener? listener,
required void Function(FilesSocket) onDestroy,
}) : _socket = socket { }) : _socket = socket {
_socket.listen( _socket.listen(
(event) async { _listener,
if (event is List<int>) {
addChunk(event);
} else if (event == separateCommandOf(_currentFileIndex)) {
_futures.add(_createFile(_currentFileIndex));
_currentFileIndex = _currentFileIndex + 1;
_bytesTable['$_currentFileIndex'] = [];
} else if (event == finishCommand) {
await Future.wait(_futures);
Logger.info('Files pack ${content.id} was created');
listener?.onData.call(_files);
onDestroy(this);
}
},
onError: listener?.onError, onError: listener?.onError,
cancelOnError: listener?.cancelOnError, cancelOnError: listener?.cancelOnError,
onDone: listener?.onDone, onDone: listener?.onDone,
@@ -35,13 +22,15 @@ class FilesSocket {
listener?.onCreated?.call(); listener?.onCreated?.call();
} }
static const finishCommand = '_@@FINISH_SENDING_FILE_'; static const finishCommand = '_@@FINISH_SENDING_FILES_';
static const separateCommand = '_@@SEPARATE_SENDING_FILE_'; static const separateCommand = '_@@SEPARATE_SENDING_FILE_';
static String separateCommandOf(int index) => '$separateCommand$index'; static String separateCommandOf(int index) => '$separateCommand$index';
final NearbyMessageFilesContent content; final NearbyMessageFilesContent content;
final void Function(String) onDestroy;
final NearbyServiceFilesListener? listener;
final WebSocket _socket; final WebSocket _socket;
final _files = <NearbyFile>[]; final _files = <NearbyFile>[];
@@ -66,6 +55,21 @@ class FilesSocket {
} }
} }
Future<void> _listener(dynamic event) async {
if (event is List<int>) {
addChunk(event);
} else if (event == separateCommandOf(_currentFileIndex)) {
_futures.add(_createFile(_currentFileIndex));
_currentFileIndex = _currentFileIndex + 1;
_bytesTable['$_currentFileIndex'] = [];
} else if (event == finishCommand) {
await Future.wait(_futures);
Logger.info('Files pack ${content.id} was created');
listener?.onData.call(_files);
onDestroy(content.id);
}
}
Future<void> _createFile(int index) async { Future<void> _createFile(int index) async {
try { try {
final bytes = _bytesTable['$index']!; final bytes = _bytesTable['$index']!;