import 'dart:async'; import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:nearby_service/nearby_service.dart'; import 'package:nearby_service_example_full/utils/files_saver.dart'; import 'app_state.dart'; class AppService extends ChangeNotifier { late final _nearbyService = NearbyService.getInstance() ..communicationChannelState.addListener(notifyListeners); AppState state = AppState.idle; List? peers; NearbyDevice? connectedDevice; NearbyDeviceInfo? currentDeviceInfo; NearbyConnectionAndroidInfo? _connectionAndroidInfo; String platformVersion = 'Unknown'; String platformModel = 'Unknown'; StreamSubscription? _peersSubscription; StreamSubscription? _connectedDeviceSubscription; StreamSubscription? _connectionInfoSubscription; final filesLoadings = {}; @override void dispose() { stopListeningAll(); super.dispose(); } Future getPlatformInfo() async { platformVersion = await _nearbyService.getPlatformVersion() ?? 'Unknown'; platformModel = await _nearbyService.getPlatformModel() ?? 'Unknown'; notifyListeners(); } Future getSavedIOSDeviceName() async { return (await _nearbyService.ios?.getSavedDeviceName()) ?? platformModel; } Future initialize(String? iosDeviceName) async { try { await _nearbyService.initialize( data: NearbyInitializeData(iosDeviceName: iosDeviceName), ); updateState( Platform.isAndroid ? AppState.permissions : AppState.selectClientType, ); } catch (e) { if (kDebugMode) { print(e); } } finally { notifyListeners(); } } Future getCurrentDeviceInfo() async { try { currentDeviceInfo = await _nearbyService.getCurrentDeviceInfo(); } catch (e) { if (kDebugMode) { print(e); } } } Future requestPermissions() async { try { final result = await _nearbyService.android?.requestPermissions(); if (result ?? false) { updateState(AppState.checkServices); } } catch (e) { if (kDebugMode) { print(e); } } } Future checkWifiService() async { final result = await _nearbyService.android?.checkWifiService(); if (result ?? false) { updateState(AppState.readyToDiscover); startListeningConnectionInfo(); return true; } return false; } Future openServicesSettings() async { await _nearbyService.openServicesSettings(); } void setIsBrowser({required bool value}) { _nearbyService.ios?.setIsBrowser(value: value); updateState(AppState.readyToDiscover); } Future discover() async { try { await getCurrentDeviceInfo(); final result = await _nearbyService.discover(); if (result) { updateState(AppState.discoveringPeers); } } catch (e) { if (kDebugMode) { print(e); } } } Future stopDiscovery() async { try { final result = await _nearbyService.stopDiscovery(); if (result) { updateState(AppState.readyToDiscover); } } catch (e) { if (kDebugMode) { print(e); } } } Future connect(NearbyDevice device) async { try { await _nearbyService.connect(device); } catch (e) { if (kDebugMode) { print(e); } } notifyListeners(); } Future disconnect([NearbyDevice? device]) async { try { await _nearbyService.disconnect(device); } catch (e) { if (kDebugMode) { print(e); } } finally { await stopListeningAll(); } notifyListeners(); } Future stopListeningAll() async { await endCommunicationChannel(); await stopListeningConnectedDevice(); await stopListeningPeers(); await stopListeningConnectionInfo(); await stopDiscovery(); } void updateState(AppState state, {bool shouldNotify = true}) { this.state = state; if (shouldNotify) { notifyListeners(); } } void _notify() => notifyListeners(); } extension GettersExtension on AppService { CommunicationChannelState get communicationChannelState { return _nearbyService.communicationChannelState.value; } bool get isIOSBrowser { return _nearbyService.ios?.isBrowser.value ?? false; } bool? get isAndroidGroupOwner { return _connectionAndroidInfo?.isGroupOwner; } } extension ConnectionInfoExtension on AppService { void startListeningConnectionInfo() { try { _connectionInfoSubscription = _nearbyService.android?.getConnectionInfoStream().listen( (event) async { _connectionAndroidInfo = event; _notify(); }, ); } catch (e) { if (kDebugMode) { print(e); } } _notify(); } Future stopListeningConnectionInfo() async { await _connectionInfoSubscription?.cancel(); _connectionInfoSubscription = null; } } extension PeersExtension on AppService { Future startListeningPeers() async { try { _peersSubscription = _nearbyService.getPeersStream().listen( (event) { peers = event; _notify(); }, ); updateState(AppState.streamingPeers); } catch (e) { if (kDebugMode) { print(e); } } } Future stopListeningPeers() async { await _peersSubscription?.cancel(); peers = null; updateState(AppState.discoveringPeers); } } extension ConnectedDeviceExtension on AppService { Future startListeningConnectedDevice(NearbyDevice device) async { updateState(AppState.loadingConnection); try { _connectedDeviceSubscription = _nearbyService.getConnectedDeviceStream(device).listen( (event) async { final wasConnected = connectedDevice?.status.isConnected ?? false; final nowConnected = event?.status.isConnected ?? false; if (wasConnected && !nowConnected) { stopListeningAll(); return; } connectedDevice = event; if (connectedDevice != null && state != AppState.connected && state != AppState.communicationChannelCreated) { updateState(AppState.connected); } _notify(); }, ); } catch (e) { updateState(AppState.streamingPeers, shouldNotify: false); } _notify(); } Future stopListeningConnectedDevice() async { await _connectedDeviceSubscription?.cancel(); await _nearbyService.endCommunicationChannel(); _connectedDeviceSubscription = null; connectedDevice = null; _notify(); } } extension CommunicationChannelExtension on AppService { Future startCommunicationChannel({ ValueChanged? listener, ValueChanged? onFilesSaved, }) async { final messagesListener = NearbyServiceMessagesListener( onCreated: () { updateState(AppState.communicationChannelCreated); }, onData: (event) { listener?.call(event); }, onError: (e, [StackTrace? s]) { stopListeningAll(); }, ); final filesListener = NearbyServiceFilesListener( onData: (event) async { final files = await FilesSaver.savePack(event); onFilesSaved?.call( ReceivedNearbyFilesPack( id: event.id, sender: event.sender, files: files, ), ); }, ); await _nearbyService.startCommunicationChannel( NearbyCommunicationChannelData( connectedDevice!.info.id, messagesListener: messagesListener, filesListener: filesListener, ), ); } Future endCommunicationChannel() async { try { await _nearbyService.endCommunicationChannel(); } catch (e) { if (kDebugMode) { print(e); } } _notify(); } } extension MessagingExtension on AppService { void sendTextRequest(String message) { if (connectedDevice == null) return; _nearbyService.send( OutgoingNearbyMessage( content: NearbyMessageTextRequest.create(value: message), receiver: connectedDevice!.info, ), ); } void sendFilesRequest(List paths) { if (connectedDevice == null) return; _nearbyService.send( OutgoingNearbyMessage( content: NearbyMessageFilesRequest.create( files: [ ...paths.map((e) => NearbyFileInfo(path: e)), ], ), receiver: connectedDevice!.info, ), ); } void sendTextResponse(String requestId) { if (connectedDevice == null) return; _nearbyService.send( OutgoingNearbyMessage( receiver: connectedDevice!.info, content: NearbyMessageTextResponse(id: requestId), ), ); } void sendFilesResponse( NearbyMessageFilesRequest request, { required bool isAccepted, }) { if (connectedDevice == null) return; _nearbyService.send( OutgoingNearbyMessage( receiver: connectedDevice!.info, content: NearbyMessageFilesResponse( id: request.id, isAccepted: isAccepted, ), ), ); if (isAccepted) { setFilesLoading(request); } } void setFilesLoading(NearbyMessageFilesRequest request) { filesLoadings[request.id] = request.files.length; _notify(); } void endFilesLoading(ReceivedNearbyFilesPack pack) { filesLoadings.remove(pack.id); _notify(); } }