feat(android, ios): remove files field from files response

This commit is contained in:
ksenia312
2024-02-05 12:28:49 +01:00
parent d6840a4313
commit 1741ee3458
9 changed files with 203 additions and 134 deletions
+4 -4
View File
@@ -479,15 +479,15 @@ class AppService extends ChangeNotifier {
}
void sendFilesResponse(
NearbyMessageFilesRequest request, {
String requestId, {
required bool response,
}) {
if (connectedDevice == null) return;
_nearbyService.send(
OutgoingNearbyMessage(
receiver: connectedDevice!.info,
content: NearbyMessageFilesResponse.fromRequest(
request,
content: NearbyMessageFilesResponse(
id: requestId,
response: response,
),
),
@@ -869,7 +869,7 @@ class _ConnectedBody extends StatelessWidget {
).then((value) {
if (value is bool) {
context.read<AppService>().sendFilesResponse(
content,
content.id,
response: value,
);
}
+26 -19
View File
@@ -58,57 +58,64 @@ class NearbyMessageTextContent : NearbyMessageContent {
class NearbyMessageFilesContent : NearbyMessageContent {
init(files: Array<String>, id: String, type: MessageContentType) {
self.files = files
init(id: String, type: MessageContentType) {
self.id = id
super.init(type: type)
}
static func fromJsonRaw(type: MessageContentType, json: [String: Any]) -> NearbyMessageFilesContent? {
if let filesObjects: Array = json["files"] as? Array<Dictionary<String, AnyObject>> {
let files : [String]? = filesObjects.map({ $0["path"] as? String }).compactMap({$0})
if let id: String = json["id"] as? String{
if let requireFiles = files {
return NearbyMessageFilesContent(files: requireFiles, id: id, type: type)
}
}
if let id: String = json["id"] as? String{
return NearbyMessageFilesContent(id: id, type: type)
}
return nil;
}
override func toJson() -> [String : Any] {
return [
"files": files.map{["path": $0]},
"id": id,
].merging( super.toJson()) { (current, _) in current}
}
let files: Array<String>
let id: String
}
class NearbyMessageFilesRequest : NearbyMessageFilesContent {
init(files: Array<String>, id: String) {
super.init(files: files, id: id, type: MessageContentType.filesRequest)
self.files = files
super.init(id: id, type: MessageContentType.filesRequest)
}
static func fromJson( json: [String: Any]) -> NearbyMessageFilesRequest? {
static func fromJson(json: [String: Any]) -> NearbyMessageFilesRequest? {
let message = NearbyMessageFilesContent.fromJsonRaw(type: MessageContentType.filesRequest, json: json)
if let requireMessage = message {
return NearbyMessageFilesRequest(files: requireMessage.files, id: requireMessage.id)
if let filesObjects: Array = json["files"] as? Array<Dictionary<String, AnyObject>> {
let files : [String]? = filesObjects.map({ $0["path"] as? String }).compactMap({$0})
if let requireMessage = message {
if let requireFiles = files {
return NearbyMessageFilesRequest(files: requireFiles, id: requireMessage.id)
}
}
}
return nil
return nil;
}
let files: Array<String>
override func toJson() -> [String : Any] {
return [
"files": files.map{["path": $0]},
].merging( super.toJson()) { (current, _) in current}
}
}
class NearbyMessageFilesResponse : NearbyMessageFilesContent {
init(files: Array<String>, id: String, response: Bool) {
init(id: String, response: Bool) {
self.response = response
super.init(files: files, id: id, type: MessageContentType.filesResponse)
super.init(id: id, type: MessageContentType.filesResponse)
}
static func fromJson( json: [String: Any]) -> NearbyMessageFilesResponse? {
let message = NearbyMessageFilesContent.fromJsonRaw(type: MessageContentType.filesResponse, json: json)
if let requireMessage = message,
let response = json["response"] as? Bool {
return NearbyMessageFilesResponse(files: requireMessage.files, id: requireMessage.id, response: response)
return NearbyMessageFilesResponse(id: requireMessage.id, response: response)
}
return nil
}
+4
View File
@@ -107,6 +107,10 @@ class NearbyManager: NSObject {
do {
if let requireDevice = device {
let message = NearbyMessage(content: content, senderName: self.device.name, senderPeerID: self.device.peerID)
if (content is NearbyMessageFilesRequest) {
NearbyRequestsStore.instance.add(request: message.content as! NearbyMessageFilesRequest)
}
try requireDevice.session?.session?.send(
try JSONSerialization.data(withJSONObject: message.toDictionary()),
toPeers: [requireDevice.peerID],
+6 -4
View File
@@ -9,18 +9,21 @@ import Foundation
import Flutter
extension NearbyServicePlugin {
@objc func onMessageReceived(notification: Notification) {
DispatchQueue.main.async {
if let userInfo = NearbyUserInfo.fromDictionary(userInfo: notification.userInfo) {
if let message = NearbyMessage.fromUserInfo(userInfo: userInfo) {
if message.content is NearbyMessageFilesResponse {
let response = message.content as! NearbyMessageFilesResponse
if (response.response) {
let cachedRequest = NearbyRequestsStore.instance.find(for: response.id)
if (response.response && cachedRequest != nil) {
self.manager.sendFiles(
id: response.id,
paths: response.files,
id: cachedRequest!.id,
paths: cachedRequest!.files,
with: message.senderPeerID.displayName
)
NearbyRequestsStore.instance.remove(for: cachedRequest!.id)
}
}
self.channel.invokeMethod(DART_COMMAND_MESSAGE_RECEIVED, arguments: message.toDartFormat())
@@ -33,7 +36,6 @@ extension NearbyServicePlugin {
}
@objc func onResourceReceived(notification: Notification) {
DispatchQueue.main.async {
if let userInfo = NearbyUserInfo.fromDictionary(userInfo: notification.userInfo) {
@@ -0,0 +1,29 @@
//
// NearbyRequestsStore.swift
// nearby_service
//
// Created by Kseniia Nikitina on 5/2/24.
//
import Foundation
class NearbyRequestsStore {
static let instance = NearbyRequestsStore()
private var requests : [NearbyMessageFilesRequest] = []
func add(request: NearbyMessageFilesRequest) {
requests.append(request)
}
func find(for id: String) -> NearbyMessageFilesRequest? {
return requests.first { request in
return request.id == id
}
}
func remove(for id: String) {
self.requests = requests.filter{$0.id != id}
}
}
+46 -50
View File
@@ -64,15 +64,7 @@ sealed class NearbyMessageFilesContent extends NearbyMessageContentBase {
/// Also [NearbyMessageFilesContent] contains [id] of the files pack and
/// list of [NearbyFileInfo] to determine the files.
///
const NearbyMessageFilesContent({
required this.id,
required this.files,
});
///
/// Info about the files to be sent or received.
///
final List<NearbyFileInfo> files;
const NearbyMessageFilesContent({required this.id});
///
/// ID of this files pack
@@ -83,51 +75,43 @@ sealed class NearbyMessageFilesContent extends NearbyMessageContentBase {
Map<String, dynamic> toJson() {
return {
'id': id,
'files': [
...files.map((e) => e.toJson()),
],
...super.toJson(),
};
}
@override
bool get isValid =>
files.isNotEmpty &&
files.every(
(element) => element.path.isNotEmpty,
);
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is NearbyMessageFilesContent &&
runtimeType == other.runtimeType &&
files == other.files &&
id == other.id;
@override
int get hashCode => files.hashCode ^ id.hashCode;
int get hashCode => id.hashCode;
@override
String toString() {
return 'NearbyMessageFilesContent{files: $files, id: $id}';
return 'NearbyMessageFilesContent{id: $id}';
}
@override
bool get isValid => id.isNotEmpty;
}
///
/// Nearby message File Request. Used for file sending requests.
/// Does not contain files' bytes!
/// Nearby message File Request. Used for files sending requests.
/// Contains info about the [files].
///
final class NearbyMessageFilesRequest extends NearbyMessageFilesContent {
const NearbyMessageFilesRequest._({
required super.id,
required super.files,
required this.files,
});
///
/// Basic constructor with [files] to be sent or received.
///
NearbyMessageFilesRequest({required super.files})
NearbyMessageFilesRequest({required this.files})
: super(
id: RandomUtils.instance.nextInt(1000000, 9999999).toString(),
);
@@ -146,6 +130,11 @@ final class NearbyMessageFilesRequest extends NearbyMessageFilesContent {
);
}
///
/// Info about the files to be sent or received.
///
final List<NearbyFileInfo> files;
@override
NearbyMessageContentType get type => NearbyMessageContentType.filesRequest;
@@ -153,6 +142,35 @@ final class NearbyMessageFilesRequest extends NearbyMessageFilesContent {
String toString() {
return 'NearbyMessageFileRequest{id: $id, files: $files}';
}
@override
Map<String, dynamic> toJson() {
return {
'files': [
...files.map((e) => e.toJson()),
],
...super.toJson(),
};
}
@override
bool get isValid =>
super.isValid &&
files.isNotEmpty &&
files.every(
(element) => element.path.isNotEmpty,
);
@override
bool operator ==(Object other) =>
identical(this, other) ||
super == other &&
other is NearbyMessageFilesRequest &&
runtimeType == other.runtimeType &&
files == other.files;
@override
int get hashCode => super.hashCode ^ files.hashCode;
}
///
@@ -162,41 +180,19 @@ final class NearbyMessageFilesRequest extends NearbyMessageFilesContent {
final 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.
/// Provide [id] from [NearbyMessageFilesRequest] or
///
NearbyMessageFilesResponse({
required super.id,
required super.files,
required this.response,
});
///
/// Factory to quickly create a response to [NearbyMessageFilesRequest].
///
factory NearbyMessageFilesResponse.fromRequest(
NearbyMessageFilesRequest request, {
required bool response,
}) {
return NearbyMessageFilesResponse(
id: request.id,
files: request.files,
response: response,
);
}
///
/// Gets [NearbyMessageFilesResponse] from [Map]
///
factory NearbyMessageFilesResponse.fromJson(Map<String, dynamic>? json) {
return NearbyMessageFilesResponse(
id: json?['id'] ?? '',
files: [
...?(json?['files'] as List?)?.map(
(e) => NearbyFileInfo.fromJson(e),
),
],
response: json?['response'] ?? false,
);
}
@@ -219,6 +215,6 @@ final class NearbyMessageFilesResponse extends NearbyMessageFilesContent {
@override
String toString() {
return 'NearbyMessageFileResponse{response: $response, id: $id, files: $files}';
return 'NearbyMessageFileResponse{response: $response, id: $id}';
}
}
@@ -12,15 +12,23 @@ class FileSocketsManager {
NearbyServiceFilesListener? _filesListener;
NearbyDeviceInfo? _sender;
NearbyMessageFilesRequest? _cachedFilesRequest;
Future? _socketCreationFuture;
var _connectionData = const NearbyAndroidCommunicationChannelData();
void setListener(NearbyServiceFilesListener? listener) {
_filesListener = listener;
}
void setConnectionData(NearbyAndroidCommunicationChannelData? data) {
_connectionData = data ?? _connectionData;
}
void onWsRequest(HttpRequest request) {
final type = NearbySocketType.fromRequest(request);
final filesPackId = NearbyFilesPackId.fromRequest(request);
if (type == NearbySocketType.file && filesPackId != null) {
Logger.debug('Save a connection request $filesPackId from client');
_serverWaitingRequests[filesPackId] = request;
}
}
@@ -28,42 +36,62 @@ class FileSocketsManager {
Future<void> handleFileMessageContent(
NearbyMessageFilesContent content, {
required NearbyDeviceInfo? sender,
required NearbyAndroidCommunicationChannelData androidData,
required bool isReceived,
}) async {
if (sender != null) {
_sender = sender;
Logger.debug('Sender was set to $_sender');
}
final socketExists = _filesSockets[content.id] != null;
final shouldStartSocket = content.byType(
onFilesResponse: (response) => response.response,
onFilesRequest: (_) => true,
) ??
false;
final isRequest = content is NearbyMessageFilesRequest;
final alreadyExists = _filesSockets[content.id] != null;
final isPositiveResponse =
content is NearbyMessageFilesResponse && content.response;
if (shouldStartSocket && !alreadyExists) {
if (isRequest) {
_cachedFilesRequest = content;
Logger.debug('Files pack request ${content.id} was cached');
}
if (!socketExists) {
final info = await _service.getConnectionInfo();
if (info != null && info.groupFormed) {
if (info.isGroupOwner) {
await _startFilesServer(content);
if (isReceived && content is NearbyMessageFilesResponse) {
await _startDataTransfer(content);
if (isPositiveResponse && _cachedFilesRequest != null) {
_socketCreationFuture = _startFilesServerSocket(
_cachedFilesRequest!,
);
}
} else {
await _connectToFilesSocket(
content,
connectionData: androidData,
ownerIpAddress: info.ownerIpAddress,
);
if (!isReceived && content is NearbyMessageFilesRequest) {
await _startDataTransfer(content);
NearbyMessageFilesRequest? request;
if (!isReceived &&
isPositiveResponse &&
_cachedFilesRequest != null) {
request = _cachedFilesRequest!;
} else if (isRequest) {
request = content;
}
if (request != null) {
_socketCreationFuture = _connectToFilesSocket(
request,
ownerIpAddress: info.ownerIpAddress,
);
}
}
}
}
if (isReceived && isPositiveResponse && _cachedFilesRequest != null) {
await _socketCreationFuture?.whenComplete(
() async {
await _startDataTransfer(_cachedFilesRequest!);
_socketCreationFuture = null;
_cachedFilesRequest = null;
},
);
}
}
Future<void> closeAll() async {
@@ -72,39 +100,39 @@ class FileSocketsManager {
}
_filesSockets.clear();
_filesListener = null;
_cachedFilesRequest = null;
_socketCreationFuture = null;
}
Future<void> _connectToFilesSocket(
NearbyMessageFilesContent content, {
required NearbyAndroidCommunicationChannelData connectionData,
NearbyMessageFilesRequest filesRequest, {
required String ownerIpAddress,
}) async {
try {
final response = await _network.pingServer(
address: ownerIpAddress,
port: connectionData.port,
port: _connectionData.port,
);
if (await _pingManager.checkPong(response)) {
await _tryStartFileSocket(
content,
await _startFilesSocket(
filesRequest,
onCreateSocket: () => _network.connectToSocket(
ownerIpAddress: ownerIpAddress,
port: connectionData.port,
port: _connectionData.port,
socketType: NearbySocketType.file,
headers: {
NearbyFilesPackId.key: content.id,
NearbyFilesPackId.key: filesRequest.id,
},
),
);
} else {
Logger.debug(
'Files server is unavailable, reconnect in ${connectionData.clientReconnectInterval}s',
'Files server is unavailable, reconnect in ${_connectionData.clientReconnectInterval}s',
);
await Future.delayed(
connectionData.clientReconnectInterval,
_connectionData.clientReconnectInterval,
() => _connectToFilesSocket(
content,
connectionData: connectionData,
filesRequest,
ownerIpAddress: ownerIpAddress,
),
);
@@ -114,50 +142,52 @@ class FileSocketsManager {
}
}
Future<void> _startFilesServer(NearbyMessageFilesContent content) async {
final request = _serverWaitingRequests[content.id];
Future<void> _startFilesServerSocket(
NearbyMessageFilesRequest filesRequest,
) async {
final connectionRequest = _serverWaitingRequests[filesRequest.id];
if (request != null) {
Logger.debug('Found cached server file request ${content.id}');
if (connectionRequest != null) {
Logger.debug('Found cached server files request ${filesRequest.id}');
final result = await _tryStartFileSocket(
content,
onCreateSocket: () => WebSocketTransformer.upgrade(request),
final result = await _startFilesSocket(
filesRequest,
onCreateSocket: () => WebSocketTransformer.upgrade(connectionRequest),
);
if (result) {
_serverWaitingRequests.remove(content.id);
_serverWaitingRequests.remove(filesRequest.id);
}
}
}
Future<bool> _tryStartFileSocket(
NearbyMessageFilesContent content, {
Future<bool> _startFilesSocket(
NearbyMessageFilesRequest filesRequest, {
required Future<WebSocket?> Function() onCreateSocket,
}) async {
final socket = await onCreateSocket();
if (socket != null && _sender != null) {
_filesSockets[content.id] = FilesSocket.startListening(
_filesSockets[filesRequest.id] = FilesSocket.startListening(
sender: _sender!,
content: content,
filesRequest: filesRequest,
socket: socket,
listener: _filesListener,
onDestroy: _filesSockets.remove,
);
Logger.info('Created a socket for the files pack ${content.id}');
Logger.info('Created a socket for the files pack ${filesRequest.id}');
return true;
}
return false;
}
Future<void> _startDataTransfer(NearbyMessageFilesContent content) async {
final filesSocket = _filesSockets[content.id];
Future<void> _startDataTransfer(NearbyMessageFilesRequest request) async {
final filesSocket = _filesSockets[request.id];
if (filesSocket != null) {
Logger.debug('Start transferring the files pack ${content.id}');
for (var i = 0; i < content.files.length; i++) {
Logger.debug('Start transferring the files pack ${request.id}');
for (var i = 0; i < request.files.length; i++) {
try {
final fileInfo = content.files[i];
final fileInfo = request.files[i];
await _streamFile(
content.id,
request.id,
filesSocket: filesSocket,
file: File(fileInfo.path),
)?.asFuture();
@@ -170,7 +200,7 @@ class FileSocketsManager {
}
}
filesSocket.sendData(FilesSocket.finishCommand);
Logger.debug('Sent finish command for the pack ${content.id}');
Logger.debug('Sent finish command for the pack ${request.id}');
}
}
@@ -58,7 +58,9 @@ class NearbySocketService {
_androidData = data.androidData;
_connectedDeviceId = data.connectedDeviceId;
_fileSocketsManager.setListener(data.filesListener);
_fileSocketsManager
..setListener(data.filesListener)
..setConnectionData(data.androidData);
final info = await _service.getConnectionInfo();
@@ -239,7 +241,6 @@ class NearbySocketService {
message.content as NearbyMessageFilesContent,
isReceived: message is ReceivedNearbyMessage,
sender: message is ReceivedNearbyMessage ? message.sender : null,
androidData: _androidData,
);
}
}
+6 -6
View File
@@ -9,7 +9,7 @@ import 'package:path_provider/path_provider.dart';
class FilesSocket {
FilesSocket.startListening({
required this.sender,
required this.content,
required this.filesRequest,
required this.listener,
required this.onDestroy,
required WebSocket socket,
@@ -29,7 +29,7 @@ class FilesSocket {
static String separateCommandOf(int index) => '$separateCommand$index';
final NearbyMessageFilesContent content;
final NearbyMessageFilesRequest filesRequest;
final void Function(String) onDestroy;
final NearbyServiceFilesListener? listener;
final NearbyDeviceInfo sender;
@@ -52,7 +52,7 @@ class FilesSocket {
final logStep = min(pow(10, _chunksCount.toString().length - 1), 100);
if (_chunksCount % logStep == 0) {
Logger.debug(
'Got $_chunksCount chunks for the file ${content.files[_currentFileIndex].name}',
'Got $_chunksCount chunks for the file ${filesRequest.files[_currentFileIndex].name}',
);
}
}
@@ -66,7 +66,7 @@ class FilesSocket {
_bytesTable['$_currentFileIndex'] = [];
} else if (event == finishCommand) {
await Future.wait(_futures);
Logger.info('Files pack ${content.id} was created');
Logger.info('Files pack ${filesRequest.id} was created');
listener?.onData.call(
ReceivedNearbyFilesPack(
@@ -74,14 +74,14 @@ class FilesSocket {
files: _files,
),
);
onDestroy(content.id);
onDestroy(filesRequest.id);
}
}
Future<void> _createFile(int index) async {
try {
final bytes = _bytesTable['$index']!;
final fileInfo = content.files[index];
final fileInfo = filesRequest.files[index];
final directory = await getTemporaryDirectory();
final file = File('${directory.path}/${fileInfo.name}');
await file.writeAsBytes(bytes);