refactor(lib): create structure with base and interface

This commit is contained in:
ksenia312
2024-02-04 19:28:28 +01:00
parent 90c2908496
commit 3f7495a6a4
31 changed files with 423 additions and 383 deletions
@@ -0,0 +1,5 @@
///
/// The status of the communication channel for data exchange.
/// Use it to determine if you can send data over the communication channel or not.
///
enum CommunicationChannelState { notConnected, loading, connected }
+7
View File
@@ -0,0 +1,7 @@
export 'nearby_device_info.dart';
export 'nearby_device_status.dart';
export 'nearby_message.dart';
export 'nearby_message_content.dart';
export 'communication_channel_state.dart';
export 'nearby_file.dart';
export 'nearby_message_content_type.dart';
+67
View File
@@ -0,0 +1,67 @@
import 'package:nearby_service/src/utils/unknown.dart';
///
/// Minimal information about the device.
///
final class NearbyDeviceInfo {
///
/// Used to connect to the device via [id].
/// Depending on the platform, [id] means different parameters.
///
/// * For Android [id] is the MAC address of the device.
/// * For IOS [id] is the [MCPeerID](https://developer.apple.com/documentation/multipeerconnectivity/mcpeerid) passed from the IOS platform.
///
const NearbyDeviceInfo({
required this.displayName,
required this.id,
});
///
/// Get [NearbyDeviceInfo] from [Map].
///
factory NearbyDeviceInfo.fromJson(Map<String, dynamic>? json) {
return NearbyDeviceInfo(
displayName: json?['displayName'] ?? kNearbyUnknown,
id: json?['id'],
);
}
///
/// The name of the device in the context of a P2P network.
///
final String displayName;
///
/// Depending on the platform, [id] means different parameters.
///
/// * For Android [id] is the MAC address of the device.
/// * For IOS [id] is the [MCPeerID](https://developer.apple.com/documentation/multipeerconnectivity/mcpeerid) passed from the IOS platform.
///
final String id;
///
/// Get [Map] from [NearbyDeviceInfo].
///
Map<String, dynamic> toJson() {
return {
'id': id,
'displayName': displayName,
};
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is NearbyDeviceInfo &&
runtimeType == other.runtimeType &&
displayName == other.displayName &&
id == other.id;
@override
int get hashCode => displayName.hashCode ^ id.hashCode;
@override
String toString() {
return 'NearbyDeviceInfo{displayName: $displayName, id: $id}';
}
}
+72
View File
@@ -0,0 +1,72 @@
///
/// Status of device connection.
///
enum NearbyDeviceStatus {
available,
connected,
failed,
connecting,
unavailable;
///
/// Checks if status is [NearbyDeviceStatus.connected]
///
bool get isConnected => this == NearbyDeviceStatus.connected;
///
/// Checks if status is [NearbyDeviceStatus.available]
///
bool get isAvailable => this == NearbyDeviceStatus.available;
///
/// Checks if status is [NearbyDeviceStatus.failed]
///
bool get isFailed => this == NearbyDeviceStatus.failed;
///
/// Checks if status is [NearbyDeviceStatus.connecting]
///
bool get isConnecting => this == NearbyDeviceStatus.connecting;
///
/// Checks if status is [NearbyDeviceStatus.unavailable]
///
bool get isUnavailable => this == NearbyDeviceStatus.unavailable;
///
/// Get [NearbyDeviceStatus] from the Android platform code.
///
/// You can read about it on the [Android developer site](https://developer.android.com/reference/android/net/wifi/p2p/WifiP2pDevice#constants_1).
///
static NearbyDeviceStatus fromAndroidCode(num? code) {
if (code == null) {
return NearbyDeviceStatus.failed;
}
return switch (code) {
(0) => NearbyDeviceStatus.connected,
(1) => NearbyDeviceStatus.connecting,
(2) => NearbyDeviceStatus.failed,
(3) => NearbyDeviceStatus.available,
(4) => NearbyDeviceStatus.unavailable,
(_) => NearbyDeviceStatus.failed,
};
}
///
/// Get [NearbyDeviceStatus] from the IOS platform code.
///
/// You can read about it on the [IOS developer site](https://developer.apple.com/documentation/multipeerconnectivity/mcsessionstate).
///
static NearbyDeviceStatus fromIosCode(String? code) {
if (code == null) {
return NearbyDeviceStatus.failed;
}
final value = num.tryParse(code);
return switch (value) {
(0) => NearbyDeviceStatus.available,
(1) => NearbyDeviceStatus.connecting,
(2) => NearbyDeviceStatus.connected,
(_) => NearbyDeviceStatus.failed,
};
}
}
+111
View File
@@ -0,0 +1,111 @@
import 'dart:io';
import 'package:nearby_service/nearby_service.dart';
///
/// A representation of a file that can be got from the Nearby Service's
/// communication channel.
///
/// From the communication channel, you usually get
/// the [NearbyMessageFilesRequest] request first.
/// After that, you can send positive [NearbyMessageFilesResponse] and
/// get the list of [NearbyFile].
///
final class NearbyFile {
///
/// Pass [info] assigned to file to be sent.
///
const NearbyFile({
required this.info,
required this.file,
});
///
/// Quick info about the file
///
final NearbyFileInfo info;
///
/// A file that you can save in your phone if needed
///
final File file;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is NearbyFile &&
runtimeType == other.runtimeType &&
info == other.info &&
file == other.file;
@override
int get hashCode => info.hashCode ^ file.hashCode;
@override
String toString() {
return 'NearbyFile{info: $info, file: $file}';
}
}
///
/// Quick info about the file
///
class NearbyFileInfo {
///
/// Contains the file [path] to get the file from it.
///
const NearbyFileInfo({required this.path});
factory NearbyFileInfo.fromJson(Map<String, dynamic>? json) {
return NearbyFileInfo(
path: json?['path'] ?? '',
);
}
///
/// Path of the representing file
///
final String path;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is NearbyFileInfo &&
runtimeType == other.runtimeType &&
path == other.path;
@override
int get hashCode => path.hashCode;
Map<String, dynamic> toJson() {
return {
'path': path,
};
}
///
/// Quick access to the file [name]
///
String get name {
try {
return path.split('/').last;
} catch (e) {
return path;
}
}
///
/// Quick access to the file [extension]
///
String get extension {
try {
return name.split('.').last;
} catch (e) {
throw NearbyServiceException('Can\'t get extension from $name');
}
}
@override
String toString() {
return 'NearbyFileInfo{path: $path}';
}
}
+95
View File
@@ -0,0 +1,95 @@
import 'package:nearby_service/nearby_service.dart';
///
/// The message that will be sent from the current device.
///
final class OutgoingNearbyMessage<Content extends NearbyMessageContentBase>
extends NearbyMessageBase<Content> implements NearbyOutgoingInterface {
///
/// To send a message, in addition to [content], you need to pass [receiver]
/// to know to whom the message is addressed.
///
const OutgoingNearbyMessage({
required super.content,
required this.receiver,
});
///
/// Data of the user to whom the message is addressed.
///
@override
final NearbyDeviceInfo receiver;
///
/// Get [Map] from [OutgoingNearbyMessage].
///
Map<String, dynamic> toJson() {
return {
'content': content.toJson(),
'receiver': receiver.toJson(),
};
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
super == other &&
other is OutgoingNearbyMessage &&
runtimeType == other.runtimeType &&
receiver == other.receiver;
@override
int get hashCode => super.hashCode ^ receiver.hashCode;
@override
String toString() {
return 'OutgoingNearbyMessage{receiver: $receiver content:$content}';
}
}
///
/// Message received by the current device.
///
final class ReceivedNearbyMessage<Content extends NearbyMessageContentBase>
extends NearbyMessageBase<Content> implements NearbyReceivedInterface {
///
/// The received message contains a [sender] in addition to [content],
/// to know from whom the message came.
///
const ReceivedNearbyMessage({
required super.content,
required this.sender,
});
///
/// Get [ReceivedNearbyMessage] from [Map].
///
factory ReceivedNearbyMessage.fromJson(Map<String, dynamic>? json) {
return ReceivedNearbyMessage(
content: NearbyMessageContentBase.fromJson(json?['content']),
sender: NearbyDeviceInfo.fromJson(json?['sender']),
);
}
///
/// Data of the user from whom the message came.
///
@override
final NearbyDeviceInfo sender;
@override
bool operator ==(Object other) =>
identical(this, other) ||
super == other &&
other is ReceivedNearbyMessage &&
runtimeType == other.runtimeType &&
sender == other.sender;
@override
int get hashCode => super.hashCode ^ sender.hashCode;
@override
String toString() {
return 'ReceivedNearbyMessage{sender: $sender content: $content}';
}
}
+224
View File
@@ -0,0 +1,224 @@
import 'package:nearby_service/nearby_service.dart';
import 'package:nearby_service/src/utils/random.dart';
///
/// Nearby message Text content.
///
/// Contains [value] - the message to be sent or received.
///
final class NearbyMessageTextContent extends NearbyMessageContentBase {
const NearbyMessageTextContent({required this.value});
///
/// Gets [NearbyMessageTextContent] from [json]
///
factory NearbyMessageTextContent.fromJson(Map<String, dynamic>? json) {
return NearbyMessageTextContent(
value: json?['value'] ?? '',
);
}
///
/// The message to be sent or received
///
final String value;
@override
NearbyMessageContentType get type => NearbyMessageContentType.text;
@override
bool get isValid => value.isNotEmpty;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is NearbyMessageTextContent &&
runtimeType == other.runtimeType &&
value == other.value;
@override
int get hashCode => value.hashCode;
@override
String toString() {
return 'NearbyMessageTextContent{value: $value}';
}
@override
Map<String, dynamic> toJson() {
return {
'value': value,
...super.toJson(),
};
}
}
///
/// Sealed class for files content in NearbyMessage.
///
sealed class NearbyMessageFilesContent extends NearbyMessageContentBase {
///
/// 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({
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
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;
@override
String toString() {
return 'NearbyMessageFilesContent{files: $files, id: $id}';
}
}
///
/// Nearby message File Request. Used for file sending requests.
/// Does not contain files' bytes!
///
final class NearbyMessageFilesRequest extends NearbyMessageFilesContent {
const NearbyMessageFilesRequest._({
required super.id,
required super.files,
});
///
/// Basic constructor with [files] to be sent or received.
///
NearbyMessageFilesRequest({required super.files})
: super(
id: RandomUtils.instance.nextInt(1000000, 9999999).toString(),
);
///
/// Gets [NearbyMessageFilesRequest] from [json].
///
factory NearbyMessageFilesRequest.fromJson(Map<String, dynamic>? json) {
return NearbyMessageFilesRequest._(
id: json?['id'] ?? '',
files: [
...?(json?['files'] as List?)?.map(
(e) => NearbyFileInfo.fromJson(e),
),
],
);
}
@override
NearbyMessageContentType get type => NearbyMessageContentType.filesRequest;
@override
String toString() {
return 'NearbyMessageFileRequest{id: $id, files: $files}';
}
}
///
/// Nearby message File Response. Used for file sending responses.
/// Does not contain files' bytes!
///
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.
///
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,
);
}
///
/// The main response to the received [NearbyMessageFilesRequest].
///
final bool response;
@override
NearbyMessageContentType get type => NearbyMessageContentType.filesResponse;
@override
Map<String, dynamic> toJson() {
return {
'response': response,
...super.toJson(),
};
}
@override
String toString() {
return 'NearbyMessageFileResponse{response: $response, id: $id, files: $files}';
}
}
@@ -0,0 +1,34 @@
///
/// Type of the message.
///
/// If [text], it will be a text message.
/// If [filesRequest], it will be a files pack request.
/// After accepting the request,
/// user can get files bytes stream from the connected device.
///
enum NearbyMessageContentType {
text,
filesRequest,
filesResponse;
///
/// Checks if this is [NearbyMessageContentType.text]
///
bool get isText {
return this == NearbyMessageContentType.text;
}
///
/// Checks if this is [NearbyMessageContentType.filesRequest]
///
bool get isFilesRequest {
return this == NearbyMessageContentType.filesRequest;
}
///
/// Checks if this is [NearbyMessageContentType.filesResponse]
///
bool get isFilesResponse {
return this == NearbyMessageContentType.filesResponse;
}
}