fix(lib): add message checks and improve exceptions
This commit is contained in:
+25
-11
@@ -53,15 +53,15 @@ class MyApp extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
if (service.currentDeviceInfo != null)
|
if (service.currentDeviceInfo != null)
|
||||||
Text(
|
Text(
|
||||||
'Device Name: ${service.currentDeviceInfo!.displayName}\n'
|
'Device Name: ${service.currentDeviceInfo!.displayName}'
|
||||||
'${Platform.isIOS ? 'Device ID: ${service.currentDeviceInfo!.id}' : ''}',
|
'${Platform.isIOS ? '\nDevice ID: ${service.currentDeviceInfo!.id}' : ''}',
|
||||||
),
|
),
|
||||||
if (Platform.isIOS)
|
if (Platform.isIOS)
|
||||||
Text(
|
Text(
|
||||||
'You are ${service.isIOSBrowser ? 'going to find your friend' : 'waiting for another user to connect'}',
|
'You are ${service.isIOSBrowser ? 'going to find your friend' : 'waiting for another user to connect'}',
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'Communication channel state: ${service.communicationChannelState.name.toUpperCase()}',
|
'Communication channel state: ${service.communicationChannelState.previewName}',
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -172,6 +172,16 @@ enum AppState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension on CommunicationChannelState {
|
||||||
|
String get previewName {
|
||||||
|
return switch (this) {
|
||||||
|
CommunicationChannelState.notConnected => 'Not connected',
|
||||||
|
CommunicationChannelState.loading => 'Connecting',
|
||||||
|
CommunicationChannelState.connected => 'Connected',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class AppService extends ChangeNotifier {
|
class AppService extends ChangeNotifier {
|
||||||
late final _nearbyService = NearbyService.getInstance()
|
late final _nearbyService = NearbyService.getInstance()
|
||||||
..communicationChannelState.addListener(notifyListeners);
|
..communicationChannelState.addListener(notifyListeners);
|
||||||
@@ -187,6 +197,12 @@ class AppService extends ChangeNotifier {
|
|||||||
StreamSubscription? peersSubscription;
|
StreamSubscription? peersSubscription;
|
||||||
StreamSubscription? connectedDeviceSubscription;
|
StreamSubscription? connectedDeviceSubscription;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
stopListeningAll();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
CommunicationChannelState get communicationChannelState {
|
CommunicationChannelState get communicationChannelState {
|
||||||
return _nearbyService.communicationChannelState.value;
|
return _nearbyService.communicationChannelState.value;
|
||||||
}
|
}
|
||||||
@@ -328,7 +344,7 @@ class AppService extends ChangeNotifier {
|
|||||||
final wasConnected = connectedDevice?.status.isConnected ?? false;
|
final wasConnected = connectedDevice?.status.isConnected ?? false;
|
||||||
final nowConnected = event?.status.isConnected ?? false;
|
final nowConnected = event?.status.isConnected ?? false;
|
||||||
if (wasConnected && !nowConnected) {
|
if (wasConnected && !nowConnected) {
|
||||||
restart();
|
stopListeningAll();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
connectedDevice = event;
|
connectedDevice = event;
|
||||||
@@ -358,7 +374,7 @@ class AppService extends ChangeNotifier {
|
|||||||
Future<void> startCommunicationChannel({
|
Future<void> startCommunicationChannel({
|
||||||
ValueChanged<ReceivedNearbyMessage>? listener,
|
ValueChanged<ReceivedNearbyMessage>? listener,
|
||||||
}) async {
|
}) async {
|
||||||
final eventListener = NearbyServiceStreamListener<ReceivedNearbyMessage>(
|
final eventListener = NearbyServiceStreamListener(
|
||||||
onCreated: (_) {
|
onCreated: (_) {
|
||||||
updateState(AppState.communicationChannelCreated);
|
updateState(AppState.communicationChannelCreated);
|
||||||
},
|
},
|
||||||
@@ -366,7 +382,7 @@ class AppService extends ChangeNotifier {
|
|||||||
listener?.call(event);
|
listener?.call(event);
|
||||||
},
|
},
|
||||||
onError: (e, [StackTrace? s]) {
|
onError: (e, [StackTrace? s]) {
|
||||||
restart();
|
stopListeningAll();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -396,17 +412,15 @@ class AppService extends ChangeNotifier {
|
|||||||
print(e);
|
print(e);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
await restart();
|
await stopListeningAll();
|
||||||
}
|
}
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> restart() async {
|
Future<void> stopListeningAll() async {
|
||||||
await stopListeningConnectedDevice();
|
await stopListeningConnectedDevice();
|
||||||
await stopListeningPeers();
|
await stopListeningPeers();
|
||||||
await stopDiscovery();
|
await stopDiscovery();
|
||||||
|
|
||||||
await discover();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateState(AppState state, {bool shouldNotify = true}) {
|
void updateState(AppState state, {bool shouldNotify = true}) {
|
||||||
@@ -768,7 +782,7 @@ class _ConnectedSocketBodyState extends State<_ConnectedSocketBody> {
|
|||||||
if (device == null) {
|
if (device == null) {
|
||||||
return Center(
|
return Center(
|
||||||
child: _ActionButton(
|
child: _ActionButton(
|
||||||
onTap: service.restart,
|
onTap: service.stopListeningAll,
|
||||||
title: 'Restart',
|
title: 'Restart',
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -12,6 +12,13 @@ abstract class NearbyMessage {
|
|||||||
|
|
||||||
final String value;
|
final String value;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Checks if [value] is not empty
|
||||||
|
///
|
||||||
|
bool get isValid {
|
||||||
|
return value.isNotEmpty;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) =>
|
bool operator ==(Object other) =>
|
||||||
identical(this, other) ||
|
identical(this, other) ||
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ class NearbySocketService {
|
|||||||
|
|
||||||
String? _connectedDeviceId;
|
String? _connectedDeviceId;
|
||||||
WebSocket? _socket;
|
WebSocket? _socket;
|
||||||
|
HttpServer? _server;
|
||||||
StreamSubscription<ReceivedNearbyMessage>? _messagesSubscription;
|
StreamSubscription<ReceivedNearbyMessage>? _messagesSubscription;
|
||||||
|
|
||||||
///
|
///
|
||||||
@@ -72,21 +73,25 @@ class NearbySocketService {
|
|||||||
/// Add [OutgoingNearbyMessage]'s JSON representation to [_socket].
|
/// Add [OutgoingNearbyMessage]'s JSON representation to [_socket].
|
||||||
///
|
///
|
||||||
Future<bool> send(OutgoingNearbyMessage message) async {
|
Future<bool> send(OutgoingNearbyMessage message) async {
|
||||||
if (_socket != null && message.receiver.id == _connectedDeviceId) {
|
if (message.isValid) {
|
||||||
final sender = await _manager.getCurrentDeviceInfo();
|
if (_socket != null && message.receiver.id == _connectedDeviceId) {
|
||||||
if (sender != null) {
|
final sender = await _manager.getCurrentDeviceInfo();
|
||||||
_socket!.add(
|
if (sender != null) {
|
||||||
jsonEncode(
|
_socket!.add(
|
||||||
{
|
jsonEncode(
|
||||||
'message': message.value,
|
{
|
||||||
'sender': sender.toJson(),
|
'message': message.value,
|
||||||
},
|
'sender': sender.toJson(),
|
||||||
),
|
},
|
||||||
);
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return true;
|
return false;
|
||||||
|
} else {
|
||||||
|
throw NearbyServiceException.invalidMessage(message.value);
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
@@ -96,8 +101,12 @@ class NearbySocketService {
|
|||||||
try {
|
try {
|
||||||
await _messagesSubscription?.cancel();
|
await _messagesSubscription?.cancel();
|
||||||
_messagesSubscription = null;
|
_messagesSubscription = null;
|
||||||
|
_socket?.close();
|
||||||
_socket = null;
|
_socket = null;
|
||||||
|
_server?.close(force: true);
|
||||||
|
_server = null;
|
||||||
_connectedDeviceId = null;
|
_connectedDeviceId = null;
|
||||||
|
state.value = CommunicationChannelState.notConnected;
|
||||||
return true;
|
return true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return false;
|
return false;
|
||||||
@@ -142,11 +151,11 @@ class NearbySocketService {
|
|||||||
required int port,
|
required int port,
|
||||||
ValueChanged<HttpRequest>? serverListener,
|
ValueChanged<HttpRequest>? serverListener,
|
||||||
}) async {
|
}) async {
|
||||||
final httpServer = await _network.startServer(
|
_server = await _network.startServer(
|
||||||
ownerIpAddress: info.ownerIpAddress,
|
ownerIpAddress: info.ownerIpAddress,
|
||||||
port: port,
|
port: port,
|
||||||
);
|
);
|
||||||
httpServer?.listen(
|
_server?.listen(
|
||||||
(request) async {
|
(request) async {
|
||||||
serverListener?.call(request);
|
serverListener?.call(request);
|
||||||
final isPing = await _pingManager.checkPing(request);
|
final isPing = await _pingManager.checkPing(request);
|
||||||
@@ -156,7 +165,7 @@ class NearbySocketService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.uri.path == '/ws') {
|
if (request.uri.path == _Urls.ws) {
|
||||||
_socket = await WebSocketTransformer.upgrade(request);
|
_socket = await WebSocketTransformer.upgrade(request);
|
||||||
_createSocketSubscription(socketListener);
|
_createSocketSubscription(socketListener);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -5,6 +5,10 @@ class _Protocols {
|
|||||||
static const ws = 'ws://';
|
static const ws = 'ws://';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _Urls {
|
||||||
|
static const ws = '/ws';
|
||||||
|
}
|
||||||
|
|
||||||
class NearbyServiceNetwork {
|
class NearbyServiceNetwork {
|
||||||
final _httpClient = HttpClient();
|
final _httpClient = HttpClient();
|
||||||
final _random = Random();
|
final _random = Random();
|
||||||
@@ -20,7 +24,7 @@ class NearbyServiceNetwork {
|
|||||||
final response = await request.close();
|
final response = await request.close();
|
||||||
return response;
|
return response;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Logger.error('Server is unreachable');
|
Logger.error('Server is unreachable: $e');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -37,7 +41,7 @@ class NearbyServiceNetwork {
|
|||||||
required int port,
|
required int port,
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
final url = 'ws://$ownerIpAddress:$port';
|
final url = '${_Protocols.ws}$ownerIpAddress:$port';
|
||||||
Logger.debug('Starting server on $url');
|
Logger.debug('Starting server on $url');
|
||||||
var server = await HttpServer.bind(
|
var server = await HttpServer.bind(
|
||||||
ownerIpAddress,
|
ownerIpAddress,
|
||||||
@@ -47,8 +51,7 @@ class NearbyServiceNetwork {
|
|||||||
Logger.info('Server running on $url');
|
Logger.info('Server running on $url');
|
||||||
return server;
|
return server;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Logger.error('Error starting socket: $e');
|
throw NearbyServiceException('Error starting socket: $e');
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,14 +61,14 @@ class NearbyServiceNetwork {
|
|||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
final connectionId = _random.nextInt(1000) + 100;
|
final connectionId = _random.nextInt(1000) + 100;
|
||||||
final url = '${_Protocols.ws}$ownerIpAddress:$port/ws?as=$connectionId';
|
final url =
|
||||||
|
'${_Protocols.ws}$ownerIpAddress:$port${_Urls.ws}?as=$connectionId';
|
||||||
Logger.debug('Connecting to $url');
|
Logger.debug('Connecting to $url');
|
||||||
final socket = await WebSocket.connect(url);
|
final socket = await WebSocket.connect(url);
|
||||||
Logger.info('Connected to $url');
|
Logger.info('Connected to $url');
|
||||||
return socket;
|
return socket;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Logger.error('Error connecting to server: $e');
|
throw NearbyServiceException('Error connecting to server: $e');
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -201,6 +201,7 @@ class NearbyIOSService extends NearbyService {
|
|||||||
FutureOr<bool> endCommunicationChannel() async {
|
FutureOr<bool> endCommunicationChannel() async {
|
||||||
await _messagesSubscription?.cancel();
|
await _messagesSubscription?.cancel();
|
||||||
_messagesSubscription = null;
|
_messagesSubscription = null;
|
||||||
|
_state.value = CommunicationChannelState.notConnected;
|
||||||
Logger.debug('Communication channel was cancelled');
|
Logger.debug('Communication channel was cancelled');
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -211,7 +212,10 @@ class NearbyIOSService extends NearbyService {
|
|||||||
///
|
///
|
||||||
@override
|
@override
|
||||||
Future<bool> send(OutgoingNearbyMessage message) {
|
Future<bool> send(OutgoingNearbyMessage message) {
|
||||||
return NearbyServiceIOSPlatform.instance.send(message);
|
if (message.isValid) {
|
||||||
|
return NearbyServiceIOSPlatform.instance.send(message);
|
||||||
|
}
|
||||||
|
throw NearbyServiceException.invalidMessage(message.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
@@ -242,7 +246,7 @@ class NearbyIOSService extends NearbyService {
|
|||||||
if (value) {
|
if (value) {
|
||||||
Logger.info(onSuccess);
|
Logger.info(onSuccess);
|
||||||
} else {
|
} else {
|
||||||
Logger.error(onError);
|
throw NearbyServiceException(onError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ class NearbyCommunicationChannelData {
|
|||||||
///
|
///
|
||||||
/// Listener for message stream changes.
|
/// Listener for message stream changes.
|
||||||
///
|
///
|
||||||
final NearbyServiceStreamListener<ReceivedNearbyMessage> eventListener;
|
final NearbyServiceStreamListener eventListener;
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Android-specific connection data.
|
/// Android-specific connection data.
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:nearby_service/nearby_service.dart';
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Stream Subscription Listener.
|
/// Stream Subscription Listener.
|
||||||
///
|
///
|
||||||
class NearbyServiceStreamListener<T> {
|
class NearbyServiceStreamListener {
|
||||||
///
|
///
|
||||||
/// It is required to pass the [onData] parameter to process the
|
/// It is required to pass the [onData] parameter to process the
|
||||||
/// data that came through the stream.
|
/// data that came through the stream.
|
||||||
@@ -18,8 +19,8 @@ class NearbyServiceStreamListener<T> {
|
|||||||
this.cancelOnError,
|
this.cancelOnError,
|
||||||
});
|
});
|
||||||
|
|
||||||
final ValueChanged<T> onData;
|
final ValueChanged<ReceivedNearbyMessage> onData;
|
||||||
final ValueChanged<StreamSubscription<T>>? onCreated;
|
final ValueChanged<StreamSubscription<ReceivedNearbyMessage>>? onCreated;
|
||||||
final VoidCallback? onDone;
|
final VoidCallback? onDone;
|
||||||
final void Function(Object, [StackTrace])? onError;
|
final void Function(Object, [StackTrace])? onError;
|
||||||
final bool? cancelOnError;
|
final bool? cancelOnError;
|
||||||
|
|||||||
@@ -30,5 +30,11 @@ class NearbyServiceException implements Exception {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
factory NearbyServiceException.invalidMessage(String value) {
|
||||||
|
return NearbyServiceException(
|
||||||
|
'The message="$value" is not valid',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
final Object? error;
|
final Object? error;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user