diff --git a/packages/connectivity_plus/connectivity_plus/android/src/main/java/dev/fluttercommunity/plus/connectivity/Connectivity.java b/packages/connectivity_plus/connectivity_plus/android/src/main/java/dev/fluttercommunity/plus/connectivity/Connectivity.java index 02a316bcbb..33886de59b 100644 --- a/packages/connectivity_plus/connectivity_plus/android/src/main/java/dev/fluttercommunity/plus/connectivity/Connectivity.java +++ b/packages/connectivity_plus/connectivity_plus/android/src/main/java/dev/fluttercommunity/plus/connectivity/Connectivity.java @@ -8,6 +8,8 @@ import android.net.Network; import android.net.NetworkCapabilities; import android.os.Build; +import java.util.ArrayList; +import java.util.List; /** Reports connectivity related information such as connectivity type and wifi information. */ public class Connectivity { @@ -23,58 +25,75 @@ public Connectivity(ConnectivityManager connectivityManager) { this.connectivityManager = connectivityManager; } - String getNetworkType() { + List getNetworkTypes() { + List types = new ArrayList<>(); if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { Network network = connectivityManager.getActiveNetwork(); NetworkCapabilities capabilities = connectivityManager.getNetworkCapabilities(network); if (capabilities == null) { - return CONNECTIVITY_NONE; + types.add(CONNECTIVITY_NONE); + return types; } if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) { - return CONNECTIVITY_WIFI; + types.add(CONNECTIVITY_WIFI); } if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) { - return CONNECTIVITY_ETHERNET; + types.add(CONNECTIVITY_ETHERNET); } if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN)) { - return CONNECTIVITY_VPN; + types.add(CONNECTIVITY_VPN); } if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { - return CONNECTIVITY_MOBILE; + types.add(CONNECTIVITY_MOBILE); } if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH)) { - return CONNECTIVITY_BLUETOOTH; + types.add(CONNECTIVITY_BLUETOOTH); } + if (types.isEmpty()) { + types.add(CONNECTIVITY_NONE); + return types; + } + } else { + // For legacy versions, return a single type as before or adapt similarly if multiple types need to be supported + return getNetworkTypesLegacy(); } - return getNetworkTypeLegacy(); + return types; } @SuppressWarnings("deprecation") - private String getNetworkTypeLegacy() { + private List getNetworkTypesLegacy() { // handle type for Android versions less than Android 6 android.net.NetworkInfo info = connectivityManager.getActiveNetworkInfo(); + List types = new ArrayList<>(); if (info == null || !info.isConnected()) { - return CONNECTIVITY_NONE; + types.add(CONNECTIVITY_NONE); + return types; } int type = info.getType(); switch (type) { case ConnectivityManager.TYPE_BLUETOOTH: - return CONNECTIVITY_BLUETOOTH; + types.add(CONNECTIVITY_BLUETOOTH); + break; case ConnectivityManager.TYPE_ETHERNET: - return CONNECTIVITY_ETHERNET; + types.add(CONNECTIVITY_ETHERNET); + break; case ConnectivityManager.TYPE_WIFI: case ConnectivityManager.TYPE_WIMAX: - return CONNECTIVITY_WIFI; + types.add(CONNECTIVITY_WIFI); + break; case ConnectivityManager.TYPE_VPN: - return CONNECTIVITY_VPN; + types.add(CONNECTIVITY_VPN); + break; case ConnectivityManager.TYPE_MOBILE: case ConnectivityManager.TYPE_MOBILE_DUN: case ConnectivityManager.TYPE_MOBILE_HIPRI: - return CONNECTIVITY_MOBILE; + types.add(CONNECTIVITY_MOBILE); + break; default: - return CONNECTIVITY_NONE; + types.add(CONNECTIVITY_NONE); } + return types; } public ConnectivityManager getConnectivityManager() { diff --git a/packages/connectivity_plus/connectivity_plus/android/src/main/java/dev/fluttercommunity/plus/connectivity/ConnectivityBroadcastReceiver.java b/packages/connectivity_plus/connectivity_plus/android/src/main/java/dev/fluttercommunity/plus/connectivity/ConnectivityBroadcastReceiver.java index 22ecacd7f5..2380b44042 100644 --- a/packages/connectivity_plus/connectivity_plus/android/src/main/java/dev/fluttercommunity/plus/connectivity/ConnectivityBroadcastReceiver.java +++ b/packages/connectivity_plus/connectivity_plus/android/src/main/java/dev/fluttercommunity/plus/connectivity/ConnectivityBroadcastReceiver.java @@ -78,12 +78,12 @@ public void onCancel(Object arguments) { @Override public void onReceive(Context context, Intent intent) { if (events != null) { - events.success(connectivity.getNetworkType()); + events.success(String.join(",", connectivity.getNetworkTypes())); } } private void sendEvent() { - Runnable runnable = () -> events.success(connectivity.getNetworkType()); + Runnable runnable = () -> events.success(String.join(",", connectivity.getNetworkTypes())); mainHandler.post(runnable); } diff --git a/packages/connectivity_plus/connectivity_plus/android/src/main/java/dev/fluttercommunity/plus/connectivity/ConnectivityMethodChannelHandler.java b/packages/connectivity_plus/connectivity_plus/android/src/main/java/dev/fluttercommunity/plus/connectivity/ConnectivityMethodChannelHandler.java index ad12f66d33..8c6e394e4a 100644 --- a/packages/connectivity_plus/connectivity_plus/android/src/main/java/dev/fluttercommunity/plus/connectivity/ConnectivityMethodChannelHandler.java +++ b/packages/connectivity_plus/connectivity_plus/android/src/main/java/dev/fluttercommunity/plus/connectivity/ConnectivityMethodChannelHandler.java @@ -29,7 +29,7 @@ class ConnectivityMethodChannelHandler implements MethodChannel.MethodCallHandle @Override public void onMethodCall(MethodCall call, @NonNull MethodChannel.Result result) { if ("check".equals(call.method)) { - result.success(connectivity.getNetworkType()); + result.success(String.join(",", connectivity.getNetworkTypes())); } else { result.notImplemented(); } diff --git a/packages/connectivity_plus/connectivity_plus/example/integration_test/connectivity_plus_test.dart b/packages/connectivity_plus/connectivity_plus/example/integration_test/connectivity_plus_test.dart index 6ad4f5b4fb..fe8cd72f8e 100644 --- a/packages/connectivity_plus/connectivity_plus/example/integration_test/connectivity_plus_test.dart +++ b/packages/connectivity_plus/connectivity_plus/example/integration_test/connectivity_plus_test.dart @@ -26,7 +26,7 @@ void main() { (WidgetTester tester) async { final result = await connectivity.checkConnectivity(); - expect(result, ConnectivityResult.wifi); + expect(result, [ConnectivityResult.wifi]); }, skip: !Platform.isAndroid || Platform.operatingSystemVersion.contains('5.0.2')); @@ -35,7 +35,7 @@ void main() { (WidgetTester tester) async { final result = await connectivity.checkConnectivity(); - expect(result, ConnectivityResult.mobile); + expect(result, [ConnectivityResult.mobile]); }, skip: !Platform.isAndroid || !Platform.operatingSystemVersion.contains('5.0.2')); @@ -44,14 +44,14 @@ void main() { (WidgetTester tester) async { final result = await connectivity.checkConnectivity(); - expect(result, ConnectivityResult.ethernet); + expect(result, [ConnectivityResult.ethernet]); }, skip: !Platform.isMacOS); testWidgets('connectivity on Linux should be none', (WidgetTester tester) async { final result = await connectivity.checkConnectivity(); - expect(result, ConnectivityResult.other); + expect(result, [ConnectivityResult.other]); }, skip: !Platform.isLinux); }); } diff --git a/packages/connectivity_plus/connectivity_plus/example/ios/Runner.xcodeproj/project.pbxproj b/packages/connectivity_plus/connectivity_plus/example/ios/Runner.xcodeproj/project.pbxproj index 65dbc24af3..922e2dea2e 100644 --- a/packages/connectivity_plus/connectivity_plus/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/connectivity_plus/connectivity_plus/example/ios/Runner.xcodeproj/project.pbxproj @@ -214,7 +214,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1300; + LastUpgradeCheck = 1430; ORGANIZATIONNAME = ""; TargetAttributes = { 331C8080294A63A400263BE5 = { @@ -451,7 +451,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -579,7 +579,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -628,7 +628,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; diff --git a/packages/connectivity_plus/connectivity_plus/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/connectivity_plus/connectivity_plus/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index e42adcb34c..87131a09be 100644 --- a/packages/connectivity_plus/connectivity_plus/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/connectivity_plus/connectivity_plus/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ { - ConnectivityResult _connectionStatus = ConnectivityResult.none; + List _connectionStatus = [ConnectivityResult.none]; final Connectivity _connectivity = Connectivity(); - late StreamSubscription _connectivitySubscription; + late StreamSubscription> _connectivitySubscription; @override void initState() { @@ -63,7 +63,7 @@ class _MyHomePageState extends State { // Platform messages are asynchronous, so we initialize in an async method. Future initConnectivity() async { - late ConnectivityResult result; + late List result; // Platform messages may fail, so we use a try/catch PlatformException. try { result = await _connectivity.checkConnectivity(); @@ -82,7 +82,7 @@ class _MyHomePageState extends State { return _updateConnectionStatus(result); } - Future _updateConnectionStatus(ConnectivityResult result) async { + Future _updateConnectionStatus(List result) async { setState(() { _connectionStatus = result; }); diff --git a/packages/connectivity_plus/connectivity_plus/ios/Classes/ConnectivityProvider.swift b/packages/connectivity_plus/connectivity_plus/ios/Classes/ConnectivityProvider.swift index 38156822b3..ad2cbc1e16 100644 --- a/packages/connectivity_plus/connectivity_plus/ios/Classes/ConnectivityProvider.swift +++ b/packages/connectivity_plus/connectivity_plus/ios/Classes/ConnectivityProvider.swift @@ -9,13 +9,13 @@ public enum ConnectivityType { } public protocol ConnectivityProvider: NSObjectProtocol { - typealias ConnectivityUpdateHandler = (ConnectivityType) -> Void - - var currentConnectivityType: ConnectivityType { get } - + typealias ConnectivityUpdateHandler = ([ConnectivityType]) -> Void + + var currentConnectivityTypes: [ConnectivityType] { get } + var connectivityUpdateHandler: ConnectivityUpdateHandler? { get set } - + func start() - + func stop() } diff --git a/packages/connectivity_plus/connectivity_plus/ios/Classes/PathMonitorConnectivityProvider.swift b/packages/connectivity_plus/connectivity_plus/ios/Classes/PathMonitorConnectivityProvider.swift index 5447780d0a..cad09a88a8 100644 --- a/packages/connectivity_plus/connectivity_plus/ios/Classes/PathMonitorConnectivityProvider.swift +++ b/packages/connectivity_plus/connectivity_plus/ios/Classes/PathMonitorConnectivityProvider.swift @@ -8,23 +8,27 @@ public class PathMonitorConnectivityProvider: NSObject, ConnectivityProvider { private var _pathMonitor: NWPathMonitor? - public var currentConnectivityType: ConnectivityType { + public var currentConnectivityTypes: [ConnectivityType] { let path = ensurePathMonitor().currentPath - // .satisfied means that the network is available + var types: [ConnectivityType] = [] + + // Check for connectivity and append to types array as necessary if path.status == .satisfied { if path.usesInterfaceType(.wifi) { - return .wifi - } else if path.usesInterfaceType(.cellular) { - return .cellular - } else if path.usesInterfaceType(.wiredEthernet) { - // .wiredEthernet is available in simulator - // but for consistency it is probably correct to report .wifi - return .wifi - } else if path.usesInterfaceType(.other) { - return .other + types.append(.wifi) + } + if path.usesInterfaceType(.cellular) { + types.append(.cellular) + } + if path.usesInterfaceType(.wiredEthernet) { + types.append(.wiredEthernet) + } + if path.usesInterfaceType(.other) { + types.append(.other) } } - return .none + + return types.isEmpty ? [.none] : types } public var connectivityUpdateHandler: ConnectivityUpdateHandler? @@ -55,6 +59,6 @@ public class PathMonitorConnectivityProvider: NSObject, ConnectivityProvider { } private func pathUpdateHandler(path: NWPath) { - connectivityUpdateHandler?(currentConnectivityType) + connectivityUpdateHandler?(currentConnectivityTypes) } } diff --git a/packages/connectivity_plus/connectivity_plus/ios/Classes/ReachabilityConnectivityProvider.swift b/packages/connectivity_plus/connectivity_plus/ios/Classes/ReachabilityConnectivityProvider.swift index 4398a12e6e..a3ca5427ab 100644 --- a/packages/connectivity_plus/connectivity_plus/ios/Classes/ReachabilityConnectivityProvider.swift +++ b/packages/connectivity_plus/connectivity_plus/ios/Classes/ReachabilityConnectivityProvider.swift @@ -4,15 +4,18 @@ import Reachability public class ReachabilityConnectivityProvider: NSObject, ConnectivityProvider { private var _reachability: Reachability? - public var currentConnectivityType: ConnectivityType { - let reachability = ensureReachability() + public var currentConnectivityTypes: [ConnectivityType] { + guard let reachability = _reachability else { + return [.none] + } + switch reachability.connection { case .wifi: - return .wifi + return [.wifi] case .cellular: - return .cellular + return [.cellular] default: - return .none + return [.none] } } @@ -54,6 +57,9 @@ public class ReachabilityConnectivityProvider: NSObject, ConnectivityProvider { } @objc private func reachabilityChanged(notification: NSNotification) { - connectivityUpdateHandler?(currentConnectivityType) + if let reachability = notification.object as? Reachability { + _reachability = reachability + connectivityUpdateHandler?(currentConnectivityTypes) + } } } diff --git a/packages/connectivity_plus/connectivity_plus/ios/Classes/SwiftConnectivityPlusPlugin.swift b/packages/connectivity_plus/connectivity_plus/ios/Classes/SwiftConnectivityPlusPlugin.swift index c969b4e565..0b93aedca2 100644 --- a/packages/connectivity_plus/connectivity_plus/ios/Classes/SwiftConnectivityPlusPlugin.swift +++ b/packages/connectivity_plus/connectivity_plus/ios/Classes/SwiftConnectivityPlusPlugin.swift @@ -44,7 +44,7 @@ public class SwiftConnectivityPlusPlugin: NSObject, FlutterPlugin, FlutterStream public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { switch call.method { case "check": - result(statusFrom(connectivityType: connectivityProvider.currentConnectivityType)) + result(statusFrom(connectivityTypes: connectivityProvider.currentConnectivityTypes)) default: result(FlutterMethodNotImplemented) } @@ -64,6 +64,12 @@ public class SwiftConnectivityPlusPlugin: NSObject, FlutterPlugin, FlutterStream return "none" } } + + private func statusFrom(connectivityTypes: [ConnectivityType]) -> String { + return connectivityTypes.map { + self.statusFrom(connectivityType: $0) + }.joined(separator: ",") + } public func onListen( withArguments _: Any?, @@ -71,13 +77,14 @@ public class SwiftConnectivityPlusPlugin: NSObject, FlutterPlugin, FlutterStream ) -> FlutterError? { eventSink = events connectivityProvider.start() - connectivityUpdateHandler(connectivityType: connectivityProvider.currentConnectivityType) + // Update this to handle a list + connectivityUpdateHandler(connectivityTypes: connectivityProvider.currentConnectivityTypes) return nil } - private func connectivityUpdateHandler(connectivityType: ConnectivityType) { + private func connectivityUpdateHandler(connectivityTypes: [ConnectivityType]) { DispatchQueue.main.async { - self.eventSink?(self.statusFrom(connectivityType: connectivityType)) + self.eventSink?(self.statusFrom(connectivityTypes: connectivityTypes)) } } diff --git a/packages/connectivity_plus/connectivity_plus/lib/connectivity_plus.dart b/packages/connectivity_plus/connectivity_plus/lib/connectivity_plus.dart index 38cf3515f3..a5195864b9 100644 --- a/packages/connectivity_plus/connectivity_plus/lib/connectivity_plus.dart +++ b/packages/connectivity_plus/connectivity_plus/lib/connectivity_plus.dart @@ -39,7 +39,7 @@ class Connectivity { /// On iOS, the connectivity status might not update when WiFi /// status changes, this is a known issue that only affects simulators. /// For details see https://github.com/fluttercommunity/plus_plugins/issues/479. - Stream get onConnectivityChanged { + Stream> get onConnectivityChanged { return _platform.onConnectivityChanged; } @@ -49,7 +49,7 @@ class Connectivity { /// make a network request. It only gives you the radio status. /// /// Instead listen for connectivity changes via [onConnectivityChanged] stream. - Future checkConnectivity() { + Future> checkConnectivity() { return _platform.checkConnectivity(); } } diff --git a/packages/connectivity_plus/connectivity_plus/lib/src/connectivity_plus_linux.dart b/packages/connectivity_plus/connectivity_plus/lib/src/connectivity_plus_linux.dart index 9c882dec54..31b562aa24 100644 --- a/packages/connectivity_plus/connectivity_plus/lib/src/connectivity_plus_linux.dart +++ b/packages/connectivity_plus/connectivity_plus/lib/src/connectivity_plus_linux.dart @@ -19,7 +19,7 @@ class ConnectivityPlusLinuxPlugin extends ConnectivityPlatform { /// Checks the connection status of the device. @override - Future checkConnectivity() async { + Future> checkConnectivity() async { final client = createClient(); await client.connect(); final connectivity = _getConnectivity(client); @@ -28,38 +28,44 @@ class ConnectivityPlusLinuxPlugin extends ConnectivityPlatform { } NetworkManagerClient? _client; - StreamController? _controller; + StreamController>? _controller; /// Returns a Stream of ConnectivityResults changes. @override - Stream get onConnectivityChanged { - _controller ??= StreamController.broadcast( + Stream> get onConnectivityChanged { + _controller ??= StreamController>.broadcast( onListen: _startListenConnectivity, onCancel: _stopListenConnectivity, ); return _controller!.stream; } - ConnectivityResult _getConnectivity(NetworkManagerClient client) { + List _getConnectivity(NetworkManagerClient client) { + final List results = []; if (client.connectivity == NetworkManagerConnectivityState.none) { - return ConnectivityResult.none; - } - if (client.primaryConnectionType.contains('wireless')) { - return ConnectivityResult.wifi; - } - if (client.primaryConnectionType.contains('ethernet')) { - return ConnectivityResult.ethernet; - } - if (client.primaryConnectionType.contains('vpn')) { - return ConnectivityResult.vpn; - } - if (client.primaryConnectionType.contains('bluetooth')) { - return ConnectivityResult.bluetooth; - } - if (client.primaryConnectionType.contains('mobile')) { - return ConnectivityResult.mobile; + results.add(ConnectivityResult.none); + } else { + if (client.primaryConnectionType.contains('wireless')) { + results.add(ConnectivityResult.wifi); + } + if (client.primaryConnectionType.contains('ethernet')) { + results.add(ConnectivityResult.ethernet); + } + if (client.primaryConnectionType.contains('vpn')) { + results.add(ConnectivityResult.vpn); + } + if (client.primaryConnectionType.contains('bluetooth')) { + results.add(ConnectivityResult.bluetooth); + } + if (client.primaryConnectionType.contains('mobile')) { + results.add(ConnectivityResult.mobile); + } + // Assuming 'other' is a catch-all for unspecified types + if (results.isEmpty) { + results.add(ConnectivityResult.other); + } } - return ConnectivityResult.other; + return results; } Future _startListenConnectivity() async { diff --git a/packages/connectivity_plus/connectivity_plus/lib/src/web/dart_html_connectivity_plugin.dart b/packages/connectivity_plus/connectivity_plus/lib/src/web/dart_html_connectivity_plugin.dart index d5c0c68bf8..7d0fbf419f 100644 --- a/packages/connectivity_plus/connectivity_plus/lib/src/web/dart_html_connectivity_plugin.dart +++ b/packages/connectivity_plus/connectivity_plus/lib/src/web/dart_html_connectivity_plugin.dart @@ -9,25 +9,26 @@ import '../connectivity_plus_web.dart'; class DartHtmlConnectivityPlugin extends ConnectivityPlusWebPlugin { /// Checks the connection status of the device. @override - Future checkConnectivity() async { + Future> checkConnectivity() async { return (html.window.navigator.onLine ?? false) - ? ConnectivityResult.wifi - : ConnectivityResult.none; + ? [ConnectivityResult.wifi] + : [ConnectivityResult.none]; } - StreamController? _connectivityResult; + StreamController>? _connectivityResult; /// Returns a Stream of ConnectivityResults changes. @override - Stream get onConnectivityChanged { + Stream> get onConnectivityChanged { if (_connectivityResult == null) { - _connectivityResult = StreamController.broadcast(); + _connectivityResult = + StreamController>.broadcast(); // Fallback to dart:html window.onOnline / window.onOffline html.window.onOnline.listen((event) { - _connectivityResult!.add(ConnectivityResult.wifi); + _connectivityResult!.add([ConnectivityResult.wifi]); }); html.window.onOffline.listen((event) { - _connectivityResult!.add(ConnectivityResult.none); + _connectivityResult!.add([ConnectivityResult.none]); }); } return _connectivityResult!.stream; diff --git a/packages/connectivity_plus/connectivity_plus/lib/src/web/network_information_api_connectivity_plugin.dart b/packages/connectivity_plus/connectivity_plus/lib/src/web/network_information_api_connectivity_plugin.dart index bc822a76a6..dba8715847 100644 --- a/packages/connectivity_plus/connectivity_plus/lib/src/web/network_information_api_connectivity_plugin.dart +++ b/packages/connectivity_plus/connectivity_plus/lib/src/web/network_information_api_connectivity_plugin.dart @@ -29,23 +29,24 @@ class NetworkInformationApiConnectivityPlugin /// Checks the connection status of the device. @override - Future checkConnectivity() async { + Future> checkConnectivity() async { return networkInformationToConnectivityResult(_networkInformation); } - StreamController? _connectivityResultStreamController; - late Stream _connectivityResultStream; + StreamController>? + _connectivityResultStreamController; + late Stream> _connectivityResultStream; /// Returns a Stream of ConnectivityResults changes. @override - Stream get onConnectivityChanged { + Stream> get onConnectivityChanged { // use fallback implementation if [_connectionSupported] is not availible if (_connectionSupported == null) { return _webPseudoStream(); } if (_connectivityResultStreamController == null) { _connectivityResultStreamController = - StreamController(); + StreamController>(); setProperty(_networkInformation, 'onchange', allowInterop((_) { _connectivityResultStreamController! .add(networkInformationToConnectivityResult(_networkInformation)); @@ -66,11 +67,11 @@ class NetworkInformationApiConnectivityPlugin } /// stores the last fallback network state - ConnectivityResult? _lastFallbackState; + List? _lastFallbackState; /// periodically checks the current network state - Stream _webPseudoStream() { - final StreamController webStream = + Stream> _webPseudoStream() { + final StreamController> webStream = StreamController.broadcast(); Timer.periodic( const Duration(milliseconds: 250), diff --git a/packages/connectivity_plus/connectivity_plus/lib/src/web/utils/connectivity_result.dart b/packages/connectivity_plus/connectivity_plus/lib/src/web/utils/connectivity_result.dart index 004cba81d6..c3250f194c 100644 --- a/packages/connectivity_plus/connectivity_plus/lib/src/web/utils/connectivity_result.dart +++ b/packages/connectivity_plus/connectivity_plus/lib/src/web/utils/connectivity_result.dart @@ -3,11 +3,11 @@ import 'dart:html' as html show NetworkInformation; import 'package:connectivity_plus_platform_interface/connectivity_plus_platform_interface.dart'; /// Converts an incoming NetworkInformation object into the correct ConnectivityResult. -ConnectivityResult networkInformationToConnectivityResult( +List networkInformationToConnectivityResult( html.NetworkInformation info, ) { if (info.downlink == 0 && info.rtt == 0) { - return ConnectivityResult.none; + return [ConnectivityResult.none]; } if (info.type != null) { return _typeToConnectivityResult(info.type!); @@ -15,10 +15,11 @@ ConnectivityResult networkInformationToConnectivityResult( if (info.effectiveType != null) { return _effectiveTypeToConnectivityResult(info.effectiveType!); } - return ConnectivityResult.none; + return [ConnectivityResult.none]; } -ConnectivityResult _effectiveTypeToConnectivityResult(String effectiveType) { +List _effectiveTypeToConnectivityResult( + String effectiveType) { // Possible values: /*'2g'|'3g'|'4g'|'slow-2g'*/ switch (effectiveType) { @@ -26,28 +27,32 @@ ConnectivityResult _effectiveTypeToConnectivityResult(String effectiveType) { case '2g': case '3g': case '4g': - return ConnectivityResult.mobile; + return [ConnectivityResult.mobile]; default: - return ConnectivityResult.wifi; + return [ConnectivityResult.wifi]; } } -ConnectivityResult _typeToConnectivityResult(String type) { - // Possible values: - /*'bluetooth'|'cellular'|'ethernet'|'mixed'|'none'|'other'|'unknown'|'wifi'|'wimax'*/ +List _typeToConnectivityResult(String type) { + // Possible values: 'bluetooth', 'cellular', 'ethernet', 'mixed', 'none', 'other', 'unknown', 'wifi', 'wimax' switch (type) { case 'none': - return ConnectivityResult.none; + // Corrected to return a list + return [ConnectivityResult.none]; case 'bluetooth': - return ConnectivityResult.bluetooth; + return [ConnectivityResult.bluetooth]; case 'cellular': case 'mixed': case 'other': case 'unknown': - return ConnectivityResult.mobile; + return [ConnectivityResult.mobile]; case 'ethernet': - return ConnectivityResult.ethernet; + return [ConnectivityResult.ethernet]; + case 'wifi': + case 'wimax': // Assuming 'wimax' should be treated the same as 'wifi' + return [ConnectivityResult.wifi]; default: - return ConnectivityResult.wifi; + // Assuming default should be 'other' to cover all unspecified cases + return [ConnectivityResult.other]; } } diff --git a/packages/connectivity_plus/connectivity_plus/macos/Classes/ConnectivityPlugin.swift b/packages/connectivity_plus/connectivity_plus/macos/Classes/ConnectivityPlugin.swift index 8be217e9a6..0e4fd0dd72 100644 --- a/packages/connectivity_plus/connectivity_plus/macos/Classes/ConnectivityPlugin.swift +++ b/packages/connectivity_plus/connectivity_plus/macos/Classes/ConnectivityPlugin.swift @@ -45,7 +45,7 @@ public class ConnectivityPlugin: NSObject, FlutterPlugin, FlutterStreamHandler { public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { switch call.method { case "check": - result(statusFrom(connectivityType: connectivityProvider.currentConnectivityType)) + result(statusFrom(connectivityTypes: connectivityProvider.currentConnectivityTypes)) default: result(FlutterMethodNotImplemented) } @@ -66,19 +66,25 @@ public class ConnectivityPlugin: NSObject, FlutterPlugin, FlutterStreamHandler { } } + private func statusFrom(connectivityTypes: [ConnectivityType]) -> String { + return connectivityTypes.map { + self.statusFrom(connectivityType: $0) + }.joined(separator: ",") + } + public func onListen( withArguments _: Any?, eventSink events: @escaping FlutterEventSink ) -> FlutterError? { eventSink = events connectivityProvider.start() - connectivityUpdateHandler(connectivityType: connectivityProvider.currentConnectivityType) + connectivityUpdateHandler(connectivityTypes: connectivityProvider.currentConnectivityTypes) return nil } - private func connectivityUpdateHandler(connectivityType: ConnectivityType) { + private func connectivityUpdateHandler(connectivityTypes: [ConnectivityType]) { DispatchQueue.main.async { - self.eventSink?(self.statusFrom(connectivityType: connectivityType)) + self.eventSink?(self.statusFrom(connectivityTypes: connectivityTypes)) } } diff --git a/packages/connectivity_plus/connectivity_plus/macos/Classes/ConnectivityProvider.swift b/packages/connectivity_plus/connectivity_plus/macos/Classes/ConnectivityProvider.swift index 38156822b3..ad2cbc1e16 100644 --- a/packages/connectivity_plus/connectivity_plus/macos/Classes/ConnectivityProvider.swift +++ b/packages/connectivity_plus/connectivity_plus/macos/Classes/ConnectivityProvider.swift @@ -9,13 +9,13 @@ public enum ConnectivityType { } public protocol ConnectivityProvider: NSObjectProtocol { - typealias ConnectivityUpdateHandler = (ConnectivityType) -> Void - - var currentConnectivityType: ConnectivityType { get } - + typealias ConnectivityUpdateHandler = ([ConnectivityType]) -> Void + + var currentConnectivityTypes: [ConnectivityType] { get } + var connectivityUpdateHandler: ConnectivityUpdateHandler? { get set } - + func start() - + func stop() } diff --git a/packages/connectivity_plus/connectivity_plus/macos/Classes/PathMonitorConnectivityProvider.swift b/packages/connectivity_plus/connectivity_plus/macos/Classes/PathMonitorConnectivityProvider.swift index 5b86a0d898..d15b8d487a 100644 --- a/packages/connectivity_plus/connectivity_plus/macos/Classes/PathMonitorConnectivityProvider.swift +++ b/packages/connectivity_plus/connectivity_plus/macos/Classes/PathMonitorConnectivityProvider.swift @@ -8,21 +8,27 @@ public class PathMonitorConnectivityProvider: NSObject, ConnectivityProvider { private var _pathMonitor: NWPathMonitor? - public var currentConnectivityType: ConnectivityType { + public var currentConnectivityTypes: [ConnectivityType] { let path = ensurePathMonitor().currentPath - // .satisfied means that the network is available + var types: [ConnectivityType] = [] + + // Check for connectivity and append to types array as necessary if path.status == .satisfied { if path.usesInterfaceType(.wifi) { - return .wifi - } else if path.usesInterfaceType(.cellular) { - return .cellular - } else if path.usesInterfaceType(.wiredEthernet) { - return .wiredEthernet - } else if path.usesInterfaceType(.other) { - return .other + types.append(.wifi) + } + if path.usesInterfaceType(.cellular) { + types.append(.cellular) + } + if path.usesInterfaceType(.wiredEthernet) { + types.append(.wiredEthernet) + } + if path.usesInterfaceType(.other) { + types.append(.other) } } - return .none + + return types.isEmpty ? [.none] : types } public var connectivityUpdateHandler: ConnectivityUpdateHandler? @@ -52,6 +58,6 @@ public class PathMonitorConnectivityProvider: NSObject, ConnectivityProvider { } private func pathUpdateHandler(path: NWPath) { - connectivityUpdateHandler?(currentConnectivityType) + connectivityUpdateHandler?(currentConnectivityTypes) } } diff --git a/packages/connectivity_plus/connectivity_plus/macos/Classes/ReachabilityConnectivityProvider.swift b/packages/connectivity_plus/connectivity_plus/macos/Classes/ReachabilityConnectivityProvider.swift index 4398a12e6e..a3ca5427ab 100644 --- a/packages/connectivity_plus/connectivity_plus/macos/Classes/ReachabilityConnectivityProvider.swift +++ b/packages/connectivity_plus/connectivity_plus/macos/Classes/ReachabilityConnectivityProvider.swift @@ -4,15 +4,18 @@ import Reachability public class ReachabilityConnectivityProvider: NSObject, ConnectivityProvider { private var _reachability: Reachability? - public var currentConnectivityType: ConnectivityType { - let reachability = ensureReachability() + public var currentConnectivityTypes: [ConnectivityType] { + guard let reachability = _reachability else { + return [.none] + } + switch reachability.connection { case .wifi: - return .wifi + return [.wifi] case .cellular: - return .cellular + return [.cellular] default: - return .none + return [.none] } } @@ -54,6 +57,9 @@ public class ReachabilityConnectivityProvider: NSObject, ConnectivityProvider { } @objc private func reachabilityChanged(notification: NSNotification) { - connectivityUpdateHandler?(currentConnectivityType) + if let reachability = notification.object as? Reachability { + _reachability = reachability + connectivityUpdateHandler?(currentConnectivityTypes) + } } } diff --git a/packages/connectivity_plus/connectivity_plus/test/connectivity_plus_linux_test.dart b/packages/connectivity_plus/connectivity_plus/test/connectivity_plus_linux_test.dart index ecdbc7391f..b7ecf03cab 100644 --- a/packages/connectivity_plus/connectivity_plus/test/connectivity_plus_linux_test.dart +++ b/packages/connectivity_plus/connectivity_plus/test/connectivity_plus_linux_test.dart @@ -25,7 +25,7 @@ void main() { }; expect( linux.checkConnectivity(), - completion(equals(ConnectivityResult.bluetooth)), + completion(equals([ConnectivityResult.bluetooth])), ); }); @@ -40,7 +40,7 @@ void main() { }; expect( linux.checkConnectivity(), - completion(equals(ConnectivityResult.ethernet)), + completion(equals([ConnectivityResult.ethernet])), ); }); @@ -55,7 +55,7 @@ void main() { }; expect( linux.checkConnectivity(), - completion(equals(ConnectivityResult.wifi)), + completion(equals([ConnectivityResult.wifi])), ); }); @@ -70,7 +70,22 @@ void main() { }; expect( linux.checkConnectivity(), - completion(equals(ConnectivityResult.vpn)), + completion(equals([ConnectivityResult.vpn])), + ); + }); + + test('wireless+vpn', () async { + final linux = ConnectivityPlusLinuxPlugin(); + linux.createClient = () { + final client = MockNetworkManagerClient(); + when(client.connectivity) + .thenReturn(NetworkManagerConnectivityState.full); + when(client.primaryConnectionType).thenReturn('wireless,vpn'); + return client; + }; + expect( + linux.checkConnectivity(), + completion(equals([ConnectivityResult.wifi, ConnectivityResult.vpn])), ); }); @@ -82,8 +97,8 @@ void main() { .thenReturn(NetworkManagerConnectivityState.none); return client; }; - expect( - linux.checkConnectivity(), completion(equals(ConnectivityResult.none))); + expect(linux.checkConnectivity(), + completion(equals([ConnectivityResult.none]))); }); test('connectivity changes', () { @@ -100,7 +115,11 @@ void main() { }); return client; }; - expect(linux.onConnectivityChanged, - emitsInOrder([ConnectivityResult.wifi, ConnectivityResult.none])); + expect( + linux.onConnectivityChanged, + emitsInOrder([ + [ConnectivityResult.wifi], + [ConnectivityResult.none] + ])); }); } diff --git a/packages/connectivity_plus/connectivity_plus/test/connectivity_test.dart b/packages/connectivity_plus/connectivity_plus/test/connectivity_test.dart index 8800dc2b3a..4f177c0f31 100644 --- a/packages/connectivity_plus/connectivity_plus/test/connectivity_test.dart +++ b/packages/connectivity_plus/connectivity_plus/test/connectivity_test.dart @@ -8,7 +8,9 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; import 'package:mockito/mockito.dart'; -const ConnectivityResult kCheckConnectivityResult = ConnectivityResult.wifi; +const List kCheckConnectivityResult = [ + ConnectivityResult.wifi +]; void main() { group('Connectivity', () { @@ -31,7 +33,7 @@ class MockConnectivityPlatform extends Mock with MockPlatformInterfaceMixin implements ConnectivityPlatform { @override - Future checkConnectivity() async { + Future> checkConnectivity() async { return kCheckConnectivityResult; } } diff --git a/packages/connectivity_plus/connectivity_plus/windows/connectivity_plus_plugin.cpp b/packages/connectivity_plus/connectivity_plus/windows/connectivity_plus_plugin.cpp index 40742035ec..9b47c491c7 100644 --- a/packages/connectivity_plus/connectivity_plus/windows/connectivity_plus_plugin.cpp +++ b/packages/connectivity_plus/connectivity_plus/windows/connectivity_plus_plugin.cpp @@ -104,18 +104,38 @@ static std::string ConnectivityToString(ConnectivityType connectivityType) { return "wifi"; case ConnectivityType::Ethernet: return "ethernet"; + case ConnectivityType::VPN: + return "vpn"; + case ConnectivityType::Other: + return "other"; case ConnectivityType::None: default: return "none"; } } +static std::string +ConnectivityToString(std::set connectivityTypes) { + if (connectivityTypes.empty()) { + return "none"; + } + + std::string connectivity; + for (auto type : connectivityTypes) { + if (!connectivity.empty()) { + connectivity += ','; + } + connectivity += ConnectivityToString(type); + } + return connectivity; +} + void ConnectivityPlusWindowsPlugin::HandleMethodCall( const flutter::MethodCall &method_call, std::unique_ptr> result) { if (method_call.method_name().compare("check") == 0) { std::string connectivity = - ConnectivityToString(manager->GetConnectivityType()); + ConnectivityToString(manager->GetConnectivityTypes()); result->Success(flutter::EncodableValue(connectivity)); } else { result->NotImplemented(); @@ -130,7 +150,7 @@ ConnectivityStreamHandler::~ConnectivityStreamHandler() {} void ConnectivityStreamHandler::AddConnectivityEvent() { std::string connectivity = - ConnectivityToString(manager->GetConnectivityType()); + ConnectivityToString(manager->GetConnectivityTypes()); sink->Success(flutter::EncodableValue(connectivity)); } diff --git a/packages/connectivity_plus/connectivity_plus/windows/include/connectivity_plus/network_manager.h b/packages/connectivity_plus/connectivity_plus/windows/include/connectivity_plus/network_manager.h index 51a7e9b388..19394fec82 100644 --- a/packages/connectivity_plus/connectivity_plus/windows/include/connectivity_plus/network_manager.h +++ b/packages/connectivity_plus/connectivity_plus/windows/include/connectivity_plus/network_manager.h @@ -7,9 +7,10 @@ #include #include +#include #include -enum class ConnectivityType { None, Ethernet, WiFi }; +enum class ConnectivityType { None, Ethernet, WiFi, Other, VPN }; class NetworkListener; struct IConnectionPoint; @@ -27,7 +28,7 @@ class NetworkManager { bool Init(); void Cleanup(); - ConnectivityType GetConnectivityType() const; + std::set GetConnectivityTypes() const; bool StartListen(NetworkCallback pCallback); void StopListen(); diff --git a/packages/connectivity_plus/connectivity_plus/windows/network_manager.cpp b/packages/connectivity_plus/connectivity_plus/windows/network_manager.cpp index e009c7ed1b..31822c5590 100644 --- a/packages/connectivity_plus/connectivity_plus/windows/network_manager.cpp +++ b/packages/connectivity_plus/connectivity_plus/windows/network_manager.cpp @@ -137,7 +137,7 @@ std::vector NetworkManager::GetConnectedAdapterIds() const { return adapterIds; } -ConnectivityType NetworkManager::GetConnectivityType() const { +std::set NetworkManager::GetConnectivityTypes() const { ULONG bufferSize = 15 * 1024; ULONG flags = GAA_FLAG_SKIP_UNICAST | GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | @@ -153,12 +153,12 @@ ConnectivityType NetworkManager::GetConnectivityType() const { } if (rc != NO_ERROR) { - return ConnectivityType::None; + return {ConnectivityType::None}; } std::vector adapterIds = GetConnectedAdapterIds(); if (adapterIds.empty()) { - return ConnectivityType::None; + return {ConnectivityType::None}; } std::set connectivities; @@ -177,26 +177,35 @@ ConnectivityType NetworkManager::GetConnectivityType() const { if (std::find(adapterIds.begin(), adapterIds.end(), guid) != adapterIds.end()) { + // Read more at + // https://learn.microsoft.com/en-us/windows/win32/api/iptypes/ns-iptypes-ip_adapter_addresses_lh switch (addresses->IfType) { case IF_TYPE_ETHERNET_CSMACD: + case IF_TYPE_IEEE1394: connectivities.insert(ConnectivityType::Ethernet); break; - default: + case IF_TYPE_IEEE80211: connectivities.insert(ConnectivityType::WiFi); break; + case IF_TYPE_TUNNEL: + case IF_TYPE_PPP: + connectivities.insert(ConnectivityType::VPN); + break; + default: + connectivities.insert(ConnectivityType::Other); + break; } } } - if (connectivities.find(ConnectivityType::WiFi) != connectivities.end()) { - return ConnectivityType::WiFi; - } - - if (connectivities.find(ConnectivityType::Ethernet) != connectivities.end()) { - return ConnectivityType::Ethernet; + if (connectivities.empty()) { + // If no specific connectivity types were found, return a set containing + // only None + return {ConnectivityType::None}; } - return ConnectivityType::None; + // Return the set of detected connectivity types + return connectivities; } bool NetworkManager::StartListen(NetworkCallback pCallback) { diff --git a/packages/connectivity_plus/connectivity_plus_platform_interface/lib/connectivity_plus_platform_interface.dart b/packages/connectivity_plus/connectivity_plus_platform_interface/lib/connectivity_plus_platform_interface.dart index 76d98666b8..ef086a44e3 100644 --- a/packages/connectivity_plus/connectivity_plus_platform_interface/lib/connectivity_plus_platform_interface.dart +++ b/packages/connectivity_plus/connectivity_plus_platform_interface/lib/connectivity_plus_platform_interface.dart @@ -39,12 +39,12 @@ abstract class ConnectivityPlatform extends PlatformInterface { } /// Checks the connection status of the device. - Future checkConnectivity() { + Future> checkConnectivity() { throw UnimplementedError('checkConnectivity() has not been implemented.'); } /// Returns a Stream of ConnectivityResults changes. - Stream get onConnectivityChanged { + Stream> get onConnectivityChanged { throw UnimplementedError( 'get onConnectivityChanged has not been implemented.'); } diff --git a/packages/connectivity_plus/connectivity_plus_platform_interface/lib/method_channel_connectivity.dart b/packages/connectivity_plus/connectivity_plus_platform_interface/lib/method_channel_connectivity.dart index 1c053d9133..5fa96e8eb9 100644 --- a/packages/connectivity_plus/connectivity_plus_platform_interface/lib/method_channel_connectivity.dart +++ b/packages/connectivity_plus/connectivity_plus_platform_interface/lib/method_channel_connectivity.dart @@ -22,22 +22,22 @@ class MethodChannelConnectivity extends ConnectivityPlatform { EventChannel eventChannel = const EventChannel('dev.fluttercommunity.plus/connectivity_status'); - Stream? _onConnectivityChanged; + Stream>? _onConnectivityChanged; /// Fires whenever the connectivity state changes. @override - Stream get onConnectivityChanged { + Stream> get onConnectivityChanged { _onConnectivityChanged ??= eventChannel .receiveBroadcastStream() .map((dynamic result) => result.toString()) - .map(parseConnectivityResult); + .map(parseConnectivityResults); return _onConnectivityChanged!; } @override - Future checkConnectivity() { + Future> checkConnectivity() { return methodChannel .invokeMethod('check') - .then((value) => parseConnectivityResult(value ?? '')); + .then((value) => parseConnectivityResults(value ?? '')); } } diff --git a/packages/connectivity_plus/connectivity_plus_platform_interface/lib/src/utils.dart b/packages/connectivity_plus/connectivity_plus_platform_interface/lib/src/utils.dart index 4032537dc0..026faf0928 100644 --- a/packages/connectivity_plus/connectivity_plus_platform_interface/lib/src/utils.dart +++ b/packages/connectivity_plus/connectivity_plus_platform_interface/lib/src/utils.dart @@ -1,22 +1,23 @@ import 'package:connectivity_plus_platform_interface/connectivity_plus_platform_interface.dart'; -/// Convert a String to a ConnectivityResult value. -ConnectivityResult parseConnectivityResult(String state) { - switch (state) { - case 'bluetooth': - return ConnectivityResult.bluetooth; - case 'wifi': - return ConnectivityResult.wifi; - case 'ethernet': - return ConnectivityResult.ethernet; - case 'mobile': - return ConnectivityResult.mobile; - case 'vpn': - return ConnectivityResult.vpn; - case 'other': - return ConnectivityResult.other; - case 'none': - default: - return ConnectivityResult.none; - } +/// Convert a comma-separated String to a list of ConnectivityResult values. +List parseConnectivityResults(String states) { + return states.split(',').map((state) { + switch (state.trim()) { + case 'bluetooth': + return ConnectivityResult.bluetooth; + case 'wifi': + return ConnectivityResult.wifi; + case 'ethernet': + return ConnectivityResult.ethernet; + case 'mobile': + return ConnectivityResult.mobile; + case 'vpn': + return ConnectivityResult.vpn; + case 'other': + return ConnectivityResult.other; + default: + return ConnectivityResult.none; + } + }).toList(); } diff --git a/packages/connectivity_plus/connectivity_plus_platform_interface/test/method_channel_connectivity_test.dart b/packages/connectivity_plus/connectivity_plus_platform_interface/test/method_channel_connectivity_test.dart index 8680c70665..66ee13b67b 100644 --- a/packages/connectivity_plus/connectivity_plus_platform_interface/test/method_channel_connectivity_test.dart +++ b/packages/connectivity_plus/connectivity_plus_platform_interface/test/method_channel_connectivity_test.dart @@ -24,7 +24,8 @@ void main() { log.add(methodCall); switch (methodCall.method) { case 'check': - return 'wifi'; + // Simulate returning a comma-separated string of connectivity statuses + return 'wifi,mobile'; default: return null; } @@ -38,12 +39,13 @@ void main() { (MethodCall methodCall) async { switch (methodCall.method) { case 'listen': + // Simulate returning a comma-separated string of connectivity statuses await TestDefaultBinaryMessengerBinding .instance.defaultBinaryMessenger .handlePlatformMessage( methodChannelConnectivity.eventChannel.name, methodChannelConnectivity.eventChannel.codec - .encodeSuccessEnvelope('wifi'), + .encodeSuccessEnvelope('wifi,mobile'), (_) {}, ); break; @@ -56,15 +58,19 @@ void main() { ); }); + // Test adjusted to handle multiple connectivity types test('onConnectivityChanged', () async { final result = await methodChannelConnectivity.onConnectivityChanged.first; - expect(result, ConnectivityResult.wifi); + expect(result, + containsAll([ConnectivityResult.wifi, ConnectivityResult.mobile])); }); + // Test adjusted to handle multiple connectivity types test('checkConnectivity', () async { final result = await methodChannelConnectivity.checkConnectivity(); - expect(result, ConnectivityResult.wifi); + expect(result, + containsAll([ConnectivityResult.wifi, ConnectivityResult.mobile])); expect( log, [