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(
|
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,
|
||||||
|
);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,4 +38,9 @@ class NearbyServiceException implements Exception {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final Object? error;
|
final Object? error;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'NearbyServiceException{error: $error}';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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']!;
|
||||||
|
|||||||
Reference in New Issue
Block a user