Skip to content

Commit

Permalink
feat(connectivity)!: support multiple ConnectivityResult values at th…
Browse files Browse the repository at this point in the history
…e same time

BREAKING CHANGES: checkConnectivity and onConnectivityChanged returns list of connectivityResult. Users will need to update their apps to handle the new list of ConnectivityResult types returned by checkConnectivity and onConnectivityChanged methods.

Signed-off-by: George Kutsurua <[email protected]>
  • Loading branch information
suquant committed Feb 16, 2024
1 parent e3a983b commit e654491
Show file tree
Hide file tree
Showing 29 changed files with 322 additions and 197 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -23,58 +25,75 @@ public Connectivity(ConnectivityManager connectivityManager) {
this.connectivityManager = connectivityManager;
}

String getNetworkType() {
List<String> getNetworkTypes() {
List<String> 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<String> getNetworkTypesLegacy() {
// handle type for Android versions less than Android 6
android.net.NetworkInfo info = connectivityManager.getActiveNetworkInfo();
List<String> 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() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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'));
Expand All @@ -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'));
Expand All @@ -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);
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1300;
LastUpgradeCheck = 1430;
ORGANIZATIONNAME = "";
TargetAttributes = {
331C8080294A63A400263BE5 = {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1300"
LastUpgradeVersion = "1430"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ class MyHomePage extends StatefulWidget {
}

class _MyHomePageState extends State<MyHomePage> {
ConnectivityResult _connectionStatus = ConnectivityResult.none;
List<ConnectivityResult> _connectionStatus = [ConnectivityResult.none];
final Connectivity _connectivity = Connectivity();
late StreamSubscription<ConnectivityResult> _connectivitySubscription;
late StreamSubscription<List<ConnectivityResult>> _connectivitySubscription;

@override
void initState() {
Expand All @@ -63,7 +63,7 @@ class _MyHomePageState extends State<MyHomePage> {

// Platform messages are asynchronous, so we initialize in an async method.
Future<void> initConnectivity() async {
late ConnectivityResult result;
late List<ConnectivityResult> result;
// Platform messages may fail, so we use a try/catch PlatformException.
try {
result = await _connectivity.checkConnectivity();
Expand All @@ -82,7 +82,7 @@ class _MyHomePageState extends State<MyHomePage> {
return _updateConnectionStatus(result);
}

Future<void> _updateConnectionStatus(ConnectivityResult result) async {
Future<void> _updateConnectionStatus(List<ConnectivityResult> result) async {
setState(() {
_connectionStatus = result;
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
Original file line number Diff line number Diff line change
Expand Up @@ -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?
Expand Down Expand Up @@ -55,6 +59,6 @@ public class PathMonitorConnectivityProvider: NSObject, ConnectivityProvider {
}

private func pathUpdateHandler(path: NWPath) {
connectivityUpdateHandler?(currentConnectivityType)
connectivityUpdateHandler?(currentConnectivityTypes)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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]
}
}

Expand Down Expand Up @@ -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)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand All @@ -64,20 +64,27 @@ 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?,
eventSink events: @escaping FlutterEventSink
) -> 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))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<ConnectivityResult> get onConnectivityChanged {
Stream<List<ConnectivityResult>> get onConnectivityChanged {
return _platform.onConnectivityChanged;
}

Expand All @@ -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<ConnectivityResult> checkConnectivity() {
Future<List<ConnectivityResult>> checkConnectivity() {
return _platform.checkConnectivity();
}
}
Loading

0 comments on commit e654491

Please sign in to comment.