[Android, iOS]: Initialization check, no disconnect on closing (#9)

* feat(android, dart): add check for initialization, show proper error messages

* feat(exception): add exception mapper for android and ios

* feat(ios): check initialization in swift

* fix(android): add initialization check for methods, remove disconnect() inside onDetachedFromEngine

* chore: code style, version, changelog
This commit is contained in:
Kseniia Nikitina
2024-08-09 21:22:58 +02:00
committed by GitHub
parent 906e87c6a9
commit a5c77f58e5
22 changed files with 306 additions and 102 deletions
+5
View File
@@ -1,3 +1,8 @@
## 0.0.9
- Add initialization checks for Android and IOS
- Fix issue for Android platform: https://github.com/ksenia312/nearby_service/issues/8
## 0.0.8 ## 0.0.8
- Add `cancelLastConnectionProcess` for Android manager - Add `cancelLastConnectionProcess` for Android manager
+4 -2
View File
@@ -4,8 +4,10 @@
#### Connecting phones in a P2P network #### Connecting phones in a P2P network
[![Xenikii Website](https://img.shields.io/badge/-xenikii.one-1D5FA7?style=flat&logoColor=white)](https://xenikii.one) [![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-4577d9)](https://github.com/ksenia312/nearby_service/blob/main/LICENSE) [![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)
Nearby Service Flutter Plugin is used to create connections in a P2P network. Nearby Service Flutter Plugin is used to create connections in a P2P network.
The plugin supports sending text messages and files. With it, The plugin supports sending text messages and files. With it,
@@ -0,0 +1,12 @@
package com.xenikii.nearby_service
class ErrorCodes {
companion object {
const val P2P_UNSUPPORTED = "P2P_UNSUPPORTED"
const val ERROR = "ERROR"
const val BUSY = "BUSY"
const val NO_SERVICE_REQUESTS = "NO_SERVICE_REQUESTS"
const val UNKNOWN = "UNKNOWN"
const val NO_INITIALIZATION = "NO_INITIALIZATION"
}
}
@@ -87,6 +87,8 @@ class NearbyServiceManager(private var context: Context) {
* It also may be null. * It also may be null.
*/ */
fun getCurrentDevice(result: Result) { fun getCurrentDevice(result: Result) {
if (!checkInitialization(result)) return
try { try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
wifiManager.requestDeviceInfo(wifiChannel) { device -> wifiManager.requestDeviceInfo(wifiChannel) { device ->
@@ -119,6 +121,8 @@ class NearbyServiceManager(private var context: Context) {
* All permissions from [NearbyServicePermissionsHandler] are required. * All permissions from [NearbyServicePermissionsHandler] are required.
*/ */
fun discover(result: Result) { fun discover(result: Result) {
if (!checkInitialization(result)) return
try { try {
wifiManager.discoverPeers( wifiManager.discoverPeers(
wifiChannel, getActionListener( wifiChannel, getActionListener(
@@ -139,6 +143,8 @@ class NearbyServiceManager(private var context: Context) {
* Stop discovery for peers in Wi-fi Direct scope. * Stop discovery for peers in Wi-fi Direct scope.
*/ */
fun stopDiscovery(result: Result) { fun stopDiscovery(result: Result) {
if (!checkInitialization(result)) return
wifiManager.stopPeerDiscovery( wifiManager.stopPeerDiscovery(
wifiChannel, getActionListener( wifiChannel, getActionListener(
result, result,
@@ -152,6 +158,8 @@ class NearbyServiceManager(private var context: Context) {
* Returns peers from [NearbyServiceBroadcastReceiver]. * Returns peers from [NearbyServiceBroadcastReceiver].
*/ */
fun getPeers(result: Result) { fun getPeers(result: Result) {
if (!checkInitialization(result)) return
result.success(receiver.peers) result.success(receiver.peers)
} }
@@ -159,6 +167,8 @@ class NearbyServiceManager(private var context: Context) {
* Returns connection info from [NearbyServiceBroadcastReceiver] in json string. * Returns connection info from [NearbyServiceBroadcastReceiver] in json string.
*/ */
fun getConnectionInfo(result: Result) { fun getConnectionInfo(result: Result) {
if (!checkInitialization(result)) return
val info = receiver.wifiInfo?.toJsonString() val info = receiver.wifiInfo?.toJsonString()
result.success(info) result.success(info)
} }
@@ -167,6 +177,8 @@ class NearbyServiceManager(private var context: Context) {
* Connects to provided [deviceAddress] in Wi-fi Direct scope. * Connects to provided [deviceAddress] in Wi-fi Direct scope.
*/ */
fun connect(result: Result, deviceAddress: String) { fun connect(result: Result, deviceAddress: String) {
if (!checkInitialization(result)) return
val config = WifiP2pConfig() val config = WifiP2pConfig()
if (receiver.connectedDevice?.deviceAddress == deviceAddress) { if (receiver.connectedDevice?.deviceAddress == deviceAddress) {
Logger.i("Already connected to the device $deviceAddress") Logger.i("Already connected to the device $deviceAddress")
@@ -196,6 +208,8 @@ class NearbyServiceManager(private var context: Context) {
* Disconnect from a previous device in Wi-fi Direct scope. * Disconnect from a previous device in Wi-fi Direct scope.
*/ */
fun disconnect(result: Result? = null) { fun disconnect(result: Result? = null) {
if (!checkInitialization(result)) return
val actionListener = getActionListener( val actionListener = getActionListener(
result, "Disconnected from last device", "Failed to disconnect" result, "Disconnected from last device", "Failed to disconnect"
) )
@@ -204,6 +218,8 @@ class NearbyServiceManager(private var context: Context) {
} }
fun cancelConnect(result: Result? = null) { fun cancelConnect(result: Result? = null) {
if (!checkInitialization(result)) return
val actionListener = getActionListener( val actionListener = getActionListener(
result, result,
"Last connection request was cancelled", "Last connection request was cancelled",
@@ -240,6 +256,31 @@ class NearbyServiceManager(private var context: Context) {
} }
} }
private fun checkInitialization(result: Result?, shouldLog: Boolean = true): Boolean {
try {
if (!::wifiManager.isInitialized) {
Logger.e("WifiManager is not initialized. Please call 'initialize()' first")
result?.success(ErrorCodes.NO_INITIALIZATION)
return false
}
if (!::wifiChannel.isInitialized) {
Logger.e("WifiChannel is not initialized. Please call 'initialize()' first")
result?.success(ErrorCodes.NO_INITIALIZATION)
return false
}
if (!::receiver.isInitialized) {
Logger.e("Broadcast Receiver is not initialized. Please call 'initialize()' first")
result?.success(ErrorCodes.NO_INITIALIZATION)
return false
}
} catch (e: Exception) {
Logger.e("Failed to check initialization, please call 'initialize()' first")
result?.success(ErrorCodes.NO_INITIALIZATION)
return false
}
return true
}
private fun getActionListener( private fun getActionListener(
result: Result?, result: Result?,
successMessage: String? = null, successMessage: String? = null,
@@ -262,11 +303,11 @@ class NearbyServiceManager(private var context: Context) {
else -> "An unknown error occurred. Please check the device's Wi-Fi P2P settings and ensure the device supports Wi-Fi P2P." else -> "An unknown error occurred. Please check the device's Wi-Fi P2P settings and ensure the device supports Wi-Fi P2P."
} }
val stringifyReasonCode = when (reasonCode) { val stringifyReasonCode = when (reasonCode) {
WifiP2pManager.P2P_UNSUPPORTED -> "P2P_UNSUPPORTED" WifiP2pManager.P2P_UNSUPPORTED -> ErrorCodes.P2P_UNSUPPORTED
WifiP2pManager.ERROR -> "ERROR" WifiP2pManager.ERROR -> ErrorCodes.ERROR
WifiP2pManager.BUSY -> "BUSY" WifiP2pManager.BUSY -> ErrorCodes.BUSY
WifiP2pManager.NO_SERVICE_REQUESTS -> "NO_SERVICE_REQUESTS" WifiP2pManager.NO_SERVICE_REQUESTS -> ErrorCodes.NO_SERVICE_REQUESTS
else -> "UNKNOWN" else -> ErrorCodes.UNKNOWN
} }
Logger.e("$errorMessage, Reason code: $reasonCode, Reason: $reason") Logger.e("$errorMessage, Reason code: $reasonCode, Reason: $reason")
result?.success(stringifyReasonCode) result?.success(stringifyReasonCode)
@@ -281,6 +322,8 @@ class NearbyServiceManager(private var context: Context) {
val postCallback = object : Runnable { val postCallback = object : Runnable {
override fun run() { override fun run() {
if (!checkInitialization(null, false)) return
handler.post { eventSink?.success("${receiver.peers}") } handler.post { eventSink?.success("${receiver.peers}") }
handler.postDelayed(this, 1000) handler.postDelayed(this, 1000)
} }
@@ -306,6 +349,8 @@ class NearbyServiceManager(private var context: Context) {
val postCallback = object : Runnable { val postCallback = object : Runnable {
override fun run() { override fun run() {
if (!checkInitialization(null, false)) return
handler.post { eventSink?.success(receiver.connectedDevice?.toJsonString()) } handler.post { eventSink?.success(receiver.connectedDevice?.toJsonString()) }
handler.postDelayed(this, 1000) handler.postDelayed(this, 1000)
} }
@@ -330,6 +375,8 @@ class NearbyServiceManager(private var context: Context) {
val postCallback = object : Runnable { val postCallback = object : Runnable {
override fun run() { override fun run() {
if (!checkInitialization(null, false)) return
handler.post { eventSink?.success(receiver.wifiInfo?.toJsonString()) } handler.post { eventSink?.success(receiver.wifiInfo?.toJsonString()) }
handler.postDelayed(this, 1000) handler.postDelayed(this, 1000)
} }
@@ -177,7 +177,6 @@ class NearbyServicePlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
channel.setMethodCallHandler(null) channel.setMethodCallHandler(null)
peersChannel.setStreamHandler(null) peersChannel.setStreamHandler(null)
connectedDeviceChannel.setStreamHandler(null) connectedDeviceChannel.setStreamHandler(null)
manager.disconnect()
} }
override fun onAttachedToActivity(binding: ActivityPluginBinding) { override fun onAttachedToActivity(binding: ActivityPluginBinding) {
+32
View File
@@ -30,6 +30,8 @@ class NearbyManager: NSObject {
} }
func getCurrentDevice(result: @escaping FlutterResult) { func getCurrentDevice(result: @escaping FlutterResult) {
if (!checkInitialization(result: result)) { return }
result(device.toDartFormat()) result(device.toDartFormat())
} }
@@ -43,32 +45,44 @@ class NearbyManager: NSObject {
} }
func startAdvertising(result: @escaping FlutterResult) { func startAdvertising(result: @escaping FlutterResult) {
if (!checkInitialization(result: result)) { return }
self.advertiser.startAdvertisingPeer() self.advertiser.startAdvertisingPeer()
result(true) result(true)
} }
func startBrowsing(result: @escaping FlutterResult) { func startBrowsing(result: @escaping FlutterResult) {
if (!checkInitialization(result: result)) { return }
self.browser.startBrowsingForPeers() self.browser.startBrowsingForPeers()
result(true) result(true)
} }
func stopAdvertising(result: @escaping FlutterResult) { func stopAdvertising(result: @escaping FlutterResult) {
if (!checkInitialization(result: result)) { return }
self.advertiser.stopAdvertisingPeer() self.advertiser.stopAdvertisingPeer()
NearbyDevicesStore.instance.clear() NearbyDevicesStore.instance.clear()
result(true) result(true)
} }
func stopBrowsing(result: @escaping FlutterResult) { func stopBrowsing(result: @escaping FlutterResult) {
if (!checkInitialization(result: result)) { return }
self.browser.stopBrowsingForPeers() self.browser.stopBrowsingForPeers()
NearbyDevicesStore.instance.clear() NearbyDevicesStore.instance.clear()
result(true) result(true)
} }
func getPeers(result: @escaping FlutterResult) { func getPeers(result: @escaping FlutterResult) {
if (!checkInitialization(result: result)) { return }
result(NearbyDevicesStore.instance.toDartFormat()) result(NearbyDevicesStore.instance.toDartFormat())
} }
func invite(for deviceId: String, result: @escaping FlutterResult) { func invite(for deviceId: String, result: @escaping FlutterResult) {
if (!checkInitialization(result: result)) { return }
do { do {
let device = NearbyDevicesStore.instance.find(for: deviceId) let device = NearbyDevicesStore.instance.find(for: deviceId)
if let requireDevice = device { if let requireDevice = device {
@@ -87,6 +101,8 @@ class NearbyManager: NSObject {
} }
} }
func acceptInvite(for deviceId: String, result: @escaping FlutterResult) { func acceptInvite(for deviceId: String, result: @escaping FlutterResult) {
if (!checkInitialization(result: result)) { return }
let device = NearbyDevicesStore.instance.find(for: deviceId) let device = NearbyDevicesStore.instance.find(for: deviceId)
if let requireDevice = device { if let requireDevice = device {
let nearbySession = requireDevice.createSession(for: self.device.peerID) let nearbySession = requireDevice.createSession(for: self.device.peerID)
@@ -96,12 +112,16 @@ class NearbyManager: NSObject {
} }
func disconnect(for deviceId: String, result: @escaping FlutterResult) { func disconnect(for deviceId: String, result: @escaping FlutterResult) {
if (!checkInitialization(result: result)) { return }
let device = NearbyDevicesStore.instance.find(for: deviceId) let device = NearbyDevicesStore.instance.find(for: deviceId)
device?.deleteSession() device?.deleteSession()
result(true) result(true)
} }
func send(for content: NearbyMessageContent, with receiverId: String, result: @escaping FlutterResult) { func send(for content: NearbyMessageContent, with receiverId: String, result: @escaping FlutterResult) {
if (!checkInitialization(result: result)) { return }
let device = NearbyDevicesStore.instance.find(for: receiverId) let device = NearbyDevicesStore.instance.find(for: receiverId)
do { do {
@@ -158,6 +178,18 @@ class NearbyManager: NSObject {
Logger.error(message: error.localizedDescription) Logger.error(message: error.localizedDescription)
} }
} }
func checkInitialization(result: @escaping FlutterResult) -> Bool {
guard let _ = self.device,
let _ = self.advertiser,
let _ = self.browser else {
Logger.error(message: "NearbyManager is not initialized. Please call 'initialize()' first")
result(ERROR_NO_INITIALIZATION)
return false
}
return true
}
} }
extension NearbyManager: MCNearbyServiceAdvertiserDelegate { extension NearbyManager: MCNearbyServiceAdvertiserDelegate {
+2
View File
@@ -16,3 +16,5 @@ let ON_RESOURCE_RECEIVED = Notification.Name("NearbySessionOnResourceReceived")
let DART_COMMAND_MESSAGE_RECEIVED = "invoke_nearby_service_message_received" let DART_COMMAND_MESSAGE_RECEIVED = "invoke_nearby_service_message_received"
let DART_COMMAND_RESOURCES_RECEIVED = "invoke_nearby_service_resources_received" let DART_COMMAND_RESOURCES_RECEIVED = "invoke_nearby_service_resources_received"
let ERROR_NO_INITIALIZATION = "NO_INITIALIZATION"
+23 -16
View File
@@ -3,6 +3,7 @@ import 'package:flutter/services.dart';
import 'package:nearby_service/nearby_service.dart'; import 'package:nearby_service/nearby_service.dart';
import 'nearby_service_platform_interface.dart'; import 'nearby_service_platform_interface.dart';
import 'src/utils/result_handler.dart';
/// An implementation of [NearbyServicePlatform] that uses method channels. /// An implementation of [NearbyServicePlatform] that uses method channels.
class MethodChannelNearbyService extends NearbyServicePlatform { class MethodChannelNearbyService extends NearbyServicePlatform {
@@ -11,22 +12,24 @@ class MethodChannelNearbyService extends NearbyServicePlatform {
final methodChannel = const MethodChannel('nearby_service'); final methodChannel = const MethodChannel('nearby_service');
@override @override
Future<String?> getPlatformVersion() { Future<String?> getPlatformVersion() async {
return methodChannel.invokeMethod<String>('getPlatformVersion'); final result = await methodChannel.invokeMethod<String>(
'getPlatformVersion',
);
return ResultHandler.instance.handle<String?>(result);
} }
@override @override
Future<String?> getPlatformModel() { Future<String?> getPlatformModel() async {
return methodChannel.invokeMethod<String>('getPlatformModel'); final result = await methodChannel.invokeMethod<String>('getPlatformModel');
return ResultHandler.instance.handle<String?>(result);
} }
@override @override
Future<NearbyDeviceInfo?> getCurrentDeviceInfo() async { Future<NearbyDeviceInfo?> getCurrentDeviceInfo() async {
return NearbyDeviceMapper.instance final result = await methodChannel.invokeMethod('getCurrentDevice');
.mapToDevice( final updatedResult = ResultHandler.instance.handle(result);
await methodChannel.invokeMethod('getCurrentDevice'), return NearbyDeviceMapper.instance.mapToDevice(updatedResult)?.info;
)
?.info;
} }
@override @override
@@ -36,16 +39,17 @@ class MethodChannelNearbyService extends NearbyServicePlatform {
@override @override
Future<List<NearbyDevice>> getPeers() async { Future<List<NearbyDevice>> getPeers() async {
return NearbyDeviceMapper.instance.mapToDeviceList( final result = await methodChannel.invokeMethod('getPeers');
await methodChannel.invokeMethod('getPeers'), final updatedResult = ResultHandler.instance.handle(result);
); return NearbyDeviceMapper.instance.mapToDeviceList(updatedResult);
} }
@override @override
Stream<List<NearbyDevice>> getPeersStream() { Stream<List<NearbyDevice>> getPeersStream() {
const peersChannel = EventChannel("nearby_service_peers"); const peersChannel = EventChannel("nearby_service_peers");
return peersChannel.receiveBroadcastStream().map((e) { return peersChannel.receiveBroadcastStream().map((e) {
return NearbyDeviceMapper.instance.mapToDeviceList(e); final updatedResult = ResultHandler.instance.handle(e);
return NearbyDeviceMapper.instance.mapToDeviceList(updatedResult);
}); });
} }
@@ -54,8 +58,11 @@ class MethodChannelNearbyService extends NearbyServicePlatform {
const connectedDeviceChannel = EventChannel( const connectedDeviceChannel = EventChannel(
"nearby_service_connected_device", "nearby_service_connected_device",
); );
return connectedDeviceChannel.receiveBroadcastStream(device.info.id).map( return connectedDeviceChannel
(e) => NearbyDeviceMapper.instance.mapToDevice(e), .receiveBroadcastStream(device.info.id)
); .map((e) {
final updatedResult = ResultHandler.instance.handle(e);
return NearbyDeviceMapper.instance.mapToDevice(updatedResult);
});
} }
} }
@@ -0,0 +1,22 @@
import 'dart:io';
import 'package:nearby_service/nearby_service.dart';
import 'package:nearby_service/src/platforms/android/utils/mapper.dart';
import 'package:nearby_service/src/platforms/ios/utils/mapper.dart';
abstract class NearbyServiceExceptionMapper {
static NearbyServiceExceptionMapper get instance {
if (Platform.isAndroid) {
return NearbyServiceAndroidExceptionMapper();
} else if (Platform.isIOS) {
return NearbyServiceIOSExceptionMapper();
}
throw NearbyServiceException.unsupportedPlatform(
caller: 'NearbyServiceExceptionMapper',
);
}
bool canMap(String error);
NearbyServiceException map(String error);
}
+1 -1
View File
@@ -1,4 +1,4 @@
import 'package:nearby_service/src/utils/unknown.dart'; import 'package:nearby_service/src/utils/constants.dart';
/// ///
/// Minimal information about the device. /// Minimal information about the device.
@@ -1,5 +1,5 @@
import 'package:nearby_service/src/utils/constants.dart';
import 'package:nearby_service/src/utils/json_decoder.dart'; import 'package:nearby_service/src/utils/json_decoder.dart';
import 'package:nearby_service/src/utils/unknown.dart';
/// ///
/// The class representing the connection information /// The class representing the connection information
@@ -1,6 +1,6 @@
import 'package:nearby_service/nearby_service.dart'; import 'package:nearby_service/nearby_service.dart';
import 'package:nearby_service/src/utils/constants.dart';
import 'package:nearby_service/src/utils/json_decoder.dart'; import 'package:nearby_service/src/utils/json_decoder.dart';
import 'package:nearby_service/src/utils/unknown.dart';
/// ///
/// A device on a P2P network obtained from the Android platform. /// A device on a P2P network obtained from the Android platform.
@@ -2,8 +2,7 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:nearby_service/nearby_service.dart'; import 'package:nearby_service/nearby_service.dart';
import 'package:nearby_service/src/utils/logger.dart'; import 'package:nearby_service/src/utils/logger.dart';
import 'package:nearby_service/src/utils/result_handler.dart';
import 'utils/mapper.dart';
/// An implementation of [NearbyServiceAndroidPlatform] that uses method channels. /// An implementation of [NearbyServiceAndroidPlatform] that uses method channels.
class MethodChannelAndroidNearbyService extends NearbyServiceAndroidPlatform { class MethodChannelAndroidNearbyService extends NearbyServiceAndroidPlatform {
@@ -13,42 +12,44 @@ class MethodChannelAndroidNearbyService extends NearbyServiceAndroidPlatform {
@override @override
Future<bool> initialize() async { Future<bool> initialize() async {
return (await methodChannel.invokeMethod<bool>( final result = await methodChannel.invokeMethod(
'initialize', 'initialize',
{"logLevel": Logger.level.name}, {"logLevel": Logger.level.name},
)) ?? );
false;
return ResultHandler.instance.handle<bool?>(result) ?? false;
} }
@override @override
Future<bool> requestPermissions() async { Future<bool> requestPermissions() async {
return (await methodChannel.invokeMethod<bool>('requestPermissions')) ?? final result = await methodChannel.invokeMethod('requestPermissions');
false; return ResultHandler.instance.handle<bool?>(result) ?? false;
} }
@override @override
Future<bool> checkWifiService() async { Future<bool> checkWifiService() async {
return (await methodChannel.invokeMethod<bool>('checkWifiService')) ?? final result = await methodChannel.invokeMethod('checkWifiService');
false; return ResultHandler.instance.handle<bool?>(result) ?? false;
} }
@override @override
Future<NearbyConnectionAndroidInfo?> getConnectionInfo() async { Future<NearbyConnectionAndroidInfo?> getConnectionInfo() async {
return NearbyConnectionInfoMapper.mapToInfo( final result = ResultHandler.instance.handle(
await methodChannel.invokeMethod('getConnectionInfo'), await methodChannel.invokeMethod('getConnectionInfo'),
); );
return NearbyConnectionInfoMapper.mapToInfo(result);
} }
@override @override
Future<bool> discover() async { Future<bool> discover() async {
final result = await methodChannel.invokeMethod('discover'); final result = await methodChannel.invokeMethod('discover');
return _handleBooleanResult(result); return ResultHandler.instance.handle<bool?>(result) ?? false;
} }
@override @override
Future<bool> stopDiscovery() async { Future<bool> stopDiscovery() async {
final result = await methodChannel.invokeMethod('stopDiscovery'); final result = await methodChannel.invokeMethod('stopDiscovery');
return _handleBooleanResult(result); return ResultHandler.instance.handle<bool?>(result) ?? false;
} }
@override @override
@@ -57,19 +58,19 @@ class MethodChannelAndroidNearbyService extends NearbyServiceAndroidPlatform {
"connect", "connect",
{"deviceAddress": deviceAddress}, {"deviceAddress": deviceAddress},
); );
return _handleBooleanResult(result); return ResultHandler.instance.handle<bool?>(result) ?? false;
} }
@override @override
Future<bool> disconnect() async { Future<bool> disconnect() async {
final result = await methodChannel.invokeMethod("disconnect"); final result = await methodChannel.invokeMethod("disconnect");
return _handleBooleanResult(result); return ResultHandler.instance.handle<bool?>(result) ?? false;
} }
@override @override
Future<bool> cancelConnect() async { Future<bool> cancelConnect() async {
final result = await methodChannel.invokeMethod("cancelConnect"); final result = await methodChannel.invokeMethod("cancelConnect");
return _handleBooleanResult(result); return ResultHandler.instance.handle<bool?>(result) ?? false;
} }
@override @override
@@ -78,19 +79,9 @@ class MethodChannelAndroidNearbyService extends NearbyServiceAndroidPlatform {
"nearby_service_connection_info", "nearby_service_connection_info",
); );
return connectedDeviceChannel.receiveBroadcastStream().map( return connectedDeviceChannel.receiveBroadcastStream().map(
(e) => NearbyConnectionInfoMapper.mapToInfo(e), (e) => ResultHandler.instance.handle(
); NearbyConnectionInfoMapper.mapToInfo(e),
} ),
bool _handleBooleanResult(dynamic result) {
if (result is bool) {
return result;
} else if (result is String) {
throw NearbyServiceAndroidExceptionMapper.map(result);
} else {
throw NearbyServiceException(
'Got unknown value from native platform: $result',
); );
} }
} }
}
@@ -1,6 +1,5 @@
import 'package:nearby_service/nearby_service.dart'; import 'package:nearby_service/nearby_service.dart';
import 'package:nearby_service/src/utils/constants.dart';
const _kNearbyServiceMessage = 'Got error from native platform with status=';
/// ///
/// Wi-Fi P2P is not supported on this device /// Wi-Fi P2P is not supported on this device
@@ -8,7 +7,7 @@ const _kNearbyServiceMessage = 'Got error from native platform with status=';
class NearbyServiceP2PUnsupportedException extends NearbyServiceException { class NearbyServiceP2PUnsupportedException extends NearbyServiceException {
NearbyServiceP2PUnsupportedException() NearbyServiceP2PUnsupportedException()
: super( : super(
'${_kNearbyServiceMessage}P2P_UNSUPPORTED', '${kNearbyServiceMessage}P2P_UNSUPPORTED',
); );
@override @override
@@ -27,7 +26,7 @@ class NearbyServiceP2PUnsupportedException extends NearbyServiceException {
class NearbyServiceBusyException extends NearbyServiceException { class NearbyServiceBusyException extends NearbyServiceException {
NearbyServiceBusyException() NearbyServiceBusyException()
: super( : super(
'${_kNearbyServiceMessage}BUSY', '${kNearbyServiceMessage}BUSY',
); );
@override @override
@@ -43,7 +42,7 @@ class NearbyServiceBusyException extends NearbyServiceException {
class NearbyServiceNoServiceRequestsException extends NearbyServiceException { class NearbyServiceNoServiceRequestsException extends NearbyServiceException {
NearbyServiceNoServiceRequestsException() NearbyServiceNoServiceRequestsException()
: super( : super(
'${_kNearbyServiceMessage}NO_SERVICE_REQUESTS', '${kNearbyServiceMessage}NO_SERVICE_REQUESTS',
); );
@override @override
@@ -60,7 +59,7 @@ class NearbyServiceNoServiceRequestsException extends NearbyServiceException {
class NearbyServiceGenericErrorException extends NearbyServiceException { class NearbyServiceGenericErrorException extends NearbyServiceException {
NearbyServiceGenericErrorException() NearbyServiceGenericErrorException()
: super( : super(
'${_kNearbyServiceMessage}ERROR', '${kNearbyServiceMessage}ERROR',
); );
@override @override
@@ -76,7 +75,7 @@ class NearbyServiceGenericErrorException extends NearbyServiceException {
class NearbyServiceUnknownException extends NearbyServiceException { class NearbyServiceUnknownException extends NearbyServiceException {
NearbyServiceUnknownException() NearbyServiceUnknownException()
: super( : super(
'${_kNearbyServiceMessage}UNKNOWN', '${kNearbyServiceMessage}UNKNOWN',
); );
@override @override
+18 -5
View File
@@ -1,9 +1,15 @@
// ignore_for_file: constant_identifier_names
import 'package:nearby_service/nearby_service.dart'; import 'package:nearby_service/nearby_service.dart';
import 'package:nearby_service/src/interface/nearby_service_exception_mapper.dart';
class NearbyServiceAndroidExceptionMapper { class NearbyServiceAndroidExceptionMapper extends NearbyServiceExceptionMapper {
NearbyServiceAndroidExceptionMapper._(); @override
bool canMap(String error) {
return AndroidFailureCodes.values.any((element) => element.name == error);
}
static NearbyServiceException map(String error) { @override
NearbyServiceException map(String error) {
AndroidFailureCodes? enumValue; AndroidFailureCodes? enumValue;
try { try {
enumValue = AndroidFailureCodes.values.firstWhere( enumValue = AndroidFailureCodes.values.firstWhere(
@@ -17,10 +23,17 @@ class NearbyServiceAndroidExceptionMapper {
NearbyServiceP2PUnsupportedException(), NearbyServiceP2PUnsupportedException(),
AndroidFailureCodes.NO_SERVICE_REQUESTS => AndroidFailureCodes.NO_SERVICE_REQUESTS =>
NearbyServiceNoServiceRequestsException(), NearbyServiceNoServiceRequestsException(),
AndroidFailureCodes.NO_INITIALIZATION =>
NearbyServiceNoInitializationException(),
_ => NearbyServiceUnknownException(), _ => NearbyServiceUnknownException(),
}; };
} }
} }
// ignore: constant_identifier_names enum AndroidFailureCodes {
enum AndroidFailureCodes { P2P_UNSUPPORTED, BUSY, NO_SERVICE_REQUESTS, ERROR } P2P_UNSUPPORTED,
BUSY,
NO_SERVICE_REQUESTS,
ERROR,
NO_INITIALIZATION,
}
@@ -3,6 +3,7 @@ import 'dart:async';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:nearby_service/nearby_service.dart'; import 'package:nearby_service/nearby_service.dart';
import 'package:nearby_service/src/utils/result_handler.dart';
/// An implementation of [NearbyServiceIOSPlatform] that uses method channels. /// An implementation of [NearbyServiceIOSPlatform] that uses method channels.
class MethodChannelIOSNearbyService extends NearbyServiceIOSPlatform { class MethodChannelIOSNearbyService extends NearbyServiceIOSPlatform {
@@ -31,72 +32,78 @@ class MethodChannelIOSNearbyService extends NearbyServiceIOSPlatform {
break; break;
} }
}); });
return (await methodChannel.invokeMethod<bool>( final result = await methodChannel.invokeMethod(
'initialize', 'initialize',
deviceName != null ? {"deviceName": deviceName} : null, deviceName != null ? {"deviceName": deviceName} : null,
) ?? );
false); return ResultHandler.instance.handle<bool?>(result) ?? false;
} }
@override @override
Future<String?> getSavedDeviceName() async { Future<String?> getSavedDeviceName() async {
return (await methodChannel.invokeMethod<String?>('getSavedDeviceName')); final result = await methodChannel.invokeMethod<String?>(
'getSavedDeviceName',
);
return ResultHandler.instance.handle(result);
} }
@override @override
Future<bool> startAdvertising() async { Future<bool> startAdvertising() async {
return (await methodChannel.invokeMethod<bool>('startAdvertising')) ?? final result = await methodChannel.invokeMethod('startAdvertising');
false; return ResultHandler.instance.handle<bool?>(result) ?? false;
} }
@override @override
Future<bool> startBrowsing() async { Future<bool> startBrowsing() async {
return (await methodChannel.invokeMethod<bool>('startBrowsing')) ?? false; final result = await methodChannel.invokeMethod('startBrowsing');
return ResultHandler.instance.handle<bool?>(result) ?? false;
} }
@override @override
Future<bool> stopAdvertising() async { Future<bool> stopAdvertising() async {
return (await methodChannel.invokeMethod<bool>('stopAdvertising')) ?? false; final result = await methodChannel.invokeMethod('stopAdvertising');
return ResultHandler.instance.handle<bool?>(result) ?? false;
} }
@override @override
Future<bool> stopBrowsing() async { Future<bool> stopBrowsing() async {
return (await methodChannel.invokeMethod<bool>('stopBrowsing')) ?? false; final result = await methodChannel.invokeMethod('stopBrowsing');
return ResultHandler.instance.handle<bool?>(result) ?? false;
} }
@override @override
Future<bool> invite(String deviceId) async { Future<bool> invite(String deviceId) async {
return (await methodChannel.invokeMethod<bool?>( final result = await methodChannel.invokeMethod(
"invite", "invite",
{"deviceId": deviceId}, {"deviceId": deviceId},
)) ?? );
false; return ResultHandler.instance.handle<bool?>(result) ?? false;
} }
@override @override
Future<bool> acceptInvite(String deviceId) async { Future<bool> acceptInvite(String deviceId) async {
return (await methodChannel.invokeMethod<bool?>( final result = await methodChannel.invokeMethod(
"acceptInvite", "acceptInvite",
{"deviceId": deviceId}, {"deviceId": deviceId},
)) ?? );
false; return ResultHandler.instance.handle<bool?>(result) ?? false;
} }
@override @override
Future<bool> disconnect(String deviceId) async { Future<bool> disconnect(String deviceId) async {
return (await methodChannel.invokeMethod<bool?>( final result = await methodChannel.invokeMethod(
"disconnect", "disconnect",
{"deviceId": deviceId}, {"deviceId": deviceId},
)) ?? );
false; return ResultHandler.instance.handle<bool?>(result) ?? false;
} }
@override @override
Future<bool> send(OutgoingNearbyMessage message) async { Future<bool> send(OutgoingNearbyMessage message) async {
return (await methodChannel.invokeMethod<bool?>( final result = await methodChannel.invokeMethod(
"send", "send",
message.toJson(), message.toJson(),
)) ?? );
false; return ResultHandler.instance.handle<bool?>(result) ?? false;
} }
} }
+27
View File
@@ -0,0 +1,27 @@
// ignore_for_file: constant_identifier_names
import 'package:nearby_service/nearby_service.dart';
import 'package:nearby_service/src/interface/nearby_service_exception_mapper.dart';
class NearbyServiceIOSExceptionMapper extends NearbyServiceExceptionMapper {
@override
bool canMap(String error) {
return IOSFailureCodes.values.any((element) => element.name == error);
}
@override
NearbyServiceException map(String error) {
IOSFailureCodes? enumValue;
try {
enumValue = IOSFailureCodes.values.firstWhere(
(element) => element.name == error,
);
} catch (_) {}
return switch (enumValue) {
IOSFailureCodes.NO_INITIALIZATION =>
NearbyServiceNoInitializationException(),
_ => NearbyServiceUnknownException(),
};
}
}
enum IOSFailureCodes { NO_INITIALIZATION }
+2
View File
@@ -0,0 +1,2 @@
const kNearbyUnknown = 'unknown';
const kNearbyServiceMessage = 'Got error from native platform with status=';
+16
View File
@@ -1,6 +1,7 @@
import 'dart:io'; import 'dart:io';
import 'package:nearby_service/nearby_service.dart'; import 'package:nearby_service/nearby_service.dart';
import 'package:nearby_service/src/utils/constants.dart';
import 'package:nearby_service/src/utils/logger.dart'; import 'package:nearby_service/src/utils/logger.dart';
/// ///
@@ -98,3 +99,18 @@ class NearbyServiceInvalidMessageException extends NearbyServiceException {
return 'NearbyServiceInvalidMessageException{error: $error}'; return 'NearbyServiceInvalidMessageException{error: $error}';
} }
} }
///
/// Error when the plugin is not initialized. Please call initialize() method first.
///
class NearbyServiceNoInitializationException extends NearbyServiceException {
NearbyServiceNoInitializationException()
: super(
'${kNearbyServiceMessage}NO_INITIALIZATION',
);
@override
String toString() {
return 'NearbyServiceNoInitializationException{error: $error}';
}
}
+22
View File
@@ -0,0 +1,22 @@
import 'package:nearby_service/nearby_service.dart';
import 'package:nearby_service/src/interface/nearby_service_exception_mapper.dart';
class ResultHandler {
ResultHandler._();
static ResultHandler instance = ResultHandler._();
T handle<T>(dynamic result) {
if (result is String &&
NearbyServiceExceptionMapper.instance.canMap(result)) {
throw NearbyServiceExceptionMapper.instance.map(result);
}
if (result is T) {
return result;
}
throw NearbyServiceException(
'Got unknown value from native platform: $result',
);
}
}
-1
View File
@@ -1 +0,0 @@
const kNearbyUnknown = 'unknown';
+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.8 version: 0.0.9
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