diff --git a/example/lib/main.dart b/example/lib/main.dart index bcbbbb8..b042cd3 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -194,8 +194,8 @@ class AppService extends ChangeNotifier { ..communicationChannelState.addListener(notifyListeners); AppState state = AppState.idle; - List? peers; - NearbyDevice? connectedDevice; + List? peers; + NearbyDeviceBase? connectedDevice; NearbyDeviceInfo? currentDeviceInfo; NearbyConnectionAndroidInfo? connectionAndroidInfo; @@ -338,7 +338,7 @@ class AppService extends ChangeNotifier { connectionInfoSubscription = null; } - Future connect(NearbyDevice device) async { + Future connect(NearbyDeviceBase device) async { try { await _nearbyService.connect(device); } catch (e) { @@ -366,7 +366,7 @@ class AppService extends ChangeNotifier { notifyListeners(); } - Future startListeningConnectedDevice(NearbyDevice device) async { + Future startListeningConnectedDevice(NearbyDeviceBase device) async { updateState(AppState.loadingConnection); try { connectedDeviceSubscription = @@ -404,7 +404,7 @@ class AppService extends ChangeNotifier { Future startCommunicationChannel({ ValueChanged? listener, - ValueChanged>? onFilesSaved, + ValueChanged>? onFilesSaved, }) async { final messagesListener = NearbyServiceMessagesListener( onCreated: () { @@ -419,7 +419,7 @@ class AppService extends ChangeNotifier { ); final filesListener = NearbyServiceFilesListener( onData: (event) async { - final files = []; + final files = []; final directory = Platform.isAndroid ? Directory('storage/emulated/0/Download') : await getApplicationDocumentsDirectory(); @@ -431,7 +431,7 @@ class AppService extends ChangeNotifier { if (!await newFile.exists()) { await newFile.create(); } - files.add(newFile); + files.add(NearbyFileInfo(path: newFile.path)); } onFilesSaved?.call(files); }, @@ -492,7 +492,7 @@ class AppService extends ChangeNotifier { ); } - Future disconnect(NearbyDevice device) async { + Future disconnect(NearbyDeviceBase device) async { try { await _nearbyService.disconnect(device); } catch (e) { @@ -862,7 +862,7 @@ class _ConnectedBody extends StatelessWidget { onFilesRequest: (content) { ActionDialog.show( context, - title: 'Files request. Files count: ${content.files.length}', + title: 'Request to send ${content.files.length} files', subtitle: senderSubtitle, ).then((value) { if (value is bool) { @@ -883,10 +883,10 @@ class _ConnectedBody extends StatelessWidget { ); } - void _onFileSaved(BuildContext context, List files) { + void _onFileSaved(BuildContext context, List files) { AppShackBar.show( Scaffold.of(context).context, - '${files.length} files saved! \n ${files.map((e) => e.path).join('\n')}', + '${files.length} files saved! \n${files.map((e) => e.name).join('\n')}', ); } } @@ -999,7 +999,7 @@ class _ConnectedSocketBodyState extends State<_ConnectedSocketBody> { class _DevicePreview extends StatelessWidget { const _DevicePreview({required this.device, this.largeView = false}); - final NearbyDevice device; + final NearbyDeviceBase device; final bool largeView; @override diff --git a/lib/nearby_service.dart b/lib/nearby_service.dart index 0d63a99..1bb5510 100644 --- a/lib/nearby_service.dart +++ b/lib/nearby_service.dart @@ -8,9 +8,11 @@ import 'package:nearby_service/src/utils/logger.dart'; export 'package:nearby_service/src/platforms/android/android.dart'; export 'package:nearby_service/src/platforms/ios/ios.dart'; -export 'src/models/models.dart'; +export 'src/model/model.dart'; export 'src/utils/utils.dart'; export 'src/types/types.dart'; +export 'src/base/base.dart'; +export 'src/interface/interface.dart'; /// /// The main tool for working with a P2P network. @@ -126,26 +128,26 @@ abstract class NearbyService { /// /// A single retrieval of the current list of devices in a P2P network. /// - /// Returns the list of [NearbyDevice] that have been stored so far. + /// Returns the list of [NearbyDeviceBase] that have been stored so far. /// If you want to use a constantly updated list of devices, use [getPeersStream]. /// - Future> getPeers() { + Future> getPeers() { return NearbyServicePlatform.instance.getPeers(); } /// - /// Returns a constantly updating list of [NearbyDevice] that + /// Returns a constantly updating list of [NearbyDeviceBase] that /// the platform-specific service has found at each point in time. /// - Stream> getPeersStream() { + Stream> getPeersStream() { return NearbyServicePlatform.instance.getPeersStream(); } /// - /// Returns the constantly updating [NearbyDevice] you are currently connected to. + /// Returns the constantly updating [NearbyDeviceBase] you are currently connected to. /// If it returns null, then there is no connection at the moment. /// - Stream getConnectedDeviceStream(NearbyDevice device) { + Stream getConnectedDeviceStream(NearbyDeviceBase device) { return NearbyServicePlatform.instance.getConnectedDeviceStream(device); } @@ -185,7 +187,7 @@ abstract class NearbyService { /// Note that if [Platform.isIOS] == true, [NearbyIOSDevice] should be passed. /// If [Platform.isAndroid] == true, [NearbyAndroidDevice] should be passed. /// - Future connect(NearbyDevice device); + Future connect(NearbyDeviceBase device); /// /// Disconnects from passed [device] using a platform-specific service. @@ -193,7 +195,7 @@ abstract class NearbyService { /// Note that if [Platform.isIOS] == true, [NearbyIOSDevice] should be passed. /// If [Platform.isAndroid] == true, [NearbyAndroidDevice] should be passed. /// - Future disconnect(NearbyDevice device); + Future disconnect(NearbyDeviceBase device); /// /// If the device is already connected, it does not mean that you can diff --git a/lib/nearby_service_method_channel.dart b/lib/nearby_service_method_channel.dart index b904cd6..f4ee1b5 100644 --- a/lib/nearby_service_method_channel.dart +++ b/lib/nearby_service_method_channel.dart @@ -35,14 +35,14 @@ class MethodChannelNearbyService extends NearbyServicePlatform { } @override - Future> getPeers() async { + Future> getPeers() async { return NearbyDeviceMapper.instance.mapToDeviceList( await methodChannel.invokeMethod('fetchPeers'), ); } @override - Stream> getPeersStream() { + Stream> getPeersStream() { const peersChannel = EventChannel("nearby_service_peers"); return peersChannel.receiveBroadcastStream().map((e) { return NearbyDeviceMapper.instance.mapToDeviceList(e); @@ -50,7 +50,7 @@ class MethodChannelNearbyService extends NearbyServicePlatform { } @override - Stream getConnectedDeviceStream(NearbyDevice device) { + Stream getConnectedDeviceStream(NearbyDeviceBase device) { const connectedDeviceChannel = EventChannel( "nearby_service_connected_device", ); diff --git a/lib/nearby_service_platform_interface.dart b/lib/nearby_service_platform_interface.dart index 046ef66..6b577ad 100644 --- a/lib/nearby_service_platform_interface.dart +++ b/lib/nearby_service_platform_interface.dart @@ -40,20 +40,20 @@ abstract class NearbyServicePlatform extends PlatformInterface { 'openServicesSettings() has not been implemented.'); } - Future> getPeers() { + Future> getPeers() { throw UnimplementedError('getPeers() has not been implemented.'); } - Stream> getPeersStream() { + Stream> getPeersStream() { throw UnimplementedError('streamPeers() has not been implemented.'); } - Stream getConnectedDeviceStream(NearbyDevice device) { + Stream getConnectedDeviceStream(NearbyDeviceBase device) { throw UnimplementedError( 'getConnectedDeviceStream() has not been implemented.'); } - Future disconnect(NearbyDevice device) { + Future disconnect(NearbyDeviceBase device) { throw UnimplementedError('disconnect() has not been implemented.'); } } diff --git a/lib/src/base/base.dart b/lib/src/base/base.dart new file mode 100644 index 0000000..45dfcba --- /dev/null +++ b/lib/src/base/base.dart @@ -0,0 +1,3 @@ +export 'nearby_device_base.dart'; +export 'nearby_message_base.dart'; +export 'nearby_message_content_base.dart'; \ No newline at end of file diff --git a/lib/src/base/nearby_device_base.dart b/lib/src/base/nearby_device_base.dart new file mode 100644 index 0000000..2abf89c --- /dev/null +++ b/lib/src/base/nearby_device_base.dart @@ -0,0 +1,68 @@ +import 'dart:io'; + +import 'package:nearby_service/nearby_service.dart'; + +/// +/// The model of the device found in the P2P network. +/// +abstract base class NearbyDeviceBase { + /// + /// The base device contains [info] and [status]. + /// These are parameters that devices will have independent of the platform. + /// + const NearbyDeviceBase({required this.info, required this.status}); + + /// + /// The minimum information about the device required + /// to display it in the list and connect to it. + /// + final NearbyDeviceInfo info; + + /// + /// The connection status of the device. + /// + final NearbyDeviceStatus status; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is NearbyDeviceBase && + runtimeType == other.runtimeType && + info == other.info && + status == other.status; + + @override + int get hashCode => info.hashCode ^ status.hashCode; + + @override + String toString() { + return 'NearbyDevice{info: $info, status: $status}'; + } + + /// + /// If you want to get different data + /// **depending on the platform**, use [byPlatform]. + /// + /// * The [onAndroid] callback returns this instance of [NearbyDeviceBase], + /// cast as [NearbyAndroidDevice] if [Platform.isAndroid] is true. + /// + /// * The [onIOS] callback returns this instance of [NearbyDeviceBase], + /// cast as [NearbyIOSDevice] if [Platform.isIOS] is true. + /// + /// * The [onAny] callback returns this instance of [NearbyDeviceBase] with + /// no casting if both [Platform.isAndroid] and [Platform.isIOS] are false. + /// + T? byPlatform({ + T Function(NearbyDeviceBase)? onAny, + T Function(NearbyAndroidDevice)? onAndroid, + T Function(NearbyIOSDevice)? onIOS, + }) { + if (this is NearbyIOSDevice && onIOS != null) { + return onIOS(this as NearbyIOSDevice); + } else if (this is NearbyAndroidDevice && onAndroid != null) { + return onAndroid(this as NearbyAndroidDevice); + } else { + return onAny?.call(this); + } + } +} diff --git a/lib/src/base/nearby_message_base.dart b/lib/src/base/nearby_message_base.dart new file mode 100644 index 0000000..ca81464 --- /dev/null +++ b/lib/src/base/nearby_message_base.dart @@ -0,0 +1,39 @@ +import 'package:nearby_service/nearby_service.dart'; + +/// +/// Basic Message Abstraction. +/// +abstract base class NearbyMessageBase { + /// + /// The basic message contains only [content] - the content + /// to be sent or received. + /// + const NearbyMessageBase({required this.content}); + + /// + /// Model representing content to be sent or received + /// + final Content content; + + /// + /// Checks if [content] is not empty + /// + bool get isValid { + return content.isValid; + } + + @override + bool operator ==(Object other) => + identical(this, other) || + other is NearbyMessageBase && + runtimeType == other.runtimeType && + content == other.content; + + @override + int get hashCode => content.hashCode; + + @override + String toString() { + return 'NearbyMessage{content: $content}'; + } +} diff --git a/lib/src/base/nearby_message_content_base.dart b/lib/src/base/nearby_message_content_base.dart new file mode 100644 index 0000000..0f7db17 --- /dev/null +++ b/lib/src/base/nearby_message_content_base.dart @@ -0,0 +1,73 @@ +import 'package:nearby_service/nearby_service.dart'; + +/// +/// Abstraction for the message content. +/// The [type] is used to determine, what type of content is it. +/// +abstract base class NearbyMessageContentBase { + const NearbyMessageContentBase(); + + /// + /// Contains the conditional logic of creating [NearbyMessageFilesRequest], + /// [NearbyMessageFilesResponse] or [NearbyMessageTextContent] + /// by `type` field of [json]. + /// + static Content fromJson( + Map? json) { + try { + final type = NearbyMessageContentType.values.firstWhere( + (e) => e.name == json?['type'], + ); + if (type.isText) { + return NearbyMessageTextContent.fromJson(json) as Content; + } else if (type.isFilesRequest) { + return NearbyMessageFilesRequest.fromJson(json) as Content; + } else if (type.isFilesResponse) { + return NearbyMessageFilesResponse.fromJson(json) as Content; + } else { + throw NearbyServiceException.unsupportedDecoding(json); + } + } catch (e) { + throw NearbyServiceException(e); + } + } + + NearbyMessageContentType get type; + + /// + /// Check for the content if it is valid for sending or receiving. + /// + bool get isValid; + + /// + /// * The [onText] callback returns this instance of [NearbyMessageContentBase], + /// cast as [NearbyMessageTextContent] if is a text. + /// + /// * The [onFilesRequest] callback returns this instance of [NearbyMessageContentBase], + /// cast as [NearbyMessageFilesRequest] if is a files pack request. + /// + /// * The [onFilesResponse] callback returns this instance of [NearbyMessageContentBase], + /// cast as [NearbyMessageFilesResponse] if is a files pack response. + /// + T? byType({ + T Function(NearbyMessageTextContent)? onText, + T Function(NearbyMessageFilesRequest)? onFilesRequest, + T Function(NearbyMessageFilesResponse)? onFilesResponse, + }) { + if (this is NearbyMessageTextContent && onText != null) { + return onText(this as NearbyMessageTextContent); + } 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; + } + + /// + /// Gets [Map] from [NearbyMessageContentBase] + /// + Map toJson() { + return {'type': type.name}; + } +} diff --git a/lib/src/interface/interface.dart b/lib/src/interface/interface.dart new file mode 100644 index 0000000..9f05a4a --- /dev/null +++ b/lib/src/interface/interface.dart @@ -0,0 +1,3 @@ +export 'nearby_device_mapper.dart'; +export 'nearby_received_interface.dart'; +export 'nearby_outgoing_interface.dart'; \ No newline at end of file diff --git a/lib/src/interface/nearby_device_mapper.dart b/lib/src/interface/nearby_device_mapper.dart new file mode 100644 index 0000000..6571a77 --- /dev/null +++ b/lib/src/interface/nearby_device_mapper.dart @@ -0,0 +1,36 @@ +import 'dart:io'; + +import 'package:nearby_service/nearby_service.dart'; + +/// +/// Converter for devices from JSON to models. +/// +abstract interface class NearbyDeviceMapper { + /// + /// Get the mapper instance for the current platform. + /// + static NearbyDeviceMapper get instance { + if (Platform.isAndroid) { + return NearbyAndroidMapper(); + } + if (Platform.isIOS) { + return NearbyIOSMapper(); + } + + throw NearbyServiceException.unsupportedPlatform( + caller: 'NearbyDeviceMapper', + ); + } + + /// + /// Converts JSON to a list of [NearbyDeviceBase]. + /// + List mapToDeviceList(dynamic value); + + /// + /// Converts JSON to a [NearbyDeviceBase]. + /// + NearbyDeviceBase? mapToDevice(dynamic value); +} + + diff --git a/lib/src/interface/nearby_outgoing_interface.dart b/lib/src/interface/nearby_outgoing_interface.dart new file mode 100644 index 0000000..8313e17 --- /dev/null +++ b/lib/src/interface/nearby_outgoing_interface.dart @@ -0,0 +1,10 @@ +import 'package:nearby_service/nearby_service.dart'; + +abstract interface class NearbyOutgoingInterface { + NearbyOutgoingInterface({required this.receiver}); + + /// + /// Data of the user to whom the message is addressed. + /// + final NearbyDeviceInfo receiver; +} diff --git a/lib/src/interface/nearby_received_interface.dart b/lib/src/interface/nearby_received_interface.dart new file mode 100644 index 0000000..f08c269 --- /dev/null +++ b/lib/src/interface/nearby_received_interface.dart @@ -0,0 +1,10 @@ +import 'package:nearby_service/nearby_service.dart'; + +abstract interface class NearbyReceivedInterface { + NearbyReceivedInterface({required this.sender}); + + /// + /// Data of the user from whom the message came. + /// + final NearbyDeviceInfo sender; +} diff --git a/lib/src/models/communication_channel_state.dart b/lib/src/model/communication_channel_state.dart similarity index 100% rename from lib/src/models/communication_channel_state.dart rename to lib/src/model/communication_channel_state.dart diff --git a/lib/src/models/models.dart b/lib/src/model/model.dart similarity index 69% rename from lib/src/models/models.dart rename to lib/src/model/model.dart index 5324aa8..008efce 100644 --- a/lib/src/models/models.dart +++ b/lib/src/model/model.dart @@ -1,6 +1,7 @@ -export 'nearby_device.dart'; +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'; diff --git a/lib/src/model/nearby_device_info.dart b/lib/src/model/nearby_device_info.dart new file mode 100644 index 0000000..50d1469 --- /dev/null +++ b/lib/src/model/nearby_device_info.dart @@ -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? 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 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}'; + } +} diff --git a/lib/src/models/nearby_device_status.dart b/lib/src/model/nearby_device_status.dart similarity index 100% rename from lib/src/models/nearby_device_status.dart rename to lib/src/model/nearby_device_status.dart diff --git a/lib/src/models/nearby_file.dart b/lib/src/model/nearby_file.dart similarity index 98% rename from lib/src/models/nearby_file.dart rename to lib/src/model/nearby_file.dart index 3f642de..272747f 100644 --- a/lib/src/models/nearby_file.dart +++ b/lib/src/model/nearby_file.dart @@ -10,7 +10,7 @@ import 'package:nearby_service/nearby_service.dart'; /// After that, you can send positive [NearbyMessageFilesResponse] and /// get the list of [NearbyFile]. /// -class NearbyFile { +final class NearbyFile { /// /// Pass [info] assigned to file to be sent. /// diff --git a/lib/src/models/nearby_message.dart b/lib/src/model/nearby_message.dart similarity index 69% rename from lib/src/models/nearby_message.dart rename to lib/src/model/nearby_message.dart index 3df8f89..1636374 100644 --- a/lib/src/models/nearby_message.dart +++ b/lib/src/model/nearby_message.dart @@ -1,47 +1,10 @@ import 'package:nearby_service/nearby_service.dart'; -/// -/// Basic Message Abstraction. -/// -abstract class NearbyMessage { - /// - /// The basic message contains only [content] - the content - /// to be sent or received. - /// - const NearbyMessage({required this.content}); - - /// - /// Model representing content to be sent or received - /// - final NearbyMessageContent content; - - /// - /// Checks if [content] is not empty - /// - bool get isValid { - return content.isValid; - } - - @override - bool operator ==(Object other) => - identical(this, other) || - other is NearbyMessage && - runtimeType == other.runtimeType && - content == other.content; - - @override - int get hashCode => content.hashCode; - - @override - String toString() { - return 'NearbyMessage{content: $content}'; - } -} - /// /// The message that will be sent from the current device. /// -class OutgoingNearbyMessage extends NearbyMessage { +final class OutgoingNearbyMessage + extends NearbyMessageBase implements NearbyOutgoingInterface { /// /// To send a message, in addition to [content], you need to pass [receiver] /// to know to whom the message is addressed. @@ -54,6 +17,7 @@ class OutgoingNearbyMessage extends NearbyMessage { /// /// Data of the user to whom the message is addressed. /// + @override final NearbyDeviceInfo receiver; /// @@ -86,7 +50,8 @@ class OutgoingNearbyMessage extends NearbyMessage { /// /// Message received by the current device. /// -class ReceivedNearbyMessage extends NearbyMessage { +final class ReceivedNearbyMessage + extends NearbyMessageBase implements NearbyReceivedInterface { /// /// The received message contains a [sender] in addition to [content], /// to know from whom the message came. @@ -101,7 +66,7 @@ class ReceivedNearbyMessage extends NearbyMessage { /// factory ReceivedNearbyMessage.fromJson(Map? json) { return ReceivedNearbyMessage( - content: NearbyMessageContent.fromJson(json?['content']), + content: NearbyMessageContentBase.fromJson(json?['content']), sender: NearbyDeviceInfo.fromJson(json?['sender']), ); } @@ -109,6 +74,7 @@ class ReceivedNearbyMessage extends NearbyMessage { /// /// Data of the user from whom the message came. /// + @override final NearbyDeviceInfo sender; @override diff --git a/lib/src/models/nearby_message_content.dart b/lib/src/model/nearby_message_content.dart similarity index 54% rename from lib/src/models/nearby_message_content.dart rename to lib/src/model/nearby_message_content.dart index e677891..13dd73a 100644 --- a/lib/src/models/nearby_message_content.dart +++ b/lib/src/model/nearby_message_content.dart @@ -1,122 +1,13 @@ import 'package:nearby_service/nearby_service.dart'; import 'package:nearby_service/src/utils/random.dart'; -/// -/// 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 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; - } -} - -/// -/// Abstraction for the message content. -/// Contains [_type] to determine, what type of content is it. -/// -abstract class NearbyMessageContent { - const NearbyMessageContent(this._type); - - /// - /// Contains the conditional logic of creating [NearbyMessageFilesRequest], - /// [NearbyMessageFilesResponse] or [NearbyMessageTextContent] - /// by `type` field of [json]. - /// - factory NearbyMessageContent.fromJson(Map? json) { - try { - final type = NearbyMessageContentType.values.firstWhere( - (e) => e.name == json?['type'], - ); - if (type.isText) { - return NearbyMessageTextContent.fromJson(json); - } else if (type.isFilesRequest) { - return NearbyMessageFilesRequest.fromJson(json); - } else if (type.isFilesResponse) { - return NearbyMessageFilesResponse.fromJson(json); - } else { - throw NearbyServiceException.unsupportedDecoding(json); - } - } catch (e) { - throw NearbyServiceException(e); - } - } - - final NearbyMessageContentType _type; - - /// - /// Check for the content if it is valid for sending or receiving. - /// - bool get isValid; - - /// - /// * The [onText] callback returns this instance of [NearbyMessageContent], - /// cast as [NearbyMessageTextContent] if is a text. - /// - /// * The [onFilesRequest] callback returns this instance of [NearbyMessageContent], - /// cast as [NearbyMessageFilesRequest] if is a files pack request. - /// - /// * The [onFilesResponse] callback returns this instance of [NearbyMessageContent], - /// cast as [NearbyMessageFilesResponse] if is a files pack response. - /// - T? byType({ - T Function(NearbyMessageTextContent)? onText, - T Function(NearbyMessageFilesRequest)? onFilesRequest, - T Function(NearbyMessageFilesResponse)? onFilesResponse, - }) { - if (this is NearbyMessageTextContent && onText != null) { - return onText(this as NearbyMessageTextContent); - } 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; - } - - /// - /// Gets [Map] from [NearbyMessageContent] - /// - Map toJson() { - return {'type': _type.name}; - } -} - /// /// Nearby message Text content. /// /// Contains [value] - the message to be sent or received. /// -class NearbyMessageTextContent extends NearbyMessageContent { - const NearbyMessageTextContent({required this.value}) - : super( - NearbyMessageContentType.text, - ); +final class NearbyMessageTextContent extends NearbyMessageContentBase { + const NearbyMessageTextContent({required this.value}); /// /// Gets [NearbyMessageTextContent] from [json] @@ -132,6 +23,9 @@ class NearbyMessageTextContent extends NearbyMessageContent { /// final String value; + @override + NearbyMessageContentType get type => NearbyMessageContentType.text; + @override bool get isValid => value.isNotEmpty; @@ -162,16 +56,15 @@ class NearbyMessageTextContent extends NearbyMessageContent { /// /// Sealed class for files content in NearbyMessage. /// -sealed class NearbyMessageFilesContent extends NearbyMessageContent { +sealed class NearbyMessageFilesContent extends NearbyMessageContentBase { /// - /// Here [_type] = [NearbyMessageContentType.filesResponse] or - /// [_type] = [NearbyMessageContentType.filesRequest] + /// 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, { + const NearbyMessageFilesContent({ required this.id, required this.files, }); @@ -225,20 +118,17 @@ sealed class NearbyMessageFilesContent extends NearbyMessageContent { /// Nearby message File Request. Used for file sending requests. /// Does not contain files' bytes! /// -class NearbyMessageFilesRequest extends NearbyMessageFilesContent { +final class NearbyMessageFilesRequest extends NearbyMessageFilesContent { const NearbyMessageFilesRequest._({ required super.id, required super.files, - }) : super( - NearbyMessageContentType.filesRequest, - ); + }); /// /// Basic constructor with [files] to be sent or received. /// NearbyMessageFilesRequest({required super.files}) : super( - NearbyMessageContentType.filesRequest, id: RandomUtils.instance.nextInt(1000000, 9999999).toString(), ); @@ -256,6 +146,9 @@ class NearbyMessageFilesRequest extends NearbyMessageFilesContent { ); } + @override + NearbyMessageContentType get type => NearbyMessageContentType.filesRequest; + @override String toString() { return 'NearbyMessageFileRequest{id: $id, files: $files}'; @@ -266,7 +159,7 @@ class NearbyMessageFilesRequest extends NearbyMessageFilesContent { /// Nearby message File Response. Used for file sending responses. /// Does not contain files' bytes! /// -class NearbyMessageFilesResponse extends NearbyMessageFilesContent { +final class NearbyMessageFilesResponse extends NearbyMessageFilesContent { /// /// Used to send a response to a previously received request. /// Provide [id] and [files] from [NearbyMessageFilesRequest] or @@ -277,9 +170,7 @@ class NearbyMessageFilesResponse extends NearbyMessageFilesContent { required super.id, required super.files, required this.response, - }) : super( - NearbyMessageContentType.filesResponse, - ); + }); /// /// Factory to quickly create a response to [NearbyMessageFilesRequest]. @@ -315,6 +206,9 @@ class NearbyMessageFilesResponse extends NearbyMessageFilesContent { /// final bool response; + @override + NearbyMessageContentType get type => NearbyMessageContentType.filesResponse; + @override Map toJson() { return { diff --git a/lib/src/model/nearby_message_content_type.dart b/lib/src/model/nearby_message_content_type.dart new file mode 100644 index 0000000..143f084 --- /dev/null +++ b/lib/src/model/nearby_message_content_type.dart @@ -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; + } +} diff --git a/lib/src/models/nearby_device.dart b/lib/src/models/nearby_device.dart deleted file mode 100644 index afeabd5..0000000 --- a/lib/src/models/nearby_device.dart +++ /dev/null @@ -1,166 +0,0 @@ -import 'dart:io'; - -import 'package:nearby_service/nearby_service.dart'; -import 'package:nearby_service/src/utils/unknown.dart'; - -/// -/// The model of the device found in the P2P network. -/// -abstract class NearbyDevice { - /// - /// The base device contains [info] and [status]. - /// These are parameters that devices will have independent of the platform. - /// - const NearbyDevice({required this.info, required this.status}); - - /// - /// The minimum information about the device required - /// to display it in the list and connect to it. - /// - final NearbyDeviceInfo info; - - /// - /// The connection status of the device. - /// - final NearbyDeviceStatus status; - - @override - bool operator ==(Object other) => - identical(this, other) || - other is NearbyDevice && - runtimeType == other.runtimeType && - info == other.info && - status == other.status; - - @override - int get hashCode => info.hashCode ^ status.hashCode; - - @override - String toString() { - return 'NearbyDevice{info: $info, status: $status}'; - } - - /// - /// If you want to get different data - /// **depending on the platform**, use [byPlatform]. - /// - /// * The [onAndroid] callback returns this instance of [NearbyDevice], - /// cast as [NearbyAndroidDevice] if [Platform.isAndroid] is true. - /// - /// * The [onIOS] callback returns this instance of [NearbyDevice], - /// cast as [NearbyIOSDevice] if [Platform.isIOS] is true. - /// - /// * The [onAny] callback returns this instance of [NearbyDevice] with - /// no casting if both [Platform.isAndroid] and [Platform.isIOS] are false. - /// - T? byPlatform({ - T Function(NearbyDevice)? onAny, - T Function(NearbyAndroidDevice)? onAndroid, - T Function(NearbyIOSDevice)? onIOS, - }) { - if (this is NearbyIOSDevice && onIOS != null) { - return onIOS(this as NearbyIOSDevice); - } else if (this is NearbyAndroidDevice && onAndroid != null) { - return onAndroid(this as NearbyAndroidDevice); - } else { - return onAny?.call(this); - } - } -} - -/// -/// Converter for devices from JSON to models. -/// -abstract interface class NearbyDeviceMapper { - /// - /// Get the mapper instance for the current platform. - /// - static NearbyDeviceMapper get instance { - if (Platform.isAndroid) { - return NearbyAndroidMapper(); - } - if (Platform.isIOS) { - return NearbyIOSMapper(); - } - - throw NearbyServiceException.unsupportedPlatform( - caller: 'NearbyDeviceMapper', - ); - } - - /// - /// Converts JSON to a list of [NearbyDevice]. - /// - List mapToDeviceList(dynamic value); - - /// - /// Converts JSON to a [NearbyDevice]. - /// - NearbyDevice? mapToDevice(dynamic value); -} - -/// -/// Minimal information about the device. -/// -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? 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 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}'; - } -} diff --git a/lib/src/platforms/android/android.dart b/lib/src/platforms/android/android.dart index 11a65e6..73a754f 100644 --- a/lib/src/platforms/android/android.dart +++ b/lib/src/platforms/android/android.dart @@ -1,4 +1,4 @@ export 'nearby_android_service.dart'; export 'nearby_service_android_interface.dart'; -export 'models/nearby_connection_info.dart'; -export 'models/nearby_device.dart'; +export 'model/nearby_connection_info.dart'; +export 'model/nearby_device.dart'; diff --git a/lib/src/platforms/android/models/nearby_connection_info.dart b/lib/src/platforms/android/model/nearby_connection_info.dart similarity index 100% rename from lib/src/platforms/android/models/nearby_connection_info.dart rename to lib/src/platforms/android/model/nearby_connection_info.dart diff --git a/lib/src/platforms/android/models/nearby_device.dart b/lib/src/platforms/android/model/nearby_device.dart similarity index 97% rename from lib/src/platforms/android/models/nearby_device.dart rename to lib/src/platforms/android/model/nearby_device.dart index f11ba1a..666e6a2 100644 --- a/lib/src/platforms/android/models/nearby_device.dart +++ b/lib/src/platforms/android/model/nearby_device.dart @@ -5,7 +5,7 @@ import 'package:nearby_service/src/utils/unknown.dart'; /// /// A device on a P2P network obtained from the Android platform. /// -class NearbyAndroidDevice extends NearbyDevice { +final class NearbyAndroidDevice extends NearbyDeviceBase { /// /// The class representing the Android class /// [WifiP2pDevice](https://developer.android.com/reference/android/net/wifi/p2p/WifiP2pDevice). @@ -139,7 +139,7 @@ class NearbyAndroidDevice extends NearbyDevice { /// class NearbyAndroidMapper implements NearbyDeviceMapper { @override - List mapToDeviceList(dynamic value) { + List mapToDeviceList(dynamic value) { final decoded = JSONDecoder.decodeList(value); return [ ...?decoded?.map( @@ -149,7 +149,7 @@ class NearbyAndroidMapper implements NearbyDeviceMapper { } @override - NearbyDevice? mapToDevice(dynamic value) { + NearbyDeviceBase? mapToDevice(dynamic value) { final decoded = JSONDecoder.decodeMap(value); if (decoded == null) return null; diff --git a/lib/src/platforms/android/nearby_android_service.dart b/lib/src/platforms/android/nearby_android_service.dart index fa40b2c..65c10ba 100644 --- a/lib/src/platforms/android/nearby_android_service.dart +++ b/lib/src/platforms/android/nearby_android_service.dart @@ -54,7 +54,7 @@ class NearbyAndroidService extends NearbyService { /// Note! Requires [NearbyAndroidDevice] to be passed. /// @override - Future connect(NearbyDevice device) { + Future connect(NearbyDeviceBase device) { _requireAndroidDevice(device); return NearbyServiceAndroidPlatform.instance.connect(device.info.id); } @@ -65,7 +65,7 @@ class NearbyAndroidService extends NearbyService { /// Note! Requires [NearbyAndroidDevice] to be passed. /// @override - Future disconnect(NearbyDevice device) { + Future disconnect(NearbyDeviceBase device) { _requireAndroidDevice(device); return NearbyServiceAndroidPlatform.instance.disconnect(device.info.id); } @@ -134,7 +134,7 @@ class NearbyAndroidService extends NearbyService { return NearbyServiceAndroidPlatform.instance.getConnectionInfoStream(); } - void _requireAndroidDevice(NearbyDevice device) { + void _requireAndroidDevice(NearbyDeviceBase device) { assert( device is NearbyAndroidDevice, 'The Nearby Android Service can only work with the NearbyAndroidDevice and not with ${device.runtimeType}', diff --git a/lib/src/platforms/android/nearby_service_android_interface.dart b/lib/src/platforms/android/nearby_service_android_interface.dart index 949d981..204302b 100644 --- a/lib/src/platforms/android/nearby_service_android_interface.dart +++ b/lib/src/platforms/android/nearby_service_android_interface.dart @@ -1,4 +1,4 @@ -import 'package:nearby_service/src/platforms/android/models/nearby_connection_info.dart'; +import 'package:nearby_service/nearby_service.dart'; import 'package:nearby_service/src/platforms/android/nearby_service_android_method_channel.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; diff --git a/lib/src/platforms/ios/ios.dart b/lib/src/platforms/ios/ios.dart index 4f4a012..0b04118 100644 --- a/lib/src/platforms/ios/ios.dart +++ b/lib/src/platforms/ios/ios.dart @@ -1,3 +1,3 @@ -export 'models/nearby_device.dart'; +export 'model/nearby_device.dart'; export 'nearby_ios_service.dart'; export 'nearby_service_ios_interface.dart'; diff --git a/lib/src/platforms/ios/models/nearby_device.dart b/lib/src/platforms/ios/model/nearby_device.dart similarity index 93% rename from lib/src/platforms/ios/models/nearby_device.dart rename to lib/src/platforms/ios/model/nearby_device.dart index 0b46db6..d9cd344 100644 --- a/lib/src/platforms/ios/models/nearby_device.dart +++ b/lib/src/platforms/ios/model/nearby_device.dart @@ -4,7 +4,7 @@ import 'package:nearby_service/src/utils/json_decoder.dart'; /// /// A device on a P2P network obtained from the IOS platform. /// -class NearbyIOSDevice extends NearbyDevice { +final class NearbyIOSDevice extends NearbyDeviceBase { /// /// A class representing an IOS device on a P2P network. /// @@ -74,7 +74,7 @@ class NearbyIOSDevice extends NearbyDevice { /// class NearbyIOSMapper implements NearbyDeviceMapper { @override - List mapToDeviceList(dynamic value) { + List mapToDeviceList(dynamic value) { final decoded = JSONDecoder.decodeList(value); return [ ...?decoded?.map( @@ -84,7 +84,7 @@ class NearbyIOSMapper implements NearbyDeviceMapper { } @override - NearbyDevice? mapToDevice(dynamic value) { + NearbyDeviceBase? mapToDevice(dynamic value) { final decoded = JSONDecoder.decodeMap(value); if (decoded == null) return null; diff --git a/lib/src/platforms/ios/nearby_ios_service.dart b/lib/src/platforms/ios/nearby_ios_service.dart index 717fe2b..e7b6640 100644 --- a/lib/src/platforms/ios/nearby_ios_service.dart +++ b/lib/src/platforms/ios/nearby_ios_service.dart @@ -118,7 +118,7 @@ class NearbyIOSService extends NearbyService { /// Note! Requires [NearbyIOSDevice] to be passed. /// @override - Future connect(NearbyDevice device) async { + Future connect(NearbyDeviceBase device) async { _requireIOSDevice(device); final result = _isBrowser.value ? await NearbyServiceIOSPlatform.instance.invite(device.info.id) @@ -140,7 +140,7 @@ class NearbyIOSService extends NearbyService { /// Note! Requires [NearbyIOSDevice] to be passed. /// @override - Future disconnect(NearbyDevice device) async { + Future disconnect(NearbyDeviceBase device) async { _requireIOSDevice(device); final result = await NearbyServiceIOSPlatform.instance.disconnect( device.info.id, @@ -271,7 +271,7 @@ class NearbyIOSService extends NearbyService { } } - void _requireIOSDevice(NearbyDevice device) { + void _requireIOSDevice(NearbyDeviceBase device) { assert( device is NearbyIOSDevice, 'The Nearby IOS Service can only work with the NearbyIOSDevice and not with ${device.runtimeType}', diff --git a/lib/src/utils/exception.dart b/lib/src/utils/exception.dart index c6fa622..9e693fb 100644 --- a/lib/src/utils/exception.dart +++ b/lib/src/utils/exception.dart @@ -31,7 +31,7 @@ class NearbyServiceException implements Exception { ); } - factory NearbyServiceException.invalidMessage(NearbyMessageContent content) { + factory NearbyServiceException.invalidMessage(NearbyMessageContentBase content) { return NearbyServiceException( 'The message="$content" is not valid', ); diff --git a/test/nearby_service_test.dart b/test/nearby_service_test.dart index c089ba4..75b6883 100644 --- a/test/nearby_service_test.dart +++ b/test/nearby_service_test.dart @@ -8,19 +8,19 @@ class MockNearbyServicePlatform with MockPlatformInterfaceMixin implements NearbyServicePlatform { @override - Stream getConnectedDeviceStream(device) { + Stream getConnectedDeviceStream(device) { // TODO: implement getConnectedDeviceStream throw UnimplementedError(); } @override - Future> getPeers() { + Future> getPeers() { // TODO: implement getPeers throw UnimplementedError(); } @override - Stream> getPeersStream() { + Stream> getPeersStream() { // TODO: implement getPeersStream throw UnimplementedError(); } @@ -44,7 +44,7 @@ class MockNearbyServicePlatform } @override - Future disconnect(NearbyDevice device) { + Future disconnect(NearbyDeviceBase device) { // TODO: implement disconnect throw UnimplementedError(); }