Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(connectivity)!: support multiple ConnectivityResult values at the same time #2599

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -17,64 +19,87 @@ public class Connectivity {
static final String CONNECTIVITY_ETHERNET = "ethernet";
static final String CONNECTIVITY_BLUETOOTH = "bluetooth";
static final String CONNECTIVITY_VPN = "vpn";
static final String CONNECTIVITY_OTHER = "other";
private final ConnectivityManager connectivityManager;

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;
if (capabilities == null
|| !capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
types.add(CONNECTIVITY_NONE);
return types;
}
if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
return CONNECTIVITY_WIFI;
if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)
|| capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI_AWARE)) {
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()
&& capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
types.add(CONNECTIVITY_OTHER);
}
if (types.isEmpty()) {
types.add(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();
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_OTHER);
}
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(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
@@ -1,4 +1,8 @@
#if TARGET_OS_IOS
#import <Flutter/Flutter.h>
#elif TARGET_OS_OSX
#import <FlutterMacOS/FlutterMacOS.h>
#endif

@interface ConnectivityPlusPlugin : NSObject <FlutterPlugin>
@end
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
@@ -1,30 +1,33 @@
import Foundation
import Network

@available(iOS 12, *)
public class PathMonitorConnectivityProvider: NSObject, ConnectivityProvider {

private let queue = DispatchQueue.global(qos: .background)

private var _pathMonitor: NWPathMonitor?
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 All @@ -39,22 +42,22 @@ public class PathMonitorConnectivityProvider: NSObject, ConnectivityProvider {
}

public func stop() {
_pathMonitor?.cancel()
_pathMonitor = nil
pathMonitor?.cancel()
pathMonitor = nil
}

@discardableResult
private func ensurePathMonitor() -> NWPathMonitor {
if (_pathMonitor == nil) {
if (pathMonitor == nil) {
let pathMonitor = NWPathMonitor()
pathMonitor.start(queue: queue)
pathMonitor.pathUpdateHandler = pathUpdateHandler
_pathMonitor = pathMonitor
self.pathMonitor = pathMonitor
}
return _pathMonitor!
return self.pathMonitor!
}

private func pathUpdateHandler(path: NWPath) {
connectivityUpdateHandler?(currentConnectivityType)
connectivityUpdateHandler?(currentConnectivityTypes)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,21 @@ import Foundation
import Reachability

public class ReachabilityConnectivityProvider: NSObject, ConnectivityProvider {
private var _reachability: Reachability?
private var reachability: Reachability?

public var currentConnectivityType: ConnectivityType {
let reachability = ensureReachability()
public var currentConnectivityTypes: [ConnectivityType] {
guard let reachability = reachability else {
return [.none]
}

// Supported types https://github.com/ashleymills/Reachability.swift/blob/master/Sources/Reachability.swift#L99
switch reachability.connection {
case .wifi:
return .wifi
return [.wifi]
case .cellular:
return .cellular
return [.cellular]
default:
return .none
return [.none]
}
}

Expand All @@ -39,21 +43,24 @@ public class ReachabilityConnectivityProvider: NSObject, ConnectivityProvider {
NotificationCenter.default.removeObserver(
self,
name: .reachabilityChanged,
object: _reachability)
object: reachability)

_reachability?.stopNotifier()
_reachability = nil
reachability?.stopNotifier()
reachability = nil
}

private func ensureReachability() -> Reachability {
if (_reachability == nil) {
if (reachability == nil) {
let reachability = try? Reachability()
_reachability = reachability
self.reachability = reachability
}
return _reachability!
return reachability!
}

@objc private func reachabilityChanged(notification: NSNotification) {
connectivityUpdateHandler?(currentConnectivityType)
if let reachability = notification.object as? Reachability {
self.reachability = reachability
connectivityUpdateHandler?(currentConnectivityTypes)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@
// Use of this source is governed by a BSD-style license that can
// be found in the LICENSE file.

#if os(iOS)
import Flutter
#elseif os(macOS)
import Cocoa
import FlutterMacOS
#endif

public class SwiftConnectivityPlusPlugin: NSObject, FlutterPlugin, FlutterStreamHandler {
private let connectivityProvider: ConnectivityProvider
Expand All @@ -15,16 +20,22 @@ public class SwiftConnectivityPlusPlugin: NSObject, FlutterPlugin, FlutterStream
}

public static func register(with registrar: FlutterPluginRegistrar) {
#if os(iOS)
let binaryMessenger = registrar.messenger()
#elseif os(macOS)
let binaryMessenger = registrar.messenger
#endif

let channel = FlutterMethodChannel(
name: "dev.fluttercommunity.plus/connectivity",
binaryMessenger: registrar.messenger())
binaryMessenger: binaryMessenger)

let streamChannel = FlutterEventChannel(
name: "dev.fluttercommunity.plus/connectivity_status",
binaryMessenger: registrar.messenger())
binaryMessenger: binaryMessenger)

let connectivityProvider: ConnectivityProvider
if #available(iOS 12, *) {
if #available(iOS 12, macOS 10.14, *) {
connectivityProvider = PathMonitorConnectivityProvider()
} else {
connectivityProvider = ReachabilityConnectivityProvider()
Expand All @@ -44,7 +55,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 +75,27 @@ public class SwiftConnectivityPlusPlugin: NSObject, FlutterPlugin, FlutterStream
return "none"
}
}

private func statusFrom(connectivityTypes: [ConnectivityType]) -> [String] {
return connectivityTypes.map {
self.statusFrom(connectivityType: $0)
}
}

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
Loading
Loading