Skip to content

Commit

Permalink
feat(connectivity): support multiple types
Browse files Browse the repository at this point in the history
BREAKING CHANGES: checkConnectivity and onConnectivityChanged returns list of connectivityResult

Signed-off-by: George Kutsurua <[email protected]>
  • Loading branch information
suquant committed Feb 15, 2024
1 parent 707fab7 commit 3785b4b
Show file tree
Hide file tree
Showing 24 changed files with 284 additions and 194 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,59 +22,80 @@ public class Connectivity {
public Connectivity(ConnectivityManager connectivityManager) {
this.connectivityManager = connectivityManager;
}
private void appendType(StringBuilder types, String type) {
if (types.length() > 0) {
types.append(", ");
}
types.append(type);
}

String getNetworkType() {
String getNetworkTypes() {
StringBuilder types = new StringBuilder();
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;
}
if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
return CONNECTIVITY_WIFI;
appendType(types, CONNECTIVITY_WIFI);
}
if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) {
return CONNECTIVITY_ETHERNET;
appendType(types, CONNECTIVITY_ETHERNET);
}
if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN)) {
return CONNECTIVITY_VPN;
appendType(types, CONNECTIVITY_VPN);
}
if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
return CONNECTIVITY_MOBILE;
appendType(types, CONNECTIVITY_MOBILE);
}
if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH)) {
return CONNECTIVITY_BLUETOOTH;
appendType(types, CONNECTIVITY_BLUETOOTH);
}
if (types.length() == 0) {
return CONNECTIVITY_NONE;
}
} else {
// For legacy versions, return a single type as before or adapt similarly if multiple types need to be supported
return getNetworkTypesLegacy();
}

return getNetworkTypeLegacy();
// Remove the last comma
return types.toString();
}

@SuppressWarnings("deprecation")
private String getNetworkTypeLegacy() {
private String getNetworkTypesLegacy() {
// handle type for Android versions less than Android 6
android.net.NetworkInfo info = connectivityManager.getActiveNetworkInfo();
if (info == null || !info.isConnected()) {
return CONNECTIVITY_NONE;
}
int type = info.getType();
StringBuilder types = new StringBuilder();
switch (type) {
case ConnectivityManager.TYPE_BLUETOOTH:
return CONNECTIVITY_BLUETOOTH;
appendType(types, CONNECTIVITY_BLUETOOTH);
break;
case ConnectivityManager.TYPE_ETHERNET:
return CONNECTIVITY_ETHERNET;
appendType(types, CONNECTIVITY_ETHERNET);
break;
case ConnectivityManager.TYPE_WIFI:
case ConnectivityManager.TYPE_WIMAX:
return CONNECTIVITY_WIFI;
appendType(types, CONNECTIVITY_WIFI);
break;
case ConnectivityManager.TYPE_VPN:
return CONNECTIVITY_VPN;
appendType(types, CONNECTIVITY_VPN);
break;
case ConnectivityManager.TYPE_MOBILE:
case ConnectivityManager.TYPE_MOBILE_DUN:
case ConnectivityManager.TYPE_MOBILE_HIPRI:
return CONNECTIVITY_MOBILE;
appendType(types, CONNECTIVITY_MOBILE);
break;
default:
return CONNECTIVITY_NONE;
}
return types.toString();
}

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(connectivity.getNetworkTypes());
}
}

private void sendEvent() {
Runnable runnable = () -> events.success(connectivity.getNetworkType());
Runnable runnable = () -> events.success(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(connectivity.getNetworkTypes());
} else {
result.notImplemented();
}
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 @@ -10,8 +10,7 @@ import 'package:connectivity_plus_platform_interface/connectivity_plus_platform_
export 'package:connectivity_plus_platform_interface/connectivity_plus_platform_interface.dart'
show ConnectivityResult;

export 'src/connectivity_plus_linux.dart'
if (dart.library.html) 'src/connectivity_plus_web.dart';
export 'src/connectivity_plus_linux.dart' if (dart.library.html) 'src/connectivity_plus_web.dart';

/// Discover network connectivity configurations: Distinguish between WI-FI and cellular, check WI-FI status and more.
class Connectivity {
Expand Down Expand Up @@ -39,7 +38,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 +48,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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class ConnectivityPlusLinuxPlugin extends ConnectivityPlatform {

/// Checks the connection status of the device.
@override
Future<ConnectivityResult> checkConnectivity() async {
Future<List<ConnectivityResult>> checkConnectivity() async {
final client = createClient();
await client.connect();
final connectivity = _getConnectivity(client);
Expand All @@ -28,38 +28,44 @@ class ConnectivityPlusLinuxPlugin extends ConnectivityPlatform {
}

NetworkManagerClient? _client;
StreamController<ConnectivityResult>? _controller;
StreamController<List<ConnectivityResult>>? _controller;

/// Returns a Stream of ConnectivityResults changes.
@override
Stream<ConnectivityResult> get onConnectivityChanged {
_controller ??= StreamController<ConnectivityResult>.broadcast(
Stream<List<ConnectivityResult>> get onConnectivityChanged {
_controller ??= StreamController<List<ConnectivityResult>>.broadcast(
onListen: _startListenConnectivity,
onCancel: _stopListenConnectivity,
);
return _controller!.stream;
}

ConnectivityResult _getConnectivity(NetworkManagerClient client) {
List<ConnectivityResult> _getConnectivity(NetworkManagerClient client) {
List<ConnectivityResult> results = [];

Check notice on line 44 in packages/connectivity_plus/connectivity_plus/lib/src/connectivity_plus_linux.dart

View workflow job for this annotation

GitHub Actions / Flutter Analyze

Local variables should be final.

Try making the variable final. See https://dart.dev/lints/prefer_final_locals to learn more about this problem.
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<void> _startListenConnectivity() async {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,25 @@ import '../connectivity_plus_web.dart';
class DartHtmlConnectivityPlugin extends ConnectivityPlusWebPlugin {
/// Checks the connection status of the device.
@override
Future<ConnectivityResult> checkConnectivity() async {
Future<List<ConnectivityResult>> checkConnectivity() async {
return (html.window.navigator.onLine ?? false)
? ConnectivityResult.wifi
: ConnectivityResult.none;
? [ConnectivityResult.wifi]
: [ConnectivityResult.none];
}

StreamController<ConnectivityResult>? _connectivityResult;

/// Returns a Stream of ConnectivityResults changes.
@override
Stream<ConnectivityResult> get onConnectivityChanged {
Stream<List<ConnectivityResult>> get onConnectivityChanged {
if (_connectivityResult == null) {
_connectivityResult = StreamController<ConnectivityResult>.broadcast();
// Fallback to dart:html window.onOnline / window.onOffline
html.window.onOnline.listen((event) {
_connectivityResult!.add(ConnectivityResult.wifi);
_connectivityResult!.add([ConnectivityResult.wifi]);

Check failure on line 27 in packages/connectivity_plus/connectivity_plus/lib/src/web/dart_html_connectivity_plugin.dart

View workflow job for this annotation

GitHub Actions / Flutter Analyze

The argument type 'List<ConnectivityResult>' can't be assigned to the parameter type 'ConnectivityResult'.

See https://dart.dev/diagnostics/argument_type_not_assignable to learn more about this problem.
});
html.window.onOffline.listen((event) {
_connectivityResult!.add(ConnectivityResult.none);
_connectivityResult!.add([ConnectivityResult.none]);

Check failure on line 30 in packages/connectivity_plus/connectivity_plus/lib/src/web/dart_html_connectivity_plugin.dart

View workflow job for this annotation

GitHub Actions / Flutter Analyze

The argument type 'List<ConnectivityResult>' can't be assigned to the parameter type 'ConnectivityResult'.

See https://dart.dev/diagnostics/argument_type_not_assignable to learn more about this problem.
});
}
return _connectivityResult!.stream;

Check failure on line 33 in packages/connectivity_plus/connectivity_plus/lib/src/web/dart_html_connectivity_plugin.dart

View workflow job for this annotation

GitHub Actions / Flutter Analyze

A value of type 'Stream<ConnectivityResult>' can't be returned from the function 'onConnectivityChanged' because it has a return type of 'Stream<List<ConnectivityResult>>'.

See https://dart.dev/diagnostics/return_of_invalid_type to learn more about this problem.
Expand Down
Loading

0 comments on commit 3785b4b

Please sign in to comment.