BREAKING CHANGE: Update API for connection, communication channel state and is browser value (#14)

* refactor(lib, example, example_full): use streams for communication channel and is ios browser

* feat: add abstract toJson() to message

* refactor: add deprecations for nearby_service

* feat(example): update main.dart

* feat(nearby_service): add comments

* feat(nearby_service_platform_interface): add deprecation to getConnectedDeviceStream

* feat(nearby_service/message): add concrete implementation for toJson()

* feat(nearby_service/android): add deprecations

* fix(nearby_service/android): reset check for android device

* fix(nearby_service/ios): change ios service to deprecations variant

* chore(nearby_service): edit deprecation messages

* chore: update gitignore

* doc: add CONTRIBUTING file

* doc: update CONTRIBUTING file

* chore: update CONTRIBUTING.md

* chore: update CONTRIBUTING.md

* chore: version 0.1.0

* fix(example_full): move startListeningCommunicationChannelState() upper
This commit is contained in:
Kseniia Nikitina
2024-08-18 15:42:53 +02:00
committed by GitHub
parent 1818166d06
commit 7049c39cf0
15 changed files with 388 additions and 79 deletions
+2 -1
View File
@@ -34,4 +34,5 @@ build/
**/Podfile.lock **/Podfile.lock
# FVM Version Cache # FVM Version Cache
.fvm/ .fvm/
playground/*
+14
View File
@@ -1,3 +1,17 @@
## 0.1.0
**!! BREAKING CHANGES !!**
- Method `connect()` is **deprecated**. Added `connectById()` method instead
- Method `disconnect()` is **deprecated**. Added `disconnectById()` method instead
- Getter `communicationChannelState` is **deprecated**. Added `getCommunicationChannelStateStream()` method
and `communicationChannelStateValue` getter instead
- Getter `isBrowser` is **deprecated**. Added `getIsBrowserStream()` method and `isBrowserValue` getter instead
- Added `toJson()` method to `NearbyMessage` class and its subclasses
More information about the deprecated API here: https://github.com/ksenia312/nearby_service/pull/14.
In the next versions, the deprecated API will be removed.
## 0.0.9 ## 0.0.9
- Add initialization checks for Android and IOS - Add initialization checks for Android and IOS
+61
View File
@@ -0,0 +1,61 @@
[![Xenikii Website](https://img.shields.io/badge/-xenikii.one-313866?style=for-the-badge&logoColor=white)](https://xenikii.one)
[![LICENSE BSD](https://img.shields.io/badge/License-BSD-504099?style=for-the-badge)](https://github.com/ksenia312/nearby_service/blob/main/LICENSE)
[![Pub package](https://img.shields.io/pub/v/nearby_service.svg?style=for-the-badge&color=974EC3)](https://pub.dev/packages/nearby_service)
[![Pub Likes](https://img.shields.io/pub/likes/nearby_service?style=for-the-badge&color=FE7BE5)](https://pub.dev/packages/nearby_service)
## Contributing to Nearby Service
Thank you for considering contributing to the Nearby Service package! Please follow the guidelines below based on your
use case.
#### Reporting a bug
If you have found a bug, please follow these steps:
1. **Search for existing issues**: Before opening a new issue, please check
the [Issues](https://github.com/ksenia312/nearby_service/issues) to see if the bug has already been reported.
2. **Open a new Bug Report issue**: If the bug has not been reported yet, open a new
issue [here](https://github.com/ksenia312/nearby_service/issues/new/choose) and use template `Bug Report` to indicate
that this is a bug report.
3. **Provide details**: Include a clear and concise description of the bug, steps to reproduce it, and any relevant logs
or screenshots.
#### Suggesting an improvement or feature
If you have an idea for an improvement or a new feature, follow these steps:
1. **Search for existing issues**: Check the [Issues](https://github.com/ksenia312/nearby_service/issues) to see if your
suggestion has already been made.
2. **Open a new Feature Request issue**: If not, open a new
issue [here](https://github.com/ksenia312/nearby_service/issues/new/choose) and use
template `Feature Request` to indicate that this is a suggestion for improvement.
3. **Describe your suggestion**: Provide a detailed description of the improvement or feature, including potential use
cases and any other relevant information.
#### Submitting a bug fix or improvement
If you have created a fix for a bug or an improvement, please follow these steps:
1. **Search for existing issues**: Ensure the issue has not already been fixed or the improvement has not been
implemented by checking the [Issues](https://github.com/ksenia312/nearby_service/issues).
2. **Fork the repository**: Fork the [Nearby Service repository](https://github.com/ksenia312/nearby_service) to your
own GitHub account.
3. **Create a new branch**: In your forked repository, create a new branch for your fix or improvement.
4. **Implement your fix or improvement**: Make your changes in the new branch.
5. **Open a Pull Request**: Once your changes are complete, open a Pull Request (PR) from your forked repositorys
branch to the `main` branch of the original repository.
6. **Fill in the PR template**: In your PR, fill in the PR template with the appropriate information.
#### Asking a question about nearby_service or source code
If you have a question about how to use Nearby Service or about the source code, please follow these steps:
1. **Search for existing questions**: Check the [Issues](https://github.com/ksenia312/nearby_service/issues) to see if
your question has already been answered.
2. **Open a new Question issue**: If your question has not been addressed, open a new
issue [here](https://github.com/ksenia312/nearby_service/issues/new/choose) and use one of `Question` templates to
indicate that this is a question.
3. **Describe your question**: Clearly state your question and provide any necessary context.
---
Your effort is appreciated 💗
+14 -10
View File
@@ -60,7 +60,7 @@ class _AppBodyState extends State<AppBody> {
late final _nearbyService = NearbyService.getInstance( late final _nearbyService = NearbyService.getInstance(
/// Define log level here /// Define log level here
logLevel: NearbyServiceLogLevel.debug, logLevel: NearbyServiceLogLevel.debug,
)..communicationChannelState.addListener(() => setState(() {})); );
AppState _state = AppState.idle; AppState _state = AppState.idle;
@@ -70,9 +70,11 @@ class _AppBodyState extends State<AppBody> {
/// List of discovered devices /// List of discovered devices
List<NearbyDevice> _peers = []; List<NearbyDevice> _peers = [];
StreamSubscription? _peersSubscription; StreamSubscription? _peersSubscription;
CommunicationChannelState _communicationChannelState =
CommunicationChannelState.notConnected;
/// Temporary solution to check the connection, /// Temporary solution to check the connection,
/// use [NearbyService.getConnectedDeviceStream] for this purpose /// use [NearbyService.getConnectedDeviceStreamById] for this purpose
/// in your application /// in your application
Timer? _connectionCheckTimer; Timer? _connectionCheckTimer;
NearbyDevice? _connectedDevice; NearbyDevice? _connectedDevice;
@@ -158,10 +160,6 @@ class _AppBodyState extends State<AppBody> {
return Container(); return Container();
} }
CommunicationChannelState get _communicationChannelState {
return _nearbyService.communicationChannelState.value;
}
Future<void> _initialize() async { Future<void> _initialize() async {
await _nearbyService.initialize(); await _nearbyService.initialize();
} }
@@ -204,7 +202,7 @@ class _AppBodyState extends State<AppBody> {
Future<void> _connect(NearbyDevice device) async { Future<void> _connect(NearbyDevice device) async {
// Be careful with already connected devices, // Be careful with already connected devices,
// double connection may be unnecessary // double connection may be unnecessary
final result = await _nearbyService.connect(device); final result = await _nearbyService.connectById(device.info.id);
if (result || device.status.isConnected) { if (result || device.status.isConnected) {
final channelStarting = _tryCommunicate(device); final channelStarting = _tryCommunicate(device);
if (!channelStarting) { if (!channelStarting) {
@@ -240,8 +238,14 @@ class _AppBodyState extends State<AppBody> {
} }
void _startCommunicationChannel(NearbyDevice device) { void _startCommunicationChannel(NearbyDevice device) {
if (!_communicationChannelState.isNotConnected) return; if (_communicationChannelState != CommunicationChannelState.notConnected) {
// channel is loading or already connected
return;
}
// start listening communication channel state
_nearbyService.getCommunicationChannelStateStream().listen((event) {
_communicationChannelState = event;
});
_nearbyService.startCommunicationChannel( _nearbyService.startCommunicationChannel(
NearbyCommunicationChannelData( NearbyCommunicationChannelData(
device.info.id, device.info.id,
@@ -275,7 +279,7 @@ class _AppBodyState extends State<AppBody> {
Future<void> _disconnect() async { Future<void> _disconnect() async {
try { try {
await _nearbyService.disconnect(_connectedDevice!); await _nearbyService.disconnectById(_connectedDevice!.info.id);
} finally { } finally {
await _nearbyService.endCommunicationChannel(); await _nearbyService.endCommunicationChannel();
await _nearbyService.stopDiscovery(); await _nearbyService.stopDiscovery();
+29 -9
View File
@@ -8,14 +8,16 @@ import 'package:nearby_service_example_full/utils/files_saver.dart';
import 'app_state.dart'; import 'app_state.dart';
class AppService extends ChangeNotifier { class AppService extends ChangeNotifier {
late final _nearbyService = NearbyService.getInstance() late final _nearbyService = NearbyService.getInstance();
..communicationChannelState.addListener(notifyListeners);
AppState state = AppState.idle; AppState state = AppState.idle;
List<NearbyDevice>? peers; List<NearbyDevice>? peers;
NearbyDevice? connectedDevice; NearbyDevice? connectedDevice;
NearbyDeviceInfo? currentDeviceInfo; NearbyDeviceInfo? currentDeviceInfo;
NearbyConnectionAndroidInfo? _connectionAndroidInfo; NearbyConnectionAndroidInfo? _connectionAndroidInfo;
CommunicationChannelState _communicationChannelState =
CommunicationChannelState.notConnected;
bool _isIOSBrowser = true;
String platformVersion = 'Unknown'; String platformVersion = 'Unknown';
String platformModel = 'Unknown'; String platformModel = 'Unknown';
@@ -47,6 +49,10 @@ class AppService extends ChangeNotifier {
await _nearbyService.initialize( await _nearbyService.initialize(
data: NearbyInitializeData(iosDeviceName: iosDeviceName), data: NearbyInitializeData(iosDeviceName: iosDeviceName),
); );
_nearbyService.ios?.getIsBrowserStream().listen((event) {
_isIOSBrowser = event;
});
startListeningCommunicationChannelState();
updateState( updateState(
Platform.isAndroid ? AppState.permissions : AppState.selectClientType, Platform.isAndroid ? AppState.permissions : AppState.selectClientType,
); );
@@ -152,7 +158,7 @@ class AppService extends ChangeNotifier {
Future<void> connect(NearbyDevice device) async { Future<void> connect(NearbyDevice device) async {
try { try {
await _nearbyService.connect(device); await _nearbyService.connectById(device.info.id);
} on NearbyServiceBusyException catch (_) { } on NearbyServiceBusyException catch (_) {
_logBusyException(); _logBusyException();
} catch (e, s) { } catch (e, s) {
@@ -163,7 +169,7 @@ class AppService extends ChangeNotifier {
Future<void> disconnect([NearbyDevice? device]) async { Future<void> disconnect([NearbyDevice? device]) async {
try { try {
await _nearbyService.disconnect(device); await _nearbyService.disconnectById(device?.info.id);
} on NearbyServiceBusyException catch (_) { } on NearbyServiceBusyException catch (_) {
_logBusyException(); _logBusyException();
} catch (e, s) { } catch (e, s) {
@@ -204,12 +210,11 @@ class AppService extends ChangeNotifier {
} }
extension GettersExtension on AppService { extension GettersExtension on AppService {
CommunicationChannelState get communicationChannelState { CommunicationChannelState get communicationChannelState =>
return _nearbyService.communicationChannelState.value; _communicationChannelState;
}
bool get isIOSBrowser { bool get isIOSBrowser {
return _nearbyService.ios?.isBrowser.value ?? false; return _isIOSBrowser;
} }
bool? get isAndroidGroupOwner { bool? get isAndroidGroupOwner {
@@ -233,6 +238,21 @@ extension ConnectionInfoExtension on AppService {
_notify(); _notify();
} }
void startListeningCommunicationChannelState() {
try {
_connectionInfoSubscription =
_nearbyService.getCommunicationChannelStateStream().listen(
(event) async {
_communicationChannelState = event;
_notify();
},
);
} catch (e, s) {
_log(e, s);
}
_notify();
}
Future<void> stopListeningConnectionInfo() async { Future<void> stopListeningConnectionInfo() async {
await _connectionInfoSubscription?.cancel(); await _connectionInfoSubscription?.cancel();
_connectionInfoSubscription = null; _connectionInfoSubscription = null;
@@ -266,7 +286,7 @@ extension ConnectedDeviceExtension on AppService {
updateState(AppState.loadingConnection); updateState(AppState.loadingConnection);
try { try {
_connectedDeviceSubscription = _connectedDeviceSubscription =
_nearbyService.getConnectedDeviceStream(device).listen( _nearbyService.getConnectedDeviceStreamById(device.info.id).listen(
(event) async { (event) async {
final wasConnected = connectedDevice?.status.isConnected ?? false; final wasConnected = connectedDevice?.status.isConnected ?? false;
final nowConnected = event?.status.isConnected ?? false; final nowConnected = event?.status.isConnected ?? false;
+83 -9
View File
@@ -74,8 +74,26 @@ abstract class NearbyService {
/// For **IOS** this is the state of the message stream subscription. /// For **IOS** this is the state of the message stream subscription.
/// which is generated for the device with the current connected device ID. /// which is generated for the device with the current connected device ID.
/// ///
@Deprecated(
'Use getCommunicationChannelStateStream or communicationChannelStateValue instead',
)
ValueListenable<CommunicationChannelState> get communicationChannelState; ValueListenable<CommunicationChannelState> get communicationChannelState;
///
/// **A value to determine the communication channel's status.**
///
/// For **Android** this is the socket connection state.
/// The server can wait for the client to connect,
/// and the client can be waiting for the server to be created.
/// Also, both can be in connected and unconnected states.
///
/// For **IOS** this is the state of the message stream subscription.
/// which is generated for the device with the current connected device ID.
///
/// **Can be used to retrieve the current state of the communication channel without listening to the stream via** [getCommunicationChannelStateStream].
///
CommunicationChannelState get communicationChannelStateValue;
/// ///
/// Gets version of current platform. /// Gets version of current platform.
/// ///
@@ -147,10 +165,20 @@ abstract class NearbyService {
/// Returns the constantly updating [NearbyDevice] you are currently connected to. /// Returns the constantly updating [NearbyDevice] you are currently connected to.
/// If it returns null, then there is no connection at the moment. /// If it returns null, then there is no connection at the moment.
/// ///
@Deprecated('Use getConnectedDeviceStreamById instead')
Stream<NearbyDevice?> getConnectedDeviceStream(NearbyDevice device) { Stream<NearbyDevice?> getConnectedDeviceStream(NearbyDevice device) {
return NearbyServicePlatform.instance.getConnectedDeviceStream(device); return NearbyServicePlatform.instance.getConnectedDeviceStream(device);
} }
///
/// Returns the constantly updating [NearbyDevice] you are currently connected to.
/// If it returns null, then there is no connection at the moment.
///
Stream<NearbyDevice?> getConnectedDeviceStreamById(String deviceId) {
return NearbyServicePlatform.instance
.getConnectedDeviceStreamById(deviceId);
}
/// ///
/// Initialization of a platform-specific service. /// Initialization of a platform-specific service.
/// ///
@@ -166,7 +194,7 @@ abstract class NearbyService {
/// Starts searching for devices using a platform-specific service. /// Starts searching for devices using a platform-specific service.
/// ///
/// Note that the [NearbyIOSService] implementation starts **browsing** or /// Note that the [NearbyIOSService] implementation starts **browsing** or
/// **advertising** depending on the [NearbyIOSService.isBrowser]. /// **advertising** depending on the [NearbyIOSService.isBrowserValue].
/// ///
/// On Android can throw mapped from native platform exceptions: /// On Android can throw mapped from native platform exceptions:
/// 1. [NearbyServiceBusyException] /// 1. [NearbyServiceBusyException]
@@ -181,7 +209,7 @@ abstract class NearbyService {
/// Stops searching for devices using a platform-specific service. /// Stops searching for devices using a platform-specific service.
/// ///
/// Note that the [NearbyIOSService] implementation stops **browsing** or /// Note that the [NearbyIOSService] implementation stops **browsing** or
/// **advertising** depending on the [NearbyIOSService.isBrowser]. /// **advertising** depending on the [NearbyIOSService.isBrowserValue].
/// ///
/// On Android can throw mapped from native platform exceptions: /// On Android can throw mapped from native platform exceptions:
/// 1. [NearbyServiceBusyException] /// 1. [NearbyServiceBusyException]
@@ -208,16 +236,18 @@ abstract class NearbyService {
/// 4. [NearbyServiceGenericErrorException] /// 4. [NearbyServiceGenericErrorException]
/// 5. [NearbyServiceUnknownException] /// 5. [NearbyServiceUnknownException]
/// ///
@Deprecated('Use connectById instead')
Future<bool> connect(NearbyDevice device); Future<bool> connect(NearbyDevice device);
/// ///
/// Disconnects from passed [device] using a platform-specific service. /// Connects to passed [deviceId] using a platform-specific service.
///
/// Note that the [NearbyIOSService] implementation **invites** or
/// **accepts invite** depending on the [NearbyIOSService.isBrowserValue].
/// ///
/// Note that if [Platform.isIOS] == true, [NearbyIOSDevice] should be passed. /// Note that if [Platform.isIOS] == true, [NearbyIOSDevice] should be passed.
/// If [Platform.isAndroid] == true, [NearbyAndroidDevice] should be passed. /// If [Platform.isAndroid] == true, [NearbyAndroidDevice] should be passed.
/// ///
/// **For IOS [device] is required!!!**
///
/// On Android can throw mapped from native platform exceptions: /// On Android can throw mapped from native platform exceptions:
/// 1. [NearbyServiceBusyException] /// 1. [NearbyServiceBusyException]
/// 2. [NearbyServiceP2PUnsupportedException] /// 2. [NearbyServiceP2PUnsupportedException]
@@ -225,8 +255,41 @@ abstract class NearbyService {
/// 4. [NearbyServiceGenericErrorException] /// 4. [NearbyServiceGenericErrorException]
/// 5. [NearbyServiceUnknownException] /// 5. [NearbyServiceUnknownException]
/// ///
Future<bool> connectById(String deviceId);
///
/// Disconnects from passed [device] using a platform-specific service.
///
/// Note that if [Platform.isIOS] == true, [NearbyIOSDevice] should be passed.
/// If [Platform.isAndroid] == true, [NearbyAndroidDevice] should be passed.
///
/// On Android can throw mapped from native platform exceptions:
/// 1. [NearbyServiceBusyException]
/// 2. [NearbyServiceP2PUnsupportedException]
/// 3. [NearbyServiceNoServiceRequestsException]
/// 4. [NearbyServiceGenericErrorException]
/// 5. [NearbyServiceUnknownException]
///
/// **For IOS [device] is required!!!**
@Deprecated('Use disconnectById instead')
Future<bool> disconnect([NearbyDevice? device]); Future<bool> disconnect([NearbyDevice? device]);
///
/// Disconnects from passed [deviceId] using a platform-specific service.
///
/// Note that if [Platform.isIOS] == true, [NearbyIOSDevice] should be passed.
/// If [Platform.isAndroid] == true, [NearbyAndroidDevice] should be passed.
///
/// On Android can throw mapped from native platform exceptions:
/// 1. [NearbyServiceBusyException]
/// 2. [NearbyServiceP2PUnsupportedException]
/// 3. [NearbyServiceNoServiceRequestsException]
/// 4. [NearbyServiceGenericErrorException]
/// 5. [NearbyServiceUnknownException]
///
/// **For IOS [deviceId] is required!!!**
Future<bool> disconnectById([String? deviceId]);
/// ///
/// If the device is already connected, it does not mean that you can /// If the device is already connected, it does not mean that you can
/// send and receive data. /// send and receive data.
@@ -235,11 +298,9 @@ abstract class NearbyService {
/// You need to call [startCommunicationChannel] before using [send]. /// You need to call [startCommunicationChannel] before using [send].
/// A communication channel can only be created if you are connected to some device. /// A communication channel can only be created if you are connected to some device.
/// ///
/// You can monitor changes in communication channel state using the [communicationChannelState] getter. /// You can monitor changes in communication channel state using the [getCommunicationChannelStateStream] method.
/// ///
FutureOr<bool> startCommunicationChannel( FutureOr<bool> startCommunicationChannel(NearbyCommunicationChannelData data);
NearbyCommunicationChannelData data,
);
/// ///
/// If you called [startCommunicationChannel], remember that you have /// If you called [startCommunicationChannel], remember that you have
@@ -250,6 +311,19 @@ abstract class NearbyService {
/// ///
FutureOr<bool> endCommunicationChannel(); FutureOr<bool> endCommunicationChannel();
///
/// **A stream with values of [CommunicationChannelState] to determine the communication channel's status.**
///
/// For **Android** this is the socket connection state.
/// The server can wait for the client to connect,
/// and the client can be waiting for the server to be created.
/// Also, both can be in connected and unconnected states.
///
/// For **IOS** this is the state of the message stream subscription.
/// which is generated for the device with the current connected device ID.
///
Stream<CommunicationChannelState> getCommunicationChannelStateStream();
/// ///
/// Method to send data to the created communication channel. /// Method to send data to the created communication channel.
/// ///
+7 -3
View File
@@ -54,13 +54,17 @@ class MethodChannelNearbyService extends NearbyServicePlatform {
} }
@override @override
@Deprecated('Use getConnectedDeviceStreamById instead')
Stream<NearbyDevice?> getConnectedDeviceStream(NearbyDevice device) { Stream<NearbyDevice?> getConnectedDeviceStream(NearbyDevice device) {
return getConnectedDeviceStreamById(device.info.id);
}
@override
Stream<NearbyDevice?> getConnectedDeviceStreamById(String deviceId) {
const connectedDeviceChannel = EventChannel( const connectedDeviceChannel = EventChannel(
"nearby_service_connected_device", "nearby_service_connected_device",
); );
return connectedDeviceChannel return connectedDeviceChannel.receiveBroadcastStream(deviceId).map((e) {
.receiveBroadcastStream(device.info.id)
.map((e) {
final updatedResult = ResultHandler.instance.handle(e); final updatedResult = ResultHandler.instance.handle(e);
return NearbyDeviceMapper.instance.mapToDevice(updatedResult); return NearbyDeviceMapper.instance.mapToDevice(updatedResult);
}); });
@@ -48,11 +48,21 @@ abstract class NearbyServicePlatform extends PlatformInterface {
throw UnimplementedError('streamPeers() has not been implemented.'); throw UnimplementedError('streamPeers() has not been implemented.');
} }
@Deprecated('Use getConnectedDeviceStreamById instead')
Stream<NearbyDevice?> getConnectedDeviceStream(NearbyDevice device) { Stream<NearbyDevice?> getConnectedDeviceStream(NearbyDevice device) {
throw UnimplementedError( throw UnimplementedError(
'getConnectedDeviceStream() has not been implemented.'); 'getConnectedDeviceStream() has not been implemented.');
} }
Stream<NearbyDevice?> getConnectedDeviceStreamById(String deviceId) {
throw UnimplementedError(
'getConnectedDeviceStreamById() has not been implemented.',
);
}
@Deprecated(
'This method will be removed. Method disconnect is platform-specific and you should use NearbyServiceIOSPlatform.disconnectById or NearbyServiceAndroidPlatform.disconnectById instead.',
)
Future<bool> disconnect(NearbyDevice device) { Future<bool> disconnect(NearbyDevice device) {
throw UnimplementedError('disconnect() has not been implemented.'); throw UnimplementedError('disconnect() has not been implemented.');
} }
+7
View File
@@ -1,3 +1,4 @@
import 'package:flutter/foundation.dart';
import 'package:nearby_service/nearby_service.dart'; import 'package:nearby_service/nearby_service.dart';
/// ///
@@ -36,4 +37,10 @@ abstract base class NearbyMessage<C extends NearbyMessageContent> {
String toString() { String toString() {
return 'NearbyMessage{content: $content}'; return 'NearbyMessage{content: $content}';
} }
///
/// Get [Map] from [NearbyMessage].
///
@mustCallSuper
Map<String, dynamic> toJson() => {'content': content.toJson()};
} }
+13 -1
View File
@@ -23,10 +23,11 @@ final class OutgoingNearbyMessage<C extends NearbyMessageContent>
/// ///
/// Get [Map] from [OutgoingNearbyMessage]. /// Get [Map] from [OutgoingNearbyMessage].
/// ///
@override
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
return { return {
'content': content.toJson(),
'receiver': receiver.toJson(), 'receiver': receiver.toJson(),
...super.toJson(),
}; };
} }
@@ -92,4 +93,15 @@ final class ReceivedNearbyMessage<C extends NearbyMessageContent>
String toString() { String toString() {
return 'ReceivedNearbyMessage{sender: $sender content: $content}'; return 'ReceivedNearbyMessage{sender: $sender content: $content}';
} }
///
/// Get [Map] from [ReceivedNearbyMessage].
///
@override
Map<String, dynamic> toJson() {
return {
'sender': sender.toJson(),
...super.toJson(),
};
}
} }
@@ -1,5 +1,4 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:nearby_service/nearby_service.dart'; import 'package:nearby_service/nearby_service.dart';
@@ -14,10 +13,16 @@ import 'socket_service/nearby_socket_service.dart';
class NearbyAndroidService extends NearbyService { class NearbyAndroidService extends NearbyService {
late final _socketService = NearbySocketService(this); late final _socketService = NearbySocketService(this);
@Deprecated(
'Use getCommunicationChannelStateStream or communicationChannelStateValue instead',
)
@override @override
ValueListenable<CommunicationChannelState> get communicationChannelState { ValueListenable<CommunicationChannelState> get communicationChannelState =>
return _socketService.state; _socketService.communicationChannelState;
}
@override
CommunicationChannelState get communicationChannelStateValue =>
_socketService.communicationChannelStateValue;
/// ///
/// Initializes Android [WifiP2PManager](https://developer.android.com/reference/android/net/wifi/p2p/WifiP2pManager) /// Initializes Android [WifiP2PManager](https://developer.android.com/reference/android/net/wifi/p2p/WifiP2pManager)
@@ -53,19 +58,39 @@ class NearbyAndroidService extends NearbyService {
/// ///
/// Note! Requires [NearbyAndroidDevice] to be passed. /// Note! Requires [NearbyAndroidDevice] to be passed.
/// ///
@Deprecated('Use connectById instead')
@override @override
Future<bool> connect(NearbyDevice device) { Future<bool> connect(NearbyDevice device) {
_requireAndroidDevice(device); _requireAndroidDevice(device);
return NearbyServiceAndroidPlatform.instance.connect(device.info.id); return NearbyServiceAndroidPlatform.instance.connect(device.info.id);
} }
///
/// Connects to the [deviceId] on the Wifi Direct network.
///
@override
Future<bool> connectById(String deviceId) {
return NearbyServiceAndroidPlatform.instance.connect(deviceId);
}
/// ///
/// Disconnects from the [device] on the Wifi Direct network. /// Disconnects from the [device] on the Wifi Direct network.
/// ///
/// [device] is not required for Android.
///
@Deprecated('Use disconnectById instead')
@override
Future<bool> disconnect([NearbyDevice? device]) {
return NearbyServiceAndroidPlatform.instance.disconnect();
}
///
/// Disconnects from the [deviceId] on the Wifi Direct network.
///
/// Note! Requires [NearbyAndroidDevice] to be passed. /// Note! Requires [NearbyAndroidDevice] to be passed.
/// ///
@override @override
Future<bool> disconnect([NearbyDevice? device]) { Future<bool> disconnectById([String? deviceId]) {
return NearbyServiceAndroidPlatform.instance.disconnect(); return NearbyServiceAndroidPlatform.instance.disconnect();
} }
@@ -143,6 +168,11 @@ class NearbyAndroidService extends NearbyService {
return NearbyServiceAndroidPlatform.instance.getConnectionInfoStream(); return NearbyServiceAndroidPlatform.instance.getConnectionInfoStream();
} }
@override
Stream<CommunicationChannelState> getCommunicationChannelStateStream() {
return _socketService.stateController.stream.asBroadcastStream();
}
void _requireAndroidDevice(NearbyDevice device) { void _requireAndroidDevice(NearbyDevice device) {
assert( assert(
device is NearbyAndroidDevice, device is NearbyAndroidDevice,
@@ -30,7 +30,10 @@ class NearbySocketService {
_pingManager, _pingManager,
); );
final state = ValueNotifier(CommunicationChannelState.notConnected); late final stateController =
StreamController<CommunicationChannelState>.broadcast()
..add(_state.value)
..stream.asBroadcastStream().listen((e) => _state.value = e);
NearbyAndroidCommunicationChannelData _androidData = NearbyAndroidCommunicationChannelData _androidData =
const NearbyAndroidCommunicationChannelData(); const NearbyAndroidCommunicationChannelData();
@@ -40,6 +43,13 @@ class NearbySocketService {
HttpServer? _server; HttpServer? _server;
StreamSubscription? _messagesSubscription; StreamSubscription? _messagesSubscription;
final _state = ValueNotifier(CommunicationChannelState.notConnected);
CommunicationChannelState get communicationChannelStateValue => _state.value;
ValueListenable<CommunicationChannelState> get communicationChannelState =>
_state;
/// ///
/// Start a socket with the user's role defined. /// Start a socket with the user's role defined.
/// If he is the owner of the group, he becomes a server. /// If he is the owner of the group, he becomes a server.
@@ -53,7 +63,7 @@ class NearbySocketService {
Future<bool> startSocket({ Future<bool> startSocket({
required NearbyCommunicationChannelData data, required NearbyCommunicationChannelData data,
}) async { }) async {
state.value = CommunicationChannelState.loading; stateController.add(CommunicationChannelState.loading);
_androidData = data.androidData; _androidData = data.androidData;
_connectedDeviceId = data.connectedDeviceId; _connectedDeviceId = data.connectedDeviceId;
@@ -123,7 +133,7 @@ class NearbySocketService {
_server = null; _server = null;
_connectedDeviceId = null; _connectedDeviceId = null;
state.value = CommunicationChannelState.notConnected; stateController.add(CommunicationChannelState.notConnected);
return true; return true;
} catch (e) { } catch (e) {
return false; return false;
@@ -134,7 +144,7 @@ class NearbySocketService {
required NearbyServiceMessagesListener socketListener, required NearbyServiceMessagesListener socketListener,
required NearbyConnectionAndroidInfo info, required NearbyConnectionAndroidInfo info,
}) async { }) async {
if (state.value.isLoading) { if (_state.value.isLoading) {
final response = await _network.pingServer( final response = await _network.pingServer(
address: info.ownerIpAddress, address: info.ownerIpAddress,
port: _androidData.port, port: _androidData.port,
@@ -216,23 +226,23 @@ class NearbySocketService {
} }
}, },
onDone: () { onDone: () {
state.value = CommunicationChannelState.notConnected; stateController.add(CommunicationChannelState.notConnected);
socketListener.onDone?.call(); socketListener.onDone?.call();
}, },
onError: (e, s) { onError: (e, s) {
Logger.error(e); Logger.error(e);
state.value = CommunicationChannelState.notConnected; stateController.add(CommunicationChannelState.notConnected);
socketListener.onError?.call(e, s); socketListener.onError?.call(e, s);
}, },
cancelOnError: socketListener.cancelOnError, cancelOnError: socketListener.cancelOnError,
); );
} }
if (_messagesSubscription != null) { if (_messagesSubscription != null) {
state.value = CommunicationChannelState.connected; stateController.add(CommunicationChannelState.connected);
Logger.info('Socket subscription was created successfully'); Logger.info('Socket subscription was created successfully');
socketListener.onCreated?.call(); socketListener.onCreated?.call();
} else { } else {
state.value = CommunicationChannelState.notConnected; stateController.add(CommunicationChannelState.notConnected);
} }
} }
+88 -32
View File
@@ -14,29 +14,56 @@ import 'package:nearby_service/src/utils/stream_mapper.dart';
/// ///
class NearbyIOSService extends NearbyService { class NearbyIOSService extends NearbyService {
final _isBrowser = ValueNotifier<bool>(true); final _isBrowser = ValueNotifier<bool>(true);
final _state = ValueNotifier(CommunicationChannelState.notConnected); final _communicationChannelState =
ValueNotifier(CommunicationChannelState.notConnected);
late final _isBrowserController = StreamController<bool>.broadcast()
..add(_isBrowser.value)
..stream.asBroadcastStream().listen((e) => _isBrowser.value = e);
late final _stateController =
StreamController<CommunicationChannelState>.broadcast()
..add(_communicationChannelState.value)
..stream
.asBroadcastStream()
.listen((e) => _communicationChannelState.value = e);
StreamSubscription? _messagesSubscription; StreamSubscription? _messagesSubscription;
StreamSubscription? _resourcesSubscription; StreamSubscription? _resourcesSubscription;
@override @override
CommunicationChannelState get communicationChannelStateValue =>
_communicationChannelState.value;
@override
@Deprecated(
'Use getCommunicationChannelStateStream or communicationChannelStateValue instead',
)
ValueListenable<CommunicationChannelState> get communicationChannelState => ValueListenable<CommunicationChannelState> get communicationChannelState =>
_state; _communicationChannelState;
/// ///
/// Determines whether the current device is a **Browser** or **Advertiser**. /// Determines whether the current device is a **Browser** or **Advertiser**.
/// ///
@Deprecated('Use getIsBrowserStream or isBrowserValue instead')
ValueListenable<bool> get isBrowser => _isBrowser;
///
/// Determines whether the current device is a **Browser** or **Advertiser**.
///
bool get isBrowserValue => _isBrowser.value;
///
/// Stream that determines whether the current device is a **Browser** or **Advertiser**.
///
/// * Browser will only see devices with Advertiser status in the peers list. /// * Browser will only see devices with Advertiser status in the peers list.
/// Browser sends connection requests. /// Browser sends connection requests.
/// * Advertiser will see in the peers list only devices with Browser /// * Advertiser will see in the peers list only devices with Browser
/// status that have sent it a connection request. /// status that have sent it a connection request.
/// Advertiser accepts or rejects connection requests. /// Advertiser accepts or rejects connection requests.
/// ///
ValueListenable<bool> get isBrowser => _isBrowser; Stream<bool> getIsBrowserStream() =>
_isBrowserController.stream.asBroadcastStream();
String get _currentConnectionType {
return _isBrowser.value ? 'browsing' : 'advertising';
}
/// ///
/// Initializes [MCNearbyServiceAdvertiser](https://developer.apple.com/documentation/multipeerconnectivity/mcnearbyserviceadvertiser) /// Initializes [MCNearbyServiceAdvertiser](https://developer.apple.com/documentation/multipeerconnectivity/mcnearbyserviceadvertiser)
@@ -73,8 +100,8 @@ class NearbyIOSService extends NearbyService {
/// ///
/// Starts discovery on the local P2P network. /// Starts discovery on the local P2P network.
/// ///
/// Starts browsing for peers if [isBrowser] is true. /// Starts browsing for peers if [isBrowserValue] is true.
/// Starts advertising for peers if [isBrowser] is false. /// Starts advertising for peers if [isBrowserValue] is false.
/// ///
@override @override
Future<bool> discover() async { Future<bool> discover() async {
@@ -92,8 +119,8 @@ class NearbyIOSService extends NearbyService {
/// ///
/// Slops discovery on the local P2P network. /// Slops discovery on the local P2P network.
/// ///
/// Slops browsing for peers if [isBrowser] is true. /// Slops browsing for peers if [isBrowserValue] is true.
/// Slops advertising for peers if [isBrowser] is false. /// Slops advertising for peers if [isBrowserValue] is false.
/// ///
@override @override
Future<bool> stopDiscovery() async { Future<bool> stopDiscovery() async {
@@ -112,24 +139,35 @@ class NearbyIOSService extends NearbyService {
/// ///
/// Connects to the [device] on the P2P network. /// Connects to the [device] on the P2P network.
/// ///
/// Invites [device] if [isBrowser] is true. /// Invites [device] if [isBrowserValue] is true.
/// Accepts invite from [device] if [isBrowser] is false. /// Accepts invite from [device] if [isBrowserValue] is false.
/// ///
/// Note! Requires [NearbyIOSDevice] to be passed. /// Note! Requires [NearbyIOSDevice] to be passed.
/// @Deprecated('Use connectById instead')
@override @override
Future<bool> connect(NearbyDevice device) async { Future<bool> connect(NearbyDevice device) async {
_requireIOSDevice(device); _requireIOSDevice(device);
return connectById(device.info.id);
}
///
/// Connects to the [deviceId] on the P2P network.
///
/// Invites [deviceId] if [isBrowserValue] is true.
/// Accepts invite from [deviceId] if [isBrowserValue] is false.
///
@override
Future<bool> connectById(String deviceId) async {
final result = _isBrowser.value final result = _isBrowser.value
? await NearbyServiceIOSPlatform.instance.invite(device.info.id) ? await NearbyServiceIOSPlatform.instance.invite(deviceId)
: await NearbyServiceIOSPlatform.instance.acceptInvite(device.info.id); : await NearbyServiceIOSPlatform.instance.acceptInvite(deviceId);
_logResult( _logResult(
result, result,
onSuccess: onSuccess:
'${_isBrowser.value ? 'Sent invitation to' : 'Accepted invitation from'} ' '${_isBrowser.value ? 'Sent invitation to' : 'Accepted invitation from'} '
'${device.info.id}', '$deviceId',
onError: 'Failed to connect to ${device.info.id}', onError: 'Failed to connect to $deviceId',
); );
return result; return result;
} }
@@ -139,17 +177,25 @@ class NearbyIOSService extends NearbyService {
/// ///
/// Note! Requires [NearbyIOSDevice] to be passed. /// Note! Requires [NearbyIOSDevice] to be passed.
/// ///
@Deprecated('Use disconnectById instead')
@override @override
Future<bool> disconnect([NearbyDevice? device]) async { Future<bool> disconnect([NearbyDevice? device]) async {
if (device == null) return false; if (device == null) return false;
_requireIOSDevice(device); _requireIOSDevice(device);
final result = await NearbyServiceIOSPlatform.instance.disconnect( return disconnectById(device.info.id);
device.info.id, }
);
///
/// Disconnects from the [deviceId] on the P2P network.
///
@override
Future<bool> disconnectById([String? deviceId]) async {
if (deviceId == null) return false;
final result = await NearbyServiceIOSPlatform.instance.disconnect(deviceId);
_logResult( _logResult(
result, result,
onSuccess: 'Disconnected from ${device.info.id}', onSuccess: 'Disconnected from $deviceId',
onError: 'Failed to disconnect from ${device.info.id}', onError: 'Failed to disconnect from $deviceId',
); );
return result; return result;
} }
@@ -163,7 +209,8 @@ class NearbyIOSService extends NearbyService {
NearbyCommunicationChannelData data, NearbyCommunicationChannelData data,
) async { ) async {
Logger.debug('Creating messages subscription'); Logger.debug('Creating messages subscription');
_state.value = CommunicationChannelState.loading; _stateController.add(CommunicationChannelState.loading);
await endCommunicationChannel(); await endCommunicationChannel();
final eventListener = data.messagesListener; final eventListener = data.messagesListener;
final filesListener = data.filesListener; final filesListener = data.filesListener;
@@ -175,12 +222,12 @@ class NearbyIOSService extends NearbyService {
.listen( .listen(
eventListener.onData, eventListener.onData,
onDone: () { onDone: () {
_state.value = CommunicationChannelState.notConnected; _stateController.add(CommunicationChannelState.notConnected);
eventListener.onDone?.call(); eventListener.onDone?.call();
}, },
onError: (e, s) { onError: (e, s) {
Logger.error(e); Logger.error(e);
_state.value = CommunicationChannelState.notConnected; _stateController.add(CommunicationChannelState.notConnected);
eventListener.onError?.call(e, s); eventListener.onError?.call(e, s);
}, },
cancelOnError: eventListener.cancelOnError, cancelOnError: eventListener.cancelOnError,
@@ -194,7 +241,7 @@ class NearbyIOSService extends NearbyService {
onDone: filesListener?.onDone, onDone: filesListener?.onDone,
onError: (e, s) { onError: (e, s) {
Logger.error(e); Logger.error(e);
_state.value = CommunicationChannelState.notConnected; _stateController.add(CommunicationChannelState.notConnected);
filesListener?.onError?.call(e, s); filesListener?.onError?.call(e, s);
}, },
cancelOnError: filesListener?.cancelOnError, cancelOnError: filesListener?.cancelOnError,
@@ -202,9 +249,9 @@ class NearbyIOSService extends NearbyService {
if (_messagesSubscription != null) { if (_messagesSubscription != null) {
Logger.info('Messages subscription was created successfully'); Logger.info('Messages subscription was created successfully');
eventListener.onCreated?.call(); eventListener.onCreated?.call();
_state.value = CommunicationChannelState.connected; _stateController.add(CommunicationChannelState.connected);
} else { } else {
_state.value = CommunicationChannelState.notConnected; _stateController.add(CommunicationChannelState.notConnected);
} }
if (_resourcesSubscription != null) { if (_resourcesSubscription != null) {
Logger.info('Resources subscription was created successfully'); Logger.info('Resources subscription was created successfully');
@@ -223,7 +270,7 @@ class NearbyIOSService extends NearbyService {
await _resourcesSubscription?.cancel(); await _resourcesSubscription?.cancel();
_messagesSubscription = null; _messagesSubscription = null;
_resourcesSubscription = null; _resourcesSubscription = null;
_state.value = CommunicationChannelState.notConnected; _stateController.add(CommunicationChannelState.notConnected);
Logger.debug('Communication channel was cancelled'); Logger.debug('Communication channel was cancelled');
return true; return true;
} }
@@ -240,6 +287,11 @@ class NearbyIOSService extends NearbyService {
throw NearbyServiceException.invalidMessage(message.content); throw NearbyServiceException.invalidMessage(message.content);
} }
@override
Stream<CommunicationChannelState> getCommunicationChannelStateStream() {
return _stateController.stream.asBroadcastStream();
}
/// ///
/// If you want to ask the user to change the name on the network, /// If you want to ask the user to change the name on the network,
/// you can retrieve the name previously saved in /// you can retrieve the name previously saved in
@@ -253,11 +305,11 @@ class NearbyIOSService extends NearbyService {
} }
/// ///
/// Changes the [isBrowser] to the passed [value]. /// Changes the [isBrowserValue] to the passed [value].
/// ///
void setIsBrowser({required bool value}) { void setIsBrowser({required bool value}) {
Logger.debug('Is Browser Value was set to $value'); Logger.debug('Is Browser Value was set to $value');
_isBrowser.value = value; _isBrowserController.add(value);
} }
void _logResult( void _logResult(
@@ -272,6 +324,10 @@ class NearbyIOSService extends NearbyService {
} }
} }
String get _currentConnectionType {
return _isBrowser.value ? 'browsing' : 'advertising';
}
void _requireIOSDevice(NearbyDevice device) { void _requireIOSDevice(NearbyDevice device) {
assert( assert(
device is NearbyIOSDevice, device is NearbyIOSDevice,
+1 -1
View File
@@ -1,6 +1,6 @@
name: nearby_service name: nearby_service
description: Nearby Service Flutter Plugin is used to create connections in a P2P network. Supports sending text messages and files. description: Nearby Service Flutter Plugin is used to create connections in a P2P network. Supports sending text messages and files.
version: 0.0.9 version: 0.1.0
homepage: https://github.com/ksenia312/nearby_service homepage: https://github.com/ksenia312/nearby_service
repository: https://github.com/ksenia312/nearby_service repository: https://github.com/ksenia312/nearby_service
+6
View File
@@ -49,6 +49,12 @@ class MockNearbyServicePlatform
throw UnimplementedError(); throw UnimplementedError();
} }
@override
Stream<NearbyDevice?> getConnectedDeviceStreamById(String deviceId) {
// TODO: implement getConnectedDeviceStreamById
throw UnimplementedError();
}
@override @override
Future<NearbyDeviceInfo?> getCurrentDeviceInfo() { Future<NearbyDeviceInfo?> getCurrentDeviceInfo() {
// TODO: implement getCurrentDevice // TODO: implement getCurrentDevice