chore(android) clean up android files sending
This commit is contained in:
+31
-11
@@ -395,12 +395,17 @@ class AppService extends ChangeNotifier {
|
||||
final filesListener = NearbyServiceFilesListener(
|
||||
onData: (event) async {
|
||||
final files = <File>[];
|
||||
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<AppService>().sendFilesAccept(content);
|
||||
if (value is bool) {
|
||||
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,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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<String, dynamic>? 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;
|
||||
|
||||
@@ -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<String, dynamic>? 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>({
|
||||
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<NearbyFileInfo> 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<String, dynamic>? json) {
|
||||
return NearbyMessageFileRequest._(
|
||||
factory NearbyMessageFilesRequest.fromJson(Map<String, dynamic>? 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<String, dynamic>? json) {
|
||||
return NearbyMessageFileResponse(
|
||||
///
|
||||
/// Gets [NearbyMessageFilesResponse] from [Map]
|
||||
///
|
||||
factory NearbyMessageFilesResponse.fromJson(Map<String, dynamic>? 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<String, dynamic> toJson() {
|
||||
return {
|
||||
@@ -289,4 +327,4 @@ class NearbyMessageFileResponse extends NearbyMessageFilesContent {
|
||||
String toString() {
|
||||
return 'NearbyMessageFileResponse{response: $response, id: $id, files: $files}';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 = <FilesSocket>[];
|
||||
final _serverWaitingContents = <String, HttpRequest>{};
|
||||
final NearbySocketPingManager _pingManager;
|
||||
|
||||
final _filesSockets = <String, FilesSocket>{};
|
||||
final _serverWaitingRequests = <String, HttpRequest>{};
|
||||
|
||||
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<void> closeAll() async {
|
||||
for (final fileSocket in _filesSockets) {
|
||||
for (final fileSocket in _filesSockets.values) {
|
||||
await fileSocket.close();
|
||||
}
|
||||
_filesSockets.clear();
|
||||
_filesListener = null;
|
||||
}
|
||||
|
||||
Future<void> _handleClientFileContent(
|
||||
Future<void> _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<void> _handleServerFileContent(
|
||||
Future<void> _startFilesServer(
|
||||
NearbyMessageFilesContent content,
|
||||
) async {
|
||||
MapEntry<String, HttpRequest>? 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<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 {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<bool> 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<int>) {
|
||||
// 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<ReceivedNearbyMessage>()
|
||||
.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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,4 +38,9 @@ class NearbyServiceException implements Exception {
|
||||
}
|
||||
|
||||
final Object? error;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'NearbyServiceException{error: $error}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<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);
|
||||
}
|
||||
},
|
||||
_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 = <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 {
|
||||
try {
|
||||
final bytes = _bytesTable['$index']!;
|
||||
|
||||
Reference in New Issue
Block a user