first commit
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
export 'nearby_device.dart';
|
||||
export 'nearby_device_status.dart';
|
||||
export 'nearby_message.dart';
|
||||
@@ -0,0 +1,69 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:nearby_service/nearby_service.dart';
|
||||
|
||||
abstract class NearbyDevice {
|
||||
static const unknown = 'unknown';
|
||||
|
||||
const NearbyDevice({required this.info, required this.status});
|
||||
|
||||
final NearbyDeviceInfo info;
|
||||
final NearbyDeviceStatus status;
|
||||
|
||||
T? byPlatform<T>({
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract interface class NearbyDeviceMapper {
|
||||
static NearbyDeviceMapper get instance {
|
||||
if (Platform.isAndroid) {
|
||||
return NearbyAndroidMapper();
|
||||
}
|
||||
if (Platform.isIOS) {
|
||||
return NearbyIOSMapper();
|
||||
}
|
||||
|
||||
throw NearbyServiceException.unsupportedPlatform(
|
||||
caller: 'NearbyDeviceMapper',
|
||||
);
|
||||
}
|
||||
|
||||
List<NearbyDevice> mapToDeviceList(dynamic value);
|
||||
|
||||
NearbyDevice? mapToDevice(dynamic value);
|
||||
}
|
||||
|
||||
class NearbyDeviceInfo {
|
||||
const NearbyDeviceInfo({
|
||||
required this.displayName,
|
||||
required this.id,
|
||||
});
|
||||
|
||||
factory NearbyDeviceInfo.fromJson(Map<String, dynamic>? json) {
|
||||
return NearbyDeviceInfo(
|
||||
displayName: json?['displayName'] ?? NearbyDevice.unknown,
|
||||
id: json?['id'] ?? NearbyDevice.unknown,
|
||||
);
|
||||
}
|
||||
|
||||
final String displayName;
|
||||
final String id;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'id': id,
|
||||
'displayName': displayName,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
enum NearbyDeviceStatus {
|
||||
available,
|
||||
connected,
|
||||
failed,
|
||||
connecting,
|
||||
unavailable;
|
||||
|
||||
bool get isConnected => this == NearbyDeviceStatus.connected;
|
||||
|
||||
static NearbyDeviceStatus fromAndroidCode(num? code) {
|
||||
if (code == null) {
|
||||
return NearbyDeviceStatus.failed;
|
||||
}
|
||||
return switch (code) {
|
||||
(0) => NearbyDeviceStatus.connected,
|
||||
(1) => NearbyDeviceStatus.connecting,
|
||||
(2) => NearbyDeviceStatus.failed,
|
||||
(3) => NearbyDeviceStatus.available,
|
||||
(4) => NearbyDeviceStatus.unavailable,
|
||||
(_) => NearbyDeviceStatus.failed,
|
||||
};
|
||||
}
|
||||
|
||||
static NearbyDeviceStatus fromIosCode(String? code) {
|
||||
if (code == null) {
|
||||
return NearbyDeviceStatus.failed;
|
||||
}
|
||||
final value = num.tryParse(code);
|
||||
return switch (value) {
|
||||
(0) => NearbyDeviceStatus.available,
|
||||
(1) => NearbyDeviceStatus.connecting,
|
||||
(2) => NearbyDeviceStatus.connected,
|
||||
(_) => NearbyDeviceStatus.failed,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import 'package:nearby_service/nearby_service.dart';
|
||||
import 'package:nearby_service/src/utils/logger.dart';
|
||||
|
||||
abstract class NearbyMessage {
|
||||
const NearbyMessage({required this.value});
|
||||
|
||||
final String value;
|
||||
}
|
||||
|
||||
class OutgoingNearbyMessage extends NearbyMessage {
|
||||
const OutgoingNearbyMessage({
|
||||
required super.value,
|
||||
required this.receiver,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'message': value,
|
||||
'receiver': receiver.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
final NearbyDeviceInfo receiver;
|
||||
}
|
||||
|
||||
class ReceivedNearbyMessage extends NearbyMessage {
|
||||
const ReceivedNearbyMessage({
|
||||
required super.value,
|
||||
required this.sender,
|
||||
});
|
||||
|
||||
factory ReceivedNearbyMessage.fromJson(Map<String, dynamic>? json) {
|
||||
try {
|
||||
return ReceivedNearbyMessage(
|
||||
value: json?['message'] ?? '',
|
||||
sender: NearbyDeviceInfo.fromJson(json?['sender']),
|
||||
);
|
||||
} catch (e) {
|
||||
Logger.error(e);
|
||||
throw NearbyServiceException('Can\'t map to device $json');
|
||||
}
|
||||
}
|
||||
|
||||
final NearbyDeviceInfo sender;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export 'nearby_android_service.dart';
|
||||
export 'nearby_service_android_interface.dart';
|
||||
export 'models/nearby_connection_info.dart';
|
||||
export 'models/nearby_device.dart';
|
||||
@@ -0,0 +1,36 @@
|
||||
import 'dart:convert';
|
||||
|
||||
class NearbyConnectionAndroidInfo {
|
||||
static const unknown = 'unknown';
|
||||
|
||||
const NearbyConnectionAndroidInfo({
|
||||
required this.ownerIpAddress,
|
||||
required this.groupFormed,
|
||||
required this.isGroupOwner,
|
||||
});
|
||||
|
||||
factory NearbyConnectionAndroidInfo.fromJson(Map<String, dynamic> json) {
|
||||
final ownerIpAddress = (json['groupOwnerAddress'] ?? unknown) as String;
|
||||
return NearbyConnectionAndroidInfo(
|
||||
ownerIpAddress: ownerIpAddress.replaceFirst('/', ''),
|
||||
groupFormed: json['groupFormed'] ?? false,
|
||||
isGroupOwner: json['isGroupOwner'] ?? false,
|
||||
);
|
||||
}
|
||||
|
||||
final String ownerIpAddress;
|
||||
final bool isGroupOwner;
|
||||
final bool groupFormed;
|
||||
}
|
||||
|
||||
class NearbyConnectionInfoMapper {
|
||||
NearbyConnectionInfoMapper._();
|
||||
|
||||
static NearbyConnectionAndroidInfo? mapToInfo(dynamic value) {
|
||||
final jsonValue = jsonDecode(value) as Map<String, dynamic>?;
|
||||
if (jsonValue == null) {
|
||||
return null;
|
||||
}
|
||||
return NearbyConnectionAndroidInfo.fromJson(jsonValue);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
import 'package:nearby_service/nearby_service.dart';
|
||||
import 'package:nearby_service/src/utils/decoder.dart';
|
||||
|
||||
class NearbyAndroidDevice extends NearbyDevice {
|
||||
NearbyAndroidDevice({
|
||||
required String deviceName,
|
||||
required super.status,
|
||||
required this.deviceAddress,
|
||||
required this.isGroupOwner,
|
||||
required this.isServiceDiscoveryCapable,
|
||||
required this.primaryDeviceType,
|
||||
required this.wpsKeypadSupported,
|
||||
required this.wpsPbcSupported,
|
||||
required this.wpsDisplaySupported,
|
||||
this.secondaryDeviceType,
|
||||
}) : super(
|
||||
info: NearbyDeviceInfo(
|
||||
displayName: deviceName,
|
||||
id: deviceAddress,
|
||||
),
|
||||
);
|
||||
|
||||
factory NearbyAndroidDevice.fromJson(Map<String, dynamic>? json) {
|
||||
return NearbyAndroidDevice(
|
||||
deviceName: json?['deviceName'] ?? NearbyDevice.unknown,
|
||||
deviceAddress: json?['deviceAddress'] ?? NearbyDevice.unknown,
|
||||
isGroupOwner: json?['isGroupOwner'] ?? false,
|
||||
isServiceDiscoveryCapable: json?['isServiceDiscoveryCapable'] ?? false,
|
||||
primaryDeviceType: json?['primaryDeviceType'] ?? NearbyDevice.unknown,
|
||||
secondaryDeviceType: json?['secondaryDeviceType'],
|
||||
wpsDisplaySupported: json?['wpsDisplaySupported'] ?? false,
|
||||
wpsKeypadSupported: json?['wpsKeypadSupported'] ?? false,
|
||||
wpsPbcSupported: json?['wpsPbcSupported'] ?? false,
|
||||
status: NearbyDeviceStatus.fromAndroidCode(json?['status']),
|
||||
);
|
||||
}
|
||||
|
||||
final String deviceAddress;
|
||||
final bool isGroupOwner;
|
||||
final bool isServiceDiscoveryCapable;
|
||||
final String primaryDeviceType;
|
||||
final String? secondaryDeviceType;
|
||||
final bool wpsKeypadSupported;
|
||||
final bool wpsPbcSupported;
|
||||
final bool wpsDisplaySupported;
|
||||
}
|
||||
|
||||
class NearbyAndroidMapper implements NearbyDeviceMapper {
|
||||
@override
|
||||
List<NearbyDevice> mapToDeviceList(dynamic value) {
|
||||
final decoded = Decoder.decodeList(value);
|
||||
return [
|
||||
...?decoded?.map(
|
||||
(e) => NearbyAndroidDevice.fromJson(e as Map<String, dynamic>?),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
NearbyDevice? mapToDevice(dynamic value) {
|
||||
return NearbyAndroidDevice.fromJson(Decoder.decodeMap(value));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:nearby_service/nearby_service.dart';
|
||||
|
||||
import 'socket_service/nearby_socket_service.dart';
|
||||
|
||||
class NearbyAndroidService extends NearbyService {
|
||||
late final _socketService = NearbySocketService(this);
|
||||
|
||||
@override
|
||||
ValueListenable<bool> get isCommunicationChannelConnecting {
|
||||
return _socketService.isConnecting;
|
||||
}
|
||||
|
||||
NearbyConnectionAndroidInfo? get connectionInfo {
|
||||
return _socketService.connectionInfo;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> initialize({
|
||||
NearbyInitializeData data = const NearbyInitializeData(),
|
||||
}) {
|
||||
return NearbyServiceAndroidPlatform.instance.initialize();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> discover() {
|
||||
return NearbyServiceAndroidPlatform.instance.discover();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> stopDiscovery() {
|
||||
return NearbyServiceAndroidPlatform.instance.stopDiscovery();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> connect(NearbyDevice device) {
|
||||
_requireAndroidDevice(device);
|
||||
return NearbyServiceAndroidPlatform.instance.connect(device.info.id);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> disconnect(NearbyDevice device) {
|
||||
_requireAndroidDevice(device);
|
||||
return NearbyServiceAndroidPlatform.instance.disconnect(device.info.id);
|
||||
}
|
||||
|
||||
Future<bool> requestPermissions() {
|
||||
return NearbyServiceAndroidPlatform.instance.requestPermissions();
|
||||
}
|
||||
|
||||
Future<bool> checkWifiService() {
|
||||
return NearbyServiceAndroidPlatform.instance.checkWifiService();
|
||||
}
|
||||
|
||||
Future<NearbyConnectionAndroidInfo?> getConnectionInfo() {
|
||||
return NearbyServiceAndroidPlatform.instance.getConnectionInfo();
|
||||
}
|
||||
|
||||
@override
|
||||
FutureOr<bool> startCommunicationChannel(
|
||||
NearbyCommunicationChannelData data,
|
||||
) {
|
||||
return _socketService.startSocket(data: data);
|
||||
}
|
||||
|
||||
@override
|
||||
FutureOr<bool> endCommunicationChannel() {
|
||||
return _socketService.cancel();
|
||||
}
|
||||
|
||||
@override
|
||||
FutureOr<bool> send(OutgoingNearbyMessage message) {
|
||||
return _socketService.send(message);
|
||||
}
|
||||
|
||||
void _requireAndroidDevice(NearbyDevice device) {
|
||||
assert(
|
||||
device is NearbyAndroidDevice,
|
||||
'The Nearby Android Service can only work with the NearbyAndroidDevice and not with ${device.runtimeType}',
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import 'package:nearby_service/src/platforms/android/models/nearby_connection_info.dart';
|
||||
import 'package:nearby_service/src/platforms/android/nearby_service_android_method_channel.dart';
|
||||
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
|
||||
|
||||
abstract class NearbyServiceAndroidPlatform extends PlatformInterface {
|
||||
NearbyServiceAndroidPlatform() : super(token: _token);
|
||||
|
||||
static final Object _token = Object();
|
||||
|
||||
static NearbyServiceAndroidPlatform _instance =
|
||||
MethodChannelAndroidNearbyService();
|
||||
|
||||
/// The default instance of [NearbyServiceAndroidPlatform] to use.
|
||||
///
|
||||
/// Defaults to [NearbyServiceAndroidPlatform].
|
||||
static NearbyServiceAndroidPlatform get instance => _instance;
|
||||
|
||||
/// Platform-specific implementations should set this with their own
|
||||
/// platform-specific class that extends [NearbyServiceAndroidPlatform] when
|
||||
/// they register themselves.
|
||||
static set instance(NearbyServiceAndroidPlatform instance) {
|
||||
PlatformInterface.verifyToken(instance, _token);
|
||||
_instance = instance;
|
||||
}
|
||||
|
||||
Future<bool> initialize() {
|
||||
throw UnimplementedError('initialize() has not been implemented.');
|
||||
}
|
||||
|
||||
Future<bool> requestPermissions() {
|
||||
throw UnimplementedError('requestPermissions() has not been implemented.');
|
||||
}
|
||||
|
||||
Future<bool> checkWifiService() {
|
||||
throw UnimplementedError('checkWifiService() has not been implemented.');
|
||||
}
|
||||
|
||||
Future<NearbyConnectionAndroidInfo?> getConnectionInfo() {
|
||||
throw UnimplementedError('getConnectionInfo() has not been implemented.');
|
||||
}
|
||||
|
||||
Future<bool> discover() {
|
||||
throw UnimplementedError('discover() has not been implemented.');
|
||||
}
|
||||
|
||||
Future<bool> stopDiscovery() {
|
||||
throw UnimplementedError('stopDiscovery() has not been implemented.');
|
||||
}
|
||||
|
||||
Future<bool> connect(String deviceAddress) {
|
||||
throw UnimplementedError('connect() has not been implemented.');
|
||||
}
|
||||
|
||||
Future<bool> disconnect(String deviceAddress) {
|
||||
throw UnimplementedError('disconnect() has not been implemented.');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:nearby_service/nearby_service.dart';
|
||||
import 'package:nearby_service/src/utils/logger.dart';
|
||||
|
||||
/// An implementation of [NearbyServiceAndroidPlatform] that uses method channels.
|
||||
class MethodChannelAndroidNearbyService extends NearbyServiceAndroidPlatform {
|
||||
/// The method channel used to interact with the native platform.
|
||||
@visibleForTesting
|
||||
final methodChannel = const MethodChannel('nearby_service');
|
||||
|
||||
@override
|
||||
Future<bool> initialize() async {
|
||||
return (await methodChannel.invokeMethod<bool>(
|
||||
'initialize',
|
||||
{"logLevel": Logger.level.name},
|
||||
)) ??
|
||||
false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> requestPermissions() async {
|
||||
return (await methodChannel.invokeMethod<bool>('requestPermissions')) ??
|
||||
false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> checkWifiService() async {
|
||||
return (await methodChannel.invokeMethod<bool>('checkWifiService')) ??
|
||||
false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<NearbyConnectionAndroidInfo?> getConnectionInfo() async {
|
||||
return NearbyConnectionInfoMapper.mapToInfo(
|
||||
await methodChannel.invokeMethod('getConnectionInfo'),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> discover() async {
|
||||
return (await methodChannel.invokeMethod<bool>('discover')) ?? false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> stopDiscovery() async {
|
||||
return (await methodChannel.invokeMethod<bool>('stopDiscovery')) ?? false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> connect(String deviceAddress) async {
|
||||
return (await methodChannel.invokeMethod<bool?>(
|
||||
"connect",
|
||||
{"deviceAddress": deviceAddress},
|
||||
)) ??
|
||||
false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> disconnect(String deviceAddress) async {
|
||||
return (await methodChannel.invokeMethod<bool?>(
|
||||
"disconnect",
|
||||
{"deviceAddress": deviceAddress},
|
||||
)) ??
|
||||
false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:nearby_service/nearby_service.dart';
|
||||
import 'package:nearby_service/src/utils/logger.dart';
|
||||
import 'package:nearby_service/src/utils/stream_mapper.dart';
|
||||
|
||||
part 'ping_manager.dart';
|
||||
|
||||
part 'network.dart';
|
||||
|
||||
class NearbySocketService {
|
||||
NearbySocketService(this._manager);
|
||||
|
||||
final NearbyAndroidService _manager;
|
||||
final _pingManager = NearbySocketPingManager();
|
||||
final _network = NearbyServiceNetwork();
|
||||
|
||||
final isConnecting = ValueNotifier(false);
|
||||
NearbyConnectionAndroidInfo? connectionInfo;
|
||||
|
||||
String? _connectedDeviceId;
|
||||
WebSocket? _socket;
|
||||
StreamSubscription<ReceivedNearbyMessage>? _messagesSubscription;
|
||||
|
||||
Future<bool> startSocket({
|
||||
required NearbyCommunicationChannelData data,
|
||||
}) async {
|
||||
isConnecting.value = true;
|
||||
_connectedDeviceId = data.connectedDeviceId;
|
||||
connectionInfo = await _manager.getConnectionInfo();
|
||||
if (connectionInfo != null && connectionInfo!.groupFormed) {
|
||||
final androidData = data.androidData;
|
||||
if (connectionInfo!.isGroupOwner) {
|
||||
await _startServerSubscription(
|
||||
serverListener: androidData.serverListener,
|
||||
socketListener: data.eventListener,
|
||||
info: connectionInfo!,
|
||||
port: androidData.port,
|
||||
);
|
||||
return true;
|
||||
} else {
|
||||
await _tryConnectClient(
|
||||
socketListener: data.eventListener,
|
||||
reconnectInterval: androidData.clientReconnectInterval,
|
||||
info: connectionInfo!,
|
||||
port: androidData.port,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Future<bool> send(OutgoingNearbyMessage message) async {
|
||||
if (_socket != null && message.receiver.id == _connectedDeviceId) {
|
||||
final sender = await _manager.getCurrentDevice();
|
||||
if (sender != null) {
|
||||
_socket!.add(
|
||||
jsonEncode(
|
||||
{
|
||||
'message': message.value,
|
||||
'sender': sender.info.toJson(),
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Future<bool> cancel() async {
|
||||
try {
|
||||
isConnecting.value = false;
|
||||
await _messagesSubscription?.cancel();
|
||||
_messagesSubscription = null;
|
||||
_socket = null;
|
||||
_connectedDeviceId = null;
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _tryConnectClient({
|
||||
required NearbyServiceStreamListener socketListener,
|
||||
required NearbyConnectionAndroidInfo info,
|
||||
required int port,
|
||||
required Duration reconnectInterval,
|
||||
}) async {
|
||||
final response = await _network.pingServer(
|
||||
address: info.ownerIpAddress,
|
||||
port: port,
|
||||
);
|
||||
|
||||
if (await _pingManager.checkPong(response)) {
|
||||
_socket = await _network.connectToSocket(
|
||||
ownerIpAddress: info.ownerIpAddress,
|
||||
port: port,
|
||||
);
|
||||
_createSocketSubscription(socketListener);
|
||||
} else {
|
||||
Logger.debug(
|
||||
'Retry to connect to the server in ${reconnectInterval.inSeconds}s',
|
||||
);
|
||||
Future.delayed(reconnectInterval, () {
|
||||
_tryConnectClient(
|
||||
socketListener: socketListener,
|
||||
reconnectInterval: reconnectInterval,
|
||||
info: info,
|
||||
port: port,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _startServerSubscription({
|
||||
required NearbyServiceStreamListener socketListener,
|
||||
required NearbyConnectionAndroidInfo info,
|
||||
required int port,
|
||||
ValueChanged<HttpRequest>? serverListener,
|
||||
}) async {
|
||||
final httpServer = await _network.startServer(
|
||||
ownerIpAddress: info.ownerIpAddress,
|
||||
port: port,
|
||||
);
|
||||
httpServer?.listen(
|
||||
(request) async {
|
||||
serverListener?.call(request);
|
||||
final isPing = await _pingManager.checkPing(request);
|
||||
if (isPing) {
|
||||
Logger.debug('Server got ping request');
|
||||
_network.pongClient(request);
|
||||
return;
|
||||
}
|
||||
|
||||
if (request.uri.path == '/ws') {
|
||||
_socket = await WebSocketTransformer.upgrade(request);
|
||||
_createSocketSubscription(socketListener);
|
||||
} else {
|
||||
request.response
|
||||
..statusCode = HttpStatus.notFound
|
||||
..close();
|
||||
Logger.error('Got unknown request ${request.requestedUri}');
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _createSocketSubscription(NearbyServiceStreamListener socketListener) {
|
||||
Logger.debug('Starting socket subscription');
|
||||
isConnecting.value = false;
|
||||
if (_connectedDeviceId != null) {
|
||||
_messagesSubscription = _socket
|
||||
?.map(MessagesStreamMapper.toMessage)
|
||||
.where((event) => event != null)
|
||||
.cast<ReceivedNearbyMessage>()
|
||||
.map((e) => MessagesStreamMapper.replaceId(e, _connectedDeviceId!))
|
||||
.listen(
|
||||
socketListener.onData,
|
||||
onDone: socketListener.onDone,
|
||||
onError: (e, s) {
|
||||
Logger.error(e);
|
||||
socketListener.onError?.call(e, s);
|
||||
},
|
||||
cancelOnError: socketListener.cancelOnError,
|
||||
);
|
||||
}
|
||||
if (_messagesSubscription != null) {
|
||||
Logger.info('Socket subscription was created successfully');
|
||||
socketListener.onCreated?.call(_messagesSubscription!);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
part of 'nearby_socket_service.dart';
|
||||
|
||||
class _Protocols {
|
||||
static const http = 'http://';
|
||||
static const ws = 'ws://';
|
||||
}
|
||||
|
||||
class NearbyServiceNetwork {
|
||||
final _httpClient = HttpClient();
|
||||
final _random = Random();
|
||||
|
||||
Future<HttpClientResponse?> pingServer({
|
||||
required String address,
|
||||
required int port,
|
||||
}) async {
|
||||
try {
|
||||
final url = Uri.parse('${_Protocols.http}$address:$port/');
|
||||
final request = (await _httpClient.postUrl(url))
|
||||
..write(_Commands.ping.name);
|
||||
final response = await request.close();
|
||||
return response;
|
||||
} catch (e) {
|
||||
Logger.error('Server is unreachable');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
void pongClient(HttpRequest request) {
|
||||
request.response
|
||||
..write(_Commands.pong.name)
|
||||
..close();
|
||||
Logger.debug('Sent pong to client');
|
||||
}
|
||||
|
||||
Future<HttpServer?> startServer({
|
||||
required String ownerIpAddress,
|
||||
required int port,
|
||||
}) async {
|
||||
try {
|
||||
final url = 'ws://$ownerIpAddress:$port';
|
||||
Logger.debug('Starting server on $url');
|
||||
var server = await HttpServer.bind(
|
||||
ownerIpAddress,
|
||||
port,
|
||||
shared: true,
|
||||
);
|
||||
Logger.info('Server running on $url');
|
||||
return server;
|
||||
} catch (e) {
|
||||
Logger.error('Error starting socket: $e');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<WebSocket?> connectToSocket({
|
||||
required String ownerIpAddress,
|
||||
required int port,
|
||||
}) async {
|
||||
try {
|
||||
final connectionId = _random.nextInt(1000) + 100;
|
||||
final url = '${_Protocols.ws}$ownerIpAddress:$port/ws?as=$connectionId';
|
||||
Logger.debug('Connecting to $url');
|
||||
final socket = await WebSocket.connect(url);
|
||||
Logger.info('Connected to $url');
|
||||
return socket;
|
||||
} catch (e) {
|
||||
Logger.error('Error connecting to server: $e');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
part of 'nearby_socket_service.dart';
|
||||
|
||||
enum _Commands { ping, pong }
|
||||
|
||||
class NearbySocketPingManager {
|
||||
Future<bool> checkPing(HttpRequest request) async {
|
||||
final body = await _getBody(request);
|
||||
return body == _Commands.ping.name;
|
||||
}
|
||||
|
||||
Future<bool> checkPong(HttpClientResponse? response) async {
|
||||
if (response == null) return false;
|
||||
|
||||
final body = await _getBody(response);
|
||||
return body == _Commands.pong.name;
|
||||
}
|
||||
|
||||
static Future<String?> _getBody(Stream<List<int>> data) {
|
||||
try {
|
||||
return utf8.decoder.bind(data).join();
|
||||
} catch (e) {
|
||||
return Future.value(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export 'models/nearby_device.dart';
|
||||
export 'nearby_ios_service.dart';
|
||||
export 'nearby_service_ios_interface.dart';
|
||||
@@ -0,0 +1,45 @@
|
||||
import 'package:nearby_service/nearby_service.dart';
|
||||
import 'package:nearby_service/src/utils/decoder.dart';
|
||||
|
||||
class NearbyIOSDevice extends NearbyDevice {
|
||||
NearbyIOSDevice({
|
||||
required super.info,
|
||||
required super.status,
|
||||
this.os,
|
||||
this.osVersion,
|
||||
this.deviceType,
|
||||
});
|
||||
|
||||
factory NearbyIOSDevice.fromJson(Map<String, dynamic>? json) {
|
||||
return NearbyIOSDevice(
|
||||
info: NearbyDeviceInfo.fromJson(json),
|
||||
deviceType: json?["deviceType"],
|
||||
os: json?["os"],
|
||||
osVersion: json?["osVersion"],
|
||||
status: NearbyDeviceStatus.fromIosCode(json?['state']),
|
||||
);
|
||||
}
|
||||
|
||||
final String? os;
|
||||
final String? osVersion;
|
||||
final String? deviceType;
|
||||
}
|
||||
|
||||
class NearbyIOSMapper implements NearbyDeviceMapper {
|
||||
@override
|
||||
List<NearbyDevice> mapToDeviceList(dynamic value) {
|
||||
final decoded = Decoder.decodeList(value);
|
||||
return [
|
||||
...?decoded?.map(
|
||||
(e) => NearbyIOSDevice.fromJson(e as Map<String, dynamic>?),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
NearbyDevice? mapToDevice(dynamic value) {
|
||||
return NearbyIOSDevice.fromJson(
|
||||
Decoder.decodeMap(value),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:nearby_service/nearby_service.dart';
|
||||
import 'package:nearby_service/src/utils/logger.dart';
|
||||
import 'package:nearby_service/src/utils/stream_mapper.dart';
|
||||
|
||||
class NearbyIOSService extends NearbyService {
|
||||
final _isBrowser = ValueNotifier<bool>(true);
|
||||
final _isCommunicationChannelConnecting = ValueNotifier<bool>(false);
|
||||
|
||||
StreamSubscription<ReceivedNearbyMessage>? _messagesSubscription;
|
||||
|
||||
@override
|
||||
ValueListenable<bool> get isCommunicationChannelConnecting =>
|
||||
_isCommunicationChannelConnecting;
|
||||
|
||||
ValueListenable<bool> get isBrowser => _isBrowser;
|
||||
|
||||
String get _currentConnectionType {
|
||||
return _isBrowser.value ? 'browsing' : 'advertising';
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> initialize({
|
||||
NearbyInitializeData data = const NearbyInitializeData(),
|
||||
}) async {
|
||||
final result = await NearbyServiceIOSPlatform.instance.initialize(
|
||||
data.iosDeviceName,
|
||||
);
|
||||
|
||||
_logResult(
|
||||
result,
|
||||
onSuccess: 'Initialized ${data.iosDeviceName}',
|
||||
onError: 'Failed to initialize ${data.iosDeviceName}',
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> discover() async {
|
||||
final result = _isBrowser.value
|
||||
? await NearbyServiceIOSPlatform.instance.startBrowsing()
|
||||
: await NearbyServiceIOSPlatform.instance.startAdvertising();
|
||||
_logResult(
|
||||
result,
|
||||
onSuccess: 'Started $_currentConnectionType',
|
||||
onError: 'Failed to start $_currentConnectionType',
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> stopDiscovery() async {
|
||||
final result = _isBrowser.value
|
||||
? await NearbyServiceIOSPlatform.instance.stopBrowsing()
|
||||
: await NearbyServiceIOSPlatform.instance.stopAdvertising();
|
||||
|
||||
_logResult(
|
||||
result,
|
||||
onSuccess: 'Stopped $_currentConnectionType',
|
||||
onError: 'Failed to stop $_currentConnectionType',
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> connect(NearbyDevice device) async {
|
||||
_requireIOSDevice(device);
|
||||
final result = _isBrowser.value
|
||||
? await NearbyServiceIOSPlatform.instance.invite(device.info.id)
|
||||
: await NearbyServiceIOSPlatform.instance.acceptInvite(device.info.id);
|
||||
|
||||
_logResult(
|
||||
result,
|
||||
onSuccess:
|
||||
'${_isBrowser.value ? 'Sent invitation to' : 'Accepted invitation from'} '
|
||||
'${device.info.id}',
|
||||
onError: 'Failed to connect to ${device.info.id}',
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> disconnect(NearbyDevice device) async {
|
||||
_requireIOSDevice(device);
|
||||
final result = await NearbyServiceIOSPlatform.instance.disconnect(
|
||||
device.info.id,
|
||||
);
|
||||
_logResult(
|
||||
result,
|
||||
onSuccess: 'Disconnected from ${device.info.id}',
|
||||
onError: 'Failed to disconnect from ${device.info.id}',
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
FutureOr<bool> startCommunicationChannel(
|
||||
NearbyCommunicationChannelData data,
|
||||
) async {
|
||||
Logger.debug('Creating messages subscription');
|
||||
_isCommunicationChannelConnecting.value = true;
|
||||
await endCommunicationChannel();
|
||||
final eventListener = data.eventListener;
|
||||
_messagesSubscription = NearbyServiceIOSPlatform.instance.messagesStream
|
||||
.map(MessagesStreamMapper.toMessage)
|
||||
.where((event) => event?.sender.id == data.connectedDeviceId)
|
||||
.where((event) => event != null)
|
||||
.cast<ReceivedNearbyMessage>()
|
||||
.listen(
|
||||
eventListener.onData,
|
||||
onDone: eventListener.onDone,
|
||||
onError: (e, s) {
|
||||
Logger.error(e);
|
||||
eventListener.onError?.call(e, s);
|
||||
},
|
||||
cancelOnError: eventListener.cancelOnError,
|
||||
);
|
||||
if (_messagesSubscription != null) {
|
||||
Logger.info('Messages subscription was created successfully');
|
||||
eventListener.onCreated?.call(_messagesSubscription!);
|
||||
}
|
||||
_isCommunicationChannelConnecting.value = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
FutureOr<bool> endCommunicationChannel() async {
|
||||
await _messagesSubscription?.cancel();
|
||||
_messagesSubscription = null;
|
||||
Logger.debug('Communication channel was cancelled');
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> send(OutgoingNearbyMessage message) {
|
||||
return NearbyServiceIOSPlatform.instance.send(message);
|
||||
}
|
||||
|
||||
Future<String?> getSavedDeviceName() {
|
||||
return NearbyServiceIOSPlatform.instance.getSavedDeviceName();
|
||||
}
|
||||
|
||||
void setIsBrowser({required bool value}) {
|
||||
Logger.debug('Is Browser Value was set to $value');
|
||||
_isBrowser.value = value;
|
||||
}
|
||||
|
||||
void _logResult(
|
||||
bool value, {
|
||||
required String onSuccess,
|
||||
required String onError,
|
||||
}) {
|
||||
if (value) {
|
||||
Logger.info(onSuccess);
|
||||
} else {
|
||||
Logger.error(onError);
|
||||
}
|
||||
}
|
||||
|
||||
void _requireIOSDevice(NearbyDevice device) {
|
||||
assert(
|
||||
device is NearbyIOSDevice,
|
||||
'The Nearby IOS Service can only work with the NearbyIOSDevice and not with ${device.runtimeType}',
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:nearby_service/nearby_service.dart';
|
||||
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
|
||||
|
||||
import 'nearby_service_ios_method_channel.dart';
|
||||
|
||||
abstract class NearbyServiceIOSPlatform extends PlatformInterface {
|
||||
NearbyServiceIOSPlatform() : super(token: _token);
|
||||
|
||||
static final Object _token = Object();
|
||||
|
||||
static NearbyServiceIOSPlatform _instance = MethodChannelIOSNearbyService();
|
||||
|
||||
/// The default instance of [NearbyServiceIOSPlatform] to use.
|
||||
static NearbyServiceIOSPlatform get instance => _instance;
|
||||
|
||||
/// Platform-specific implementations should set this with their own
|
||||
/// platform-specific class that extends [NearbyServiceIOSPlatform] when
|
||||
/// they register themselves.
|
||||
static set instance(NearbyServiceIOSPlatform instance) {
|
||||
PlatformInterface.verifyToken(instance, _token);
|
||||
_instance = instance;
|
||||
}
|
||||
|
||||
Stream get messagesStream {
|
||||
throw UnimplementedError('messagesStream() has not been implemented.');
|
||||
}
|
||||
|
||||
Future<bool> initialize([String? deviceName]) {
|
||||
throw UnimplementedError('initialize() has not been implemented.');
|
||||
}
|
||||
|
||||
Future<String?> getSavedDeviceName() {
|
||||
throw UnimplementedError('getSavedDeviceName() has not been implemented.');
|
||||
}
|
||||
|
||||
Future<bool> startBrowsing() {
|
||||
throw UnimplementedError('startBrowsing() has not been implemented.');
|
||||
}
|
||||
|
||||
Future<bool> startAdvertising() {
|
||||
throw UnimplementedError('startAdvertising() has not been implemented.');
|
||||
}
|
||||
|
||||
Future<bool> stopBrowsing() {
|
||||
throw UnimplementedError('stopBrowsing() has not been implemented.');
|
||||
}
|
||||
|
||||
Future<bool> stopAdvertising() {
|
||||
throw UnimplementedError('stopAdvertising() has not been implemented.');
|
||||
}
|
||||
|
||||
Future<bool> invite(String deviceId) {
|
||||
throw UnimplementedError('invite() has not been implemented.');
|
||||
}
|
||||
|
||||
Future<bool> acceptInvite(String deviceId) {
|
||||
throw UnimplementedError('acceptInvite() has not been implemented.');
|
||||
}
|
||||
|
||||
Future<bool> disconnect(String deviceId) {
|
||||
throw UnimplementedError('disconnect() has not been implemented.');
|
||||
}
|
||||
|
||||
Future<bool> send(OutgoingNearbyMessage message) {
|
||||
throw UnimplementedError('send() has not been implemented.');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:nearby_service/nearby_service.dart';
|
||||
|
||||
/// An implementation of [NearbyServiceIOSPlatform] that uses method channels.
|
||||
class MethodChannelIOSNearbyService extends NearbyServiceIOSPlatform {
|
||||
final messageReceiver = StreamController.broadcast();
|
||||
|
||||
@override
|
||||
Stream get messagesStream => messageReceiver.stream;
|
||||
|
||||
/// The method channel used to interact with the native platform.
|
||||
@visibleForTesting
|
||||
final methodChannel = const MethodChannel('nearby_service');
|
||||
|
||||
@override
|
||||
Future<bool> initialize([String? deviceName]) async {
|
||||
methodChannel.setMethodCallHandler((handler) async {
|
||||
switch (handler.method) {
|
||||
case 'invoke_nearby_service_message_received':
|
||||
messageReceiver.add(handler.arguments);
|
||||
break;
|
||||
}
|
||||
});
|
||||
return (await methodChannel.invokeMethod<bool>(
|
||||
'initialize',
|
||||
deviceName != null ? {"deviceName": deviceName} : null,
|
||||
) ??
|
||||
false);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String?> getSavedDeviceName() async {
|
||||
return (await methodChannel.invokeMethod<String?>('getSavedDeviceName'));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> startAdvertising() async {
|
||||
return (await methodChannel.invokeMethod<bool>('startAdvertising')) ??
|
||||
false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> startBrowsing() async {
|
||||
return (await methodChannel.invokeMethod<bool>('startBrowsing')) ?? false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> stopAdvertising() async {
|
||||
return (await methodChannel.invokeMethod<bool>('stopAdvertising')) ?? false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> stopBrowsing() async {
|
||||
return (await methodChannel.invokeMethod<bool>('stopBrowsing')) ?? false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> invite(String deviceId) async {
|
||||
return (await methodChannel.invokeMethod<bool?>(
|
||||
"invite",
|
||||
{"deviceId": deviceId},
|
||||
)) ??
|
||||
false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> acceptInvite(String deviceId) async {
|
||||
return (await methodChannel.invokeMethod<bool?>(
|
||||
"acceptInvite",
|
||||
{"deviceId": deviceId},
|
||||
)) ??
|
||||
false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> disconnect(String deviceId) async {
|
||||
return (await methodChannel.invokeMethod<bool?>(
|
||||
"disconnect",
|
||||
{"deviceId": deviceId},
|
||||
)) ??
|
||||
false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> send(OutgoingNearbyMessage message) async {
|
||||
return (await methodChannel.invokeMethod<bool?>(
|
||||
"send",
|
||||
message.toJson(),
|
||||
)) ??
|
||||
false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:nearby_service/nearby_service.dart';
|
||||
|
||||
class NearbyCommunicationChannelData<T> {
|
||||
const NearbyCommunicationChannelData(
|
||||
this.connectedDeviceId, {
|
||||
required this.eventListener,
|
||||
this.androidData = const NearbyAndroidCommunicationChannelData(),
|
||||
});
|
||||
|
||||
final String connectedDeviceId;
|
||||
final NearbyServiceStreamListener<ReceivedNearbyMessage> eventListener;
|
||||
final NearbyAndroidCommunicationChannelData androidData;
|
||||
}
|
||||
|
||||
class NearbyAndroidCommunicationChannelData {
|
||||
const NearbyAndroidCommunicationChannelData({
|
||||
this.clientReconnectInterval = const Duration(seconds: 5),
|
||||
this.serverListener,
|
||||
this.port = 4045,
|
||||
});
|
||||
|
||||
final Duration clientReconnectInterval;
|
||||
final ValueChanged<HttpRequest>? serverListener;
|
||||
final int port;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
class NearbyInitializeData {
|
||||
const NearbyInitializeData({this.iosDeviceName});
|
||||
|
||||
final String? iosDeviceName;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
class NearbyServiceStreamListener<T> {
|
||||
const NearbyServiceStreamListener({
|
||||
required this.onData,
|
||||
this.onCreated,
|
||||
this.onDone,
|
||||
this.onError,
|
||||
this.cancelOnError,
|
||||
});
|
||||
|
||||
final ValueChanged<T> onData;
|
||||
final ValueChanged<StreamSubscription<T>>? onCreated;
|
||||
final VoidCallback? onDone;
|
||||
final void Function(Object, [StackTrace])? onError;
|
||||
final bool? cancelOnError;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export 'communication_channel_data.dart';
|
||||
export 'nearby_service_stream_listener.dart';
|
||||
export 'initialize_data.dart';
|
||||
@@ -0,0 +1,33 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:nearby_service/nearby_service.dart';
|
||||
|
||||
class Decoder {
|
||||
Decoder._();
|
||||
|
||||
static Map<String, dynamic>? decodeMap(dynamic value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
if (value is String) {
|
||||
return jsonDecode(value) as Map<String, dynamic>;
|
||||
}
|
||||
if (value is Map<String, dynamic>) {
|
||||
return value;
|
||||
}
|
||||
throw NearbyServiceException.unsupportedDecoding(value);
|
||||
}
|
||||
|
||||
static List? decodeList(dynamic value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
if (value is String) {
|
||||
return jsonDecode(value) as List?;
|
||||
}
|
||||
if (value is List) {
|
||||
return value;
|
||||
}
|
||||
throw NearbyServiceException.unsupportedDecoding(value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:nearby_service/src/utils/logger.dart';
|
||||
|
||||
class NearbyServiceException implements Exception {
|
||||
NearbyServiceException(this.error) {
|
||||
Logger.error(error);
|
||||
}
|
||||
|
||||
factory NearbyServiceException.unsupportedPlatform({required String caller}) {
|
||||
return NearbyServiceException(
|
||||
'$caller is not supported for platform ${Platform.operatingSystem}',
|
||||
);
|
||||
}
|
||||
|
||||
factory NearbyServiceException.unsupportedDecoding(dynamic value) {
|
||||
return NearbyServiceException(
|
||||
'Got unknown value=$value with runtimeType=${value.runtimeType}',
|
||||
);
|
||||
}
|
||||
|
||||
final Object? error;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
enum NearbyServiceLogLevel {
|
||||
debug,
|
||||
info,
|
||||
error,
|
||||
disabled,
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:nearby_service/nearby_service.dart';
|
||||
|
||||
class Logger {
|
||||
Logger._();
|
||||
|
||||
static NearbyServiceLogLevel level =
|
||||
kDebugMode ? NearbyServiceLogLevel.debug : NearbyServiceLogLevel.error;
|
||||
|
||||
static void debug(String message) {
|
||||
if (level.index <= NearbyServiceLogLevel.debug.index) {
|
||||
debugPrint(
|
||||
_messageWrapper(
|
||||
message,
|
||||
androidColor: AndroidConsoleColor.grey,
|
||||
iosIcon: IOSConsoleIcon.settings,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static void info(String message) {
|
||||
if (level.index <= NearbyServiceLogLevel.info.index) {
|
||||
debugPrint(
|
||||
_messageWrapper(
|
||||
message,
|
||||
androidColor: AndroidConsoleColor.green,
|
||||
iosIcon: IOSConsoleIcon.success,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static void error(Object? error) {
|
||||
if (level.index <= NearbyServiceLogLevel.error.index) {
|
||||
debugPrint(
|
||||
_messageWrapper(
|
||||
'$error',
|
||||
androidColor: AndroidConsoleColor.red,
|
||||
iosIcon: IOSConsoleIcon.error,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static String _messageWrapper(
|
||||
String message, {
|
||||
required AndroidConsoleColor androidColor,
|
||||
required IOSConsoleIcon iosIcon,
|
||||
}) {
|
||||
if (Platform.isAndroid) {
|
||||
return '\x1B[${androidColor.value}m[NearbyService]: $message\x1B[0m';
|
||||
}
|
||||
if (Platform.isIOS) {
|
||||
return '[NearbyService ${iosIcon.value}]: $message ';
|
||||
}
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
||||
enum AndroidConsoleColor {
|
||||
grey(37),
|
||||
green(32),
|
||||
red(31);
|
||||
|
||||
const AndroidConsoleColor(this.value);
|
||||
|
||||
final int value;
|
||||
}
|
||||
|
||||
enum IOSConsoleIcon {
|
||||
settings('🛠'),
|
||||
success('✅'),
|
||||
error('❌');
|
||||
|
||||
const IOSConsoleIcon(this.value);
|
||||
|
||||
final String value;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import 'package:nearby_service/nearby_service.dart';
|
||||
import 'package:nearby_service/src/utils/decoder.dart';
|
||||
|
||||
import 'logger.dart';
|
||||
|
||||
abstract class MessagesStreamMapper {
|
||||
static ReceivedNearbyMessage replaceId(
|
||||
ReceivedNearbyMessage message,
|
||||
String id,
|
||||
) {
|
||||
return ReceivedNearbyMessage(
|
||||
value: message.value,
|
||||
sender: NearbyDeviceInfo(
|
||||
id: id,
|
||||
displayName: message.sender.displayName,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static ReceivedNearbyMessage? toMessage(dynamic event) {
|
||||
try {
|
||||
final decoded = Decoder.decodeMap(event);
|
||||
return ReceivedNearbyMessage.fromJson(decoded);
|
||||
} catch (e) {
|
||||
Logger.debug(
|
||||
'Can\'t convert $event to ReceivedNearbyMessage',
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export 'exception.dart';
|
||||
export 'log_level.dart';
|
||||
Reference in New Issue
Block a user