From 32a9f2a9231c6f5ba0cd91fbe2a9ae2e660cf512 Mon Sep 17 00:00:00 2001 From: ksenia312 Date: Sat, 3 Feb 2024 20:05:19 +0100 Subject: [PATCH] chore(android) clean up android files sending --- example/lib/main.dart | 42 +++- lib/src/models/nearby_file.dart | 24 ++- lib/src/models/nearby_message_content.dart | 144 ++++++++----- .../socket_service/file_sockets_manager.dart | 190 +++++++++--------- .../socket_service/nearby_socket_service.dart | 48 ++--- .../android/socket_service/network.dart | 6 +- lib/src/utils/exception.dart | 5 + lib/src/utils/file_socket.dart | 38 ++-- 8 files changed, 283 insertions(+), 214 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index 633956b..2e6ead3 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -395,12 +395,17 @@ class AppService extends ChangeNotifier { final filesListener = NearbyServiceFilesListener( onData: (event) async { final files = []; + final directory = Directory( + 'storage/emulated/0/Download', + ); + for (final nearbyFile in event) { - final content = nearbyFile.info; - final downloadsDir = Directory('storage/emulated/0/Download'); 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); } onFilesSaved?.call(files); @@ -436,22 +441,27 @@ class AppService extends ChangeNotifier { if (connectedDevice == null) return; _nearbyService.send( OutgoingNearbyMessage( - content: NearbyMessageFileRequest( - files: [...paths.map((e) => NearbyFileInfo(path: e))], + content: NearbyMessageFilesRequest( + files: [ + ...paths.map((e) => NearbyFileInfo(path: e)), + ], ), receiver: connectedDevice!.info, ), ); } - void sendFilesAccept(NearbyMessageFileRequest request) { + void sendFilesResponse( + NearbyMessageFilesRequest request, { + required bool response, + }) { if (connectedDevice == null) return; _nearbyService.send( OutgoingNearbyMessage( receiver: connectedDevice!.info, - content: NearbyMessageFileResponse.fromRequest( + content: NearbyMessageFilesResponse.fromRequest( request, - response: true, + response: response, ), ), ); @@ -827,17 +837,27 @@ class _ConnectedBody extends StatelessWidget { subtitle: senderSubtitle, ); }, - onFileRequest: (content) { + onFilesRequest: (content) { ActionDialog.show( context, title: 'Files request. Files count: ${content.files.length}', subtitle: senderSubtitle, ).then((value) { - if (value == true) { - context.read().sendFilesAccept(content); + if (value is bool) { + context.read().sendFilesResponse( + content, + response: value, + ); } }); }, + onFilesResponse: (content) { + AppShackBar.show( + Scaffold.of(context).context, + content.response ? 'Request is accepted!' : 'Request was denied :(', + subtitle: senderSubtitle, + ); + }, ); } diff --git a/lib/src/models/nearby_file.dart b/lib/src/models/nearby_file.dart index 0fff59d..3f642de 100644 --- a/lib/src/models/nearby_file.dart +++ b/lib/src/models/nearby_file.dart @@ -6,7 +6,9 @@ import 'package:nearby_service/nearby_service.dart'; /// communication channel. /// /// 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 { /// @@ -44,8 +46,14 @@ class NearbyFile { } } +/// +/// Quick info about the file +/// 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? json) { return NearbyFileInfo( @@ -53,8 +61,10 @@ class NearbyFileInfo { ); } + /// + /// Path of the representing file + /// final String path; - final int? sizeBytes; @override bool operator ==(Object other) => @@ -72,14 +82,20 @@ class NearbyFileInfo { }; } + /// + /// Quick access to the file [name] + /// String get name { try { return path.split('/').last; } catch (e) { - throw NearbyServiceException('Can\'t get fileName from $path'); + return path; } } + /// + /// Quick access to the file [extension] + /// String get extension { try { return name.split('.').last; diff --git a/lib/src/models/nearby_message_content.dart b/lib/src/models/nearby_message_content.dart index 38d77d0..9a39dd6 100644 --- a/lib/src/models/nearby_message_content.dart +++ b/lib/src/models/nearby_message_content.dart @@ -5,13 +5,14 @@ import 'package:nearby_service/src/utils/random.dart'; /// Type of the message. /// /// If [text], it will be a text message. -/// If [fileRequest], it will be a file request. After accepting request, -/// user can get file stream from connected device. +/// If [filesRequest], it will be a files pack request. +/// After accepting the request, +/// user can get files bytes stream from connected device. /// enum NearbyMessageContentType { text, - fileRequest, - fileResponse; + filesRequest, + filesResponse; /// /// 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 { - return this == NearbyMessageContentType.fileRequest; + bool get isFilesRequest { + return this == NearbyMessageContentType.filesRequest; } /// - /// Checks if this is [NearbyMessageContentType.fileResponse] + /// Checks if this is [NearbyMessageContentType.filesResponse] /// - bool get isFileResponse { - return this == NearbyMessageContentType.fileResponse; + bool get isFilesResponse { + return this == NearbyMessageContentType.filesResponse; } } @@ -43,20 +44,21 @@ abstract class NearbyMessageContent { const NearbyMessageContent(this._type); /// - /// Contains the conditional logic of creating [NearbyMessageFileRequest] - /// or [NearbyMessageTextContent] by `type` field of [json]. + /// Contains the conditional logic of creating [NearbyMessageFilesRequest], + /// [NearbyMessageFilesResponse] or [NearbyMessageTextContent] + /// by `type` field of [json]. /// factory NearbyMessageContent.fromJson(Map? json) { try { final type = NearbyMessageContentType.values.firstWhere( (e) => e.name == json?['type'], ); - if (type.isFileRequest) { - return NearbyMessageFileRequest.fromJson(json); - } else if (type.isText) { + if (type.isText) { return NearbyMessageTextContent.fromJson(json); - } else if (type.isFileResponse) { - return NearbyMessageFileResponse.fromJson(json); + } else if (type.isFilesRequest) { + return NearbyMessageFilesRequest.fromJson(json); + } else if (type.isFilesResponse) { + return NearbyMessageFilesResponse.fromJson(json); } else { throw NearbyServiceException.unsupportedDecoding(json); } @@ -76,23 +78,23 @@ abstract class NearbyMessageContent { /// * The [onText] callback returns this instance of [NearbyMessageContent], /// cast as [NearbyMessageTextContent] if is a text. /// - /// * The [onFileRequest] callback returns this instance of [NearbyMessageContent], - /// cast as [NearbyMessageFileRequest] if is a file request. + /// * The [onFilesRequest] callback returns this instance of [NearbyMessageContent], + /// cast as [NearbyMessageFilesRequest] if is a files pack request. /// - /// * The [onFileResponse] callback returns this instance of [NearbyMessageContent], - /// cast as [NearbyMessageFileResponse] if is a file response. + /// * The [onFilesResponse] callback returns this instance of [NearbyMessageContent], + /// cast as [NearbyMessageFilesResponse] if is a files pack response. /// T? byType({ T Function(NearbyMessageTextContent)? onText, - T Function(NearbyMessageFileRequest)? onFileRequest, - T Function(NearbyMessageFileResponse)? onFileResponse, + T Function(NearbyMessageFilesRequest)? onFilesRequest, + T Function(NearbyMessageFilesResponse)? onFilesResponse, }) { if (this is NearbyMessageTextContent && onText != null) { return onText(this as NearbyMessageTextContent); - } else if (this is NearbyMessageFileRequest && onFileRequest != null) { - return onFileRequest(this as NearbyMessageFileRequest); - } else if (this is NearbyMessageFileResponse && onFileResponse != null) { - return onFileResponse(this as NearbyMessageFileResponse); + } else if (this is NearbyMessageFilesRequest && onFilesRequest != null) { + return onFilesRequest(this as NearbyMessageFilesRequest); + } else if (this is NearbyMessageFilesResponse && onFilesResponse != null) { + return onFilesResponse(this as NearbyMessageFilesResponse); } 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( super.type, { required this.id, required this.files, }); + /// + /// Info about the files to be sent or received. + /// final List files; + /// + /// ID of this files pack + /// final String id; @override @@ -179,6 +197,13 @@ abstract class NearbyMessageFilesContent extends NearbyMessageContent { }; } + @override + bool get isValid => + files.isNotEmpty && + files.every( + (element) => element.path.isNotEmpty, + ); + @override bool operator ==(Object other) => identical(this, other) || @@ -197,31 +222,31 @@ abstract class NearbyMessageFilesContent extends NearbyMessageContent { } /// -/// Nearby message File content. Used for file sending requests. -/// Does not contain file bytes! +/// Nearby message File Request. Used for file sending requests. +/// Does not contain files' bytes! /// -class NearbyMessageFileRequest extends NearbyMessageFilesContent { - const NearbyMessageFileRequest._({ +class NearbyMessageFilesRequest extends NearbyMessageFilesContent { + const NearbyMessageFilesRequest._({ required super.id, required super.files, }) : super( - NearbyMessageContentType.fileRequest, + NearbyMessageContentType.filesRequest, ); /// /// Basic constructor with [files] to be sent or received. /// - NearbyMessageFileRequest({required super.files}) + NearbyMessageFilesRequest({required super.files}) : super( - NearbyMessageContentType.fileRequest, + NearbyMessageContentType.filesRequest, id: RandomUtils.instance.nextInt(1000000, 9999999).toString(), ); /// - /// Gets [NearbyMessageFileRequest] from [json]. + /// Gets [NearbyMessageFilesRequest] from [json]. /// - factory NearbyMessageFileRequest.fromJson(Map? json) { - return NearbyMessageFileRequest._( + factory NearbyMessageFilesRequest.fromJson(Map? json) { + return NearbyMessageFilesRequest._( id: json?['id'] ?? '', files: [ ...?(json?['files'] as List?)?.map( @@ -231,37 +256,50 @@ class NearbyMessageFileRequest extends NearbyMessageFilesContent { ); } - @override - bool get isValid => files.every((element) => element.path.isNotEmpty); - @override String toString() { 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.files, required this.response, }) : 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, }) { - return NearbyMessageFileResponse( + return NearbyMessageFilesResponse( id: request.id, files: request.files, response: response, ); } - factory NearbyMessageFileResponse.fromJson(Map? json) { - return NearbyMessageFileResponse( + /// + /// Gets [NearbyMessageFilesResponse] from [Map] + /// + factory NearbyMessageFilesResponse.fromJson(Map? json) { + return NearbyMessageFilesResponse( id: json?['id'] ?? '', files: [ ...?(json?['files'] as List?)?.map( @@ -272,11 +310,11 @@ class NearbyMessageFileResponse extends NearbyMessageFilesContent { ); } + /// + /// The main response to the received [NearbyMessageFilesRequest]. + /// final bool response; - @override - bool get isValid => files.every((element) => element.path.isNotEmpty); - @override Map toJson() { return { @@ -289,4 +327,4 @@ class NearbyMessageFileResponse extends NearbyMessageFilesContent { String toString() { return 'NearbyMessageFileResponse{response: $response, id: $id, files: $files}'; } -} +} \ No newline at end of file diff --git a/lib/src/platforms/android/socket_service/file_sockets_manager.dart b/lib/src/platforms/android/socket_service/file_sockets_manager.dart index 260e0a8..cf5b624 100644 --- a/lib/src/platforms/android/socket_service/file_sockets_manager.dart +++ b/lib/src/platforms/android/socket_service/file_sockets_manager.dart @@ -1,12 +1,14 @@ part of 'nearby_socket_service.dart'; class FileSocketsManager { - FileSocketsManager(this._network, this._service); + FileSocketsManager(this._network, this._service, this._pingManager); final NearbyServiceNetwork _network; final NearbyAndroidService _service; - final _filesSockets = []; - final _serverWaitingContents = {}; + final NearbySocketPingManager _pingManager; + + final _filesSockets = {}; + final _serverWaitingRequests = {}; NearbyServiceFilesListener? _filesListener; @@ -16,9 +18,9 @@ class FileSocketsManager { void onWsRequest(HttpRequest request) { final type = NearbySocketType.fromRequest(request); - final fileId = NearbyFileId.fromRequest(request); - if (type == NearbySocketType.file && fileId != null) { - _serverWaitingContents[fileId] = request; + final filesPackId = NearbyFilesPackId.fromRequest(request); + if (type == NearbySocketType.file && filesPackId != null) { + _serverWaitingRequests[filesPackId] = request; } } @@ -27,114 +29,124 @@ class FileSocketsManager { required NearbyAndroidCommunicationChannelData androidData, required bool isReceived, }) async { - final info = await _service.getConnectionInfo(); - if (info != null && info.groupFormed) { - if (info.isGroupOwner) { - await _handleServerFileContent(content); - if (content is NearbyMessageFileResponse && isReceived) { - await _tryTransferData(content); - } - } else { - await _handleClientFileContent( - content, - connectionData: androidData, - ownerIpAddress: info.ownerIpAddress, - ); - if (content is NearbyMessageFileRequest && !isReceived) { - await _tryTransferData(content); + final shouldStartSocket = content.byType( + onFilesResponse: (response) => response.response, + onFilesRequest: (_) => true, + ) ?? + false; + + if (shouldStartSocket) { + final info = await _service.getConnectionInfo(); + if (info != null && info.groupFormed) { + if (info.isGroupOwner) { + await _startFilesServer(content); + if (content is NearbyMessageFilesResponse && 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 closeAll() async { - for (final fileSocket in _filesSockets) { + for (final fileSocket in _filesSockets.values) { await fileSocket.close(); } _filesSockets.clear(); _filesListener = null; } - Future _handleClientFileContent( + Future _connectToFilesSocket( NearbyMessageFilesContent content, { required NearbyAndroidCommunicationChannelData connectionData, required String ownerIpAddress, }) async { try { - final shouldStartFileSocket = content.byType( - onFileResponse: (response) => response.response, - onFileRequest: (request) => true, - ) ?? - false; - if (shouldStartFileSocket) { - final socket = await _network.connectToSocket( - ownerIpAddress: ownerIpAddress, - port: connectionData.port, - socketType: NearbySocketType.file, - headers: { - NearbyFileId.key: content.id, - }, + final response = await _network.pingServer( + address: ownerIpAddress, + port: connectionData.port, + ); + if (await _pingManager.checkPong(response)) { + await _tryStartFileSocket( + content, + onCreateSocket: () => _network.connectToSocket( + ownerIpAddress: ownerIpAddress, + port: connectionData.port, + socketType: NearbySocketType.file, + 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) { Logger.error(e); } } - Future _handleServerFileContent( + Future _startFilesServer( NearbyMessageFilesContent content, ) async { - MapEntry? request; - try { - request = _serverWaitingContents.entries.firstWhere( - (element) => element.key == content.id, - ); - } catch (e) { - request = null; - } + final request = _serverWaitingRequests[content.id]; if (request != null) { - Logger.debug('Found cached server file request ${request.key}'); - _filesSockets.add( - FilesSocket.startListening( - content: content, - socket: await WebSocketTransformer.upgrade(request.value), - listener: _filesListener, - onDestroy: _filesSockets.remove, - ), + Logger.debug('Found cached server file request ${content.id}'); + + final result = await _tryStartFileSocket( + content, + onCreateSocket: () => WebSocketTransformer.upgrade(request), ); - _serverWaitingContents.remove(content.id); - Logger.info('Created a socket for the files pack ${content.id}'); + if (result) { + _serverWaitingRequests.remove(content.id); + } } } + Future _tryStartFileSocket( + NearbyMessageFilesContent content, { + required Future 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 _tryTransferData(NearbyMessageFilesContent content) async { - Logger.debug('Start transferring the files pack ${content.id}'); - final filesSocket = _find(content.id); + final filesSocket = _filesSockets[content.id]; if (filesSocket != null) { + Logger.debug('Start transferring the files pack ${content.id}'); for (var i = 0; i < content.files.length; i++) { try { final fileInfo = content.files[i]; @@ -161,22 +173,6 @@ class FileSocketsManager { required FilesSocket filesSocket, required File file, }) { - return file.openRead().listen( - (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; + return file.openRead().listen(filesSocket.sendData); } } diff --git a/lib/src/platforms/android/socket_service/nearby_socket_service.dart b/lib/src/platforms/android/socket_service/nearby_socket_service.dart index 22ffbb3..38a1cae 100644 --- a/lib/src/platforms/android/socket_service/nearby_socket_service.dart +++ b/lib/src/platforms/android/socket_service/nearby_socket_service.dart @@ -24,7 +24,11 @@ class NearbySocketService { final NearbyAndroidService _service; final _pingManager = NearbySocketPingManager(); final _network = NearbyServiceNetwork(); - late final _fileSocketsManager = FileSocketsManager(_network, _service); + late final _fileSocketsManager = FileSocketsManager( + _network, + _service, + _pingManager, + ); 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 send(OutgoingNearbyMessage message) async { if (message.isValid) { @@ -199,36 +203,22 @@ class NearbySocketService { Logger.debug('Starting socket subscription'); if (_connectedDeviceId != null) { - _messagesSubscription = _socket?.listen( - (event) async { - // if (fileLoaders != null) { - // if (event is List) { - // fileLoaders!.add(event); - // } else if (event == fileLoaders?.finishCommand) { - // final file = await fileLoaders!.getFile().whenComplete( - // () { - // fileLoaders = null; - // }, - // ); - // socketListener.onFile?.call(file); - // } - // } else { + _messagesSubscription = _socket + ?.map(MessagesStreamMapper.toMessage) + .where((e) => e != null) + .cast() + .map((e) => MessagesStreamMapper.replaceId(e, _connectedDeviceId!)) + .listen( + (message) async { try { - final message = MessagesStreamMapper.toMessage(event); - if (message != null) { - final newMessage = MessagesStreamMapper.replaceId( - message, - _connectedDeviceId!, + if (message.content is NearbyMessageFilesContent) { + _fileSocketsManager.handleFileMessageContent( + message.content as NearbyMessageFilesContent, + androidData: _androidData, + 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) { Logger.error(e); } diff --git a/lib/src/platforms/android/socket_service/network.dart b/lib/src/platforms/android/socket_service/network.dart index 1d1a0e5..08aba58 100644 --- a/lib/src/platforms/android/socket_service/network.dart +++ b/lib/src/platforms/android/socket_service/network.dart @@ -101,10 +101,10 @@ enum NearbySocketType { } } -class NearbyFileId { - static const key = 'FileID'; +class NearbyFilesPackId { + static const key = 'FilesPackID'; static String? fromRequest(HttpRequest request) { - return request.headers.value(NearbyFileId.key); + return request.headers.value(NearbyFilesPackId.key); } } diff --git a/lib/src/utils/exception.dart b/lib/src/utils/exception.dart index 7145801..c6fa622 100644 --- a/lib/src/utils/exception.dart +++ b/lib/src/utils/exception.dart @@ -38,4 +38,9 @@ class NearbyServiceException implements Exception { } final Object? error; + + @override + String toString() { + return 'NearbyServiceException{error: $error}'; + } } diff --git a/lib/src/utils/file_socket.dart b/lib/src/utils/file_socket.dart index 4243a6e..a2a9c16 100644 --- a/lib/src/utils/file_socket.dart +++ b/lib/src/utils/file_socket.dart @@ -9,25 +9,12 @@ import 'package:path_provider/path_provider.dart'; class FilesSocket { FilesSocket.startListening({ required this.content, + required this.listener, + required this.onDestroy, required WebSocket socket, - required NearbyServiceFilesListener? listener, - required void Function(FilesSocket) onDestroy, }) : _socket = socket { _socket.listen( - (event) async { - if (event is List) { - 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); - } - }, + _listener, onError: listener?.onError, cancelOnError: listener?.cancelOnError, onDone: listener?.onDone, @@ -35,13 +22,15 @@ class FilesSocket { listener?.onCreated?.call(); } - static const finishCommand = '_@@FINISH_SENDING_FILE_'; + static const finishCommand = '_@@FINISH_SENDING_FILES_'; static const separateCommand = '_@@SEPARATE_SENDING_FILE_'; static String separateCommandOf(int index) => '$separateCommand$index'; final NearbyMessageFilesContent content; + final void Function(String) onDestroy; + final NearbyServiceFilesListener? listener; final WebSocket _socket; final _files = []; @@ -66,6 +55,21 @@ class FilesSocket { } } + Future _listener(dynamic event) async { + if (event is List) { + 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 _createFile(int index) async { try { final bytes = _bytesTable['$index']!;