diff --git a/lib/notification_manager.dart b/lib/notification_manager.dart
index 00042bcc..448204d2 100644
--- a/lib/notification_manager.dart
+++ b/lib/notification_manager.dart
@@ -14,7 +14,7 @@ import 'package:flutter_local_notifications_linux/src/model/icon.dart';
import 'package:path/path.dart' as path;
-import 'config.dart';
+import '../../config.dart';
// NotificationsManager provides a wrapper around platform specific notifications logic.
abstract class NotificationsManager {
diff --git a/lib/third_party/connectivity_plus/LICENSE b/lib/third_party/connectivity_plus/LICENSE
new file mode 100644
index 00000000..18c6ba27
--- /dev/null
+++ b/lib/third_party/connectivity_plus/LICENSE
@@ -0,0 +1,27 @@
+Copyright 2017 The Chromium Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
diff --git a/lib/third_party/connectivity_plus/connectivity_plus/LICENSE b/lib/third_party/connectivity_plus/connectivity_plus/LICENSE
new file mode 100644
index 00000000..7b995420
--- /dev/null
+++ b/lib/third_party/connectivity_plus/connectivity_plus/LICENSE
@@ -0,0 +1,27 @@
+Copyright 2017 The Chromium Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/lib/third_party/connectivity_plus/connectivity_plus/README.md b/lib/third_party/connectivity_plus/connectivity_plus/README.md
new file mode 100644
index 00000000..8e5b2956
--- /dev/null
+++ b/lib/third_party/connectivity_plus/connectivity_plus/README.md
@@ -0,0 +1,106 @@
+# connectivity_plus
+
+[![Flutter Community: connectivity_plus](https://fluttercommunity.dev/_github/header/connectivity_plus)](https://github.com/fluttercommunity/community)
+
+[![pub package](https://img.shields.io/pub/v/connectivity_plus.svg)](https://pub.dev/packages/connectivity_plus)
+[![pub points](https://img.shields.io/pub/points/connectivity_plus?color=2E8B57&label=pub%20points)](https://pub.dev/packages/connectivity_plus/score)
+[![connectivity_plus](https://github.com/fluttercommunity/plus_plugins/actions/workflows/connectivity_plus.yaml/badge.svg)](https://github.com/fluttercommunity/plus_plugins/actions/workflows/connectivity_plus.yaml)
+
+
+
+
+
+This plugin allows Flutter apps to discover network connectivity and configure
+themselves accordingly. It can distinguish between cellular vs WiFi connection.
+
+> **Note**
+>
+> On Android, this does not guarantee connection to Internet. For instance, the app might have wifi access but it might be a VPN or a hotel WiFi > with no access.
+
+## Platform Support
+
+| Android | iOS | MacOS | Web | Linux | Windows |
+| :-----: | :-: | :---: | :-: | :---: | :-----: |
+| ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
+
+## Usage
+
+Sample usage to check current status:
+
+```dart
+import 'package:connectivity_plus/connectivity_plus.dart';
+
+final connectivityResult = await (Connectivity().checkConnectivity());
+if (connectivityResult == ConnectivityResult.mobile) {
+ // I am connected to a mobile network.
+} else if (connectivityResult == ConnectivityResult.wifi) {
+ // I am connected to a wifi network.
+} else if (connectivityResult == ConnectivityResult.ethernet) {
+ // I am connected to a ethernet network.
+} else if (connectivityResult == ConnectivityResult.vpn) {
+ // I am connected to a vpn network.
+ // Note for iOS and macOS:
+ // There is no separate network interface type for [vpn].
+ // It returns [other] on any device (also simulator)
+} else if (connectivityResult == ConnectivityResult.bluetooth) {
+ // I am connected to a bluetooth.
+} else if (connectivityResult == ConnectivityResult.other) {
+ // I am connected to a network which is not in the above mentioned networks.
+} else if (connectivityResult == ConnectivityResult.none) {
+ // I am not connected to any network.
+}
+```
+
+> **Note**
+>
+> You should not be using the current network status for deciding whether you can reliably make a network connection. Always guard your app code against timeouts and errors that might come from the network layer.
+
+You can also listen for network state changes by subscribing to the stream
+exposed by connectivity plugin:
+
+```dart
+import 'package:connectivity_plus/connectivity_plus.dart';
+
+@override
+initState() {
+ super.initState();
+
+ subscription = Connectivity().onConnectivityChanged.listen((ConnectivityResult result) {
+ // Got a new connectivity status!
+ });
+}
+
+// Be sure to cancel subscription after you are done
+@override
+dispose() {
+ subscription.cancel();
+ super.dispose();
+}
+```
+
+> **Note**
+>
+> Connectivity changes are no longer communicated to Android apps in the background starting with Android O (8.0). _You should always check for connectivity status when your app is resumed._ The broadcast is only useful when your application is in the foreground.
+
+## Limitations on the web platform
+
+In order to retrieve information about the quality/speed of a browser's connection, the web implementation of the `connectivity` plugin uses the browser's [**NetworkInformation** Web API](https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation), which as of this writing (June 2020) is still "experimental", and not available in all browsers:
+
+![Data on support for the netinfo feature across the major browsers from caniuse.com](https://caniuse.bitsofco.de/image/netinfo.png)
+
+On desktop browsers, this API only returns a very broad set of connectivity statuses (One of `'slow-2g', '2g', '3g', or '4g'`), and may _not_ provide a Stream of changes. Firefox still hasn't enabled this feature by default.
+
+**Fallback to `navigator.onLine`**
+
+For those browsers where the NetworkInformation Web API is not available, the plugin falls back to the [**NavigatorOnLine** Web API](https://developer.mozilla.org/en-US/docs/Web/API/NavigatorOnLine), which is more broadly supported:
+
+![Data on support for the online-status feature across the major browsers from caniuse.com](https://caniuse.bitsofco.de/image/online-status.png)
+
+The NavigatorOnLine API is [provided by `dart:html`](https://api.dart.dev/stable/2.7.2/dart-html/Navigator/onLine.html), and only supports a boolean connectivity status (either online or offline), with no network speed information. In those cases the plugin will return either `wifi` (when the browser is online) or `none` (when it's not).
+
+Other than the approximate "downlink" speed, where available, and due to security and privacy concerns, **no Web browser will provide** any specific information about the actual network your users' device is connected to, like **the SSID on a Wi-Fi, or the MAC address of their device.**
+
+## Learn more
+
+- [API Documentation](https://pub.dev/documentation/connectivity_plus/latest/connectivity_plus/connectivity_plus-library.html)
+- [Plugin documentation website](https://plus.fluttercommunity.dev/docs/connectivity_plus/overview)
diff --git a/lib/third_party/connectivity_plus/connectivity_plus/android/build.gradle b/lib/third_party/connectivity_plus/connectivity_plus/android/build.gradle
new file mode 100644
index 00000000..e04630a8
--- /dev/null
+++ b/lib/third_party/connectivity_plus/connectivity_plus/android/build.gradle
@@ -0,0 +1,41 @@
+group 'io.flutter.plugins.connectivity'
+version '1.0-SNAPSHOT'
+
+buildscript {
+ repositories {
+ google()
+ mavenCentral()
+ }
+
+ dependencies {
+ classpath 'com.android.tools.build:gradle:7.4.2'
+ }
+}
+
+rootProject.allprojects {
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+
+apply plugin: 'com.android.library'
+
+android {
+ compileSdkVersion 31
+
+ namespace 'dev.fluttercommunity.plus.connectivity'
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ defaultConfig {
+ minSdkVersion 16
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+ lintOptions {
+ disable 'InvalidPackage'
+ }
+}
diff --git a/lib/third_party/connectivity_plus/connectivity_plus/android/gradle.properties b/lib/third_party/connectivity_plus/connectivity_plus/android/gradle.properties
new file mode 100644
index 00000000..8bd86f68
--- /dev/null
+++ b/lib/third_party/connectivity_plus/connectivity_plus/android/gradle.properties
@@ -0,0 +1 @@
+org.gradle.jvmargs=-Xmx1536M
diff --git a/lib/third_party/connectivity_plus/connectivity_plus/android/gradle/wrapper/gradle-wrapper.properties b/lib/third_party/connectivity_plus/connectivity_plus/android/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 00000000..62dbe3c1
--- /dev/null
+++ b/lib/third_party/connectivity_plus/connectivity_plus/android/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-bin.zip
+distributionPath=wrapper/dists
+zipStorePath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
diff --git a/lib/third_party/connectivity_plus/connectivity_plus/android/settings.gradle b/lib/third_party/connectivity_plus/connectivity_plus/android/settings.gradle
new file mode 100644
index 00000000..4fbed475
--- /dev/null
+++ b/lib/third_party/connectivity_plus/connectivity_plus/android/settings.gradle
@@ -0,0 +1 @@
+rootProject.name = 'connectivity'
diff --git a/lib/third_party/connectivity_plus/connectivity_plus/android/src/main/AndroidManifest.xml b/lib/third_party/connectivity_plus/connectivity_plus/android/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..6435d2cb
--- /dev/null
+++ b/lib/third_party/connectivity_plus/connectivity_plus/android/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+
+
+
diff --git a/lib/third_party/connectivity_plus/connectivity_plus/android/src/main/java/dev/fluttercommunity/plus/connectivity/Connectivity.java b/lib/third_party/connectivity_plus/connectivity_plus/android/src/main/java/dev/fluttercommunity/plus/connectivity/Connectivity.java
new file mode 100644
index 00000000..02a316bc
--- /dev/null
+++ b/lib/third_party/connectivity_plus/connectivity_plus/android/src/main/java/dev/fluttercommunity/plus/connectivity/Connectivity.java
@@ -0,0 +1,83 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package dev.fluttercommunity.plus.connectivity;
+
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.os.Build;
+
+/** Reports connectivity related information such as connectivity type and wifi information. */
+public class Connectivity {
+ static final String CONNECTIVITY_NONE = "none";
+ static final String CONNECTIVITY_WIFI = "wifi";
+ static final String CONNECTIVITY_MOBILE = "mobile";
+ static final String CONNECTIVITY_ETHERNET = "ethernet";
+ static final String CONNECTIVITY_BLUETOOTH = "bluetooth";
+ static final String CONNECTIVITY_VPN = "vpn";
+ private final ConnectivityManager connectivityManager;
+
+ public Connectivity(ConnectivityManager connectivityManager) {
+ this.connectivityManager = connectivityManager;
+ }
+
+ String getNetworkType() {
+ 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;
+ }
+ if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) {
+ return CONNECTIVITY_ETHERNET;
+ }
+ if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN)) {
+ return CONNECTIVITY_VPN;
+ }
+ if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
+ return CONNECTIVITY_MOBILE;
+ }
+ if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH)) {
+ return CONNECTIVITY_BLUETOOTH;
+ }
+ }
+
+ return getNetworkTypeLegacy();
+ }
+
+ @SuppressWarnings("deprecation")
+ private String getNetworkTypeLegacy() {
+ // 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();
+ switch (type) {
+ case ConnectivityManager.TYPE_BLUETOOTH:
+ return CONNECTIVITY_BLUETOOTH;
+ case ConnectivityManager.TYPE_ETHERNET:
+ return CONNECTIVITY_ETHERNET;
+ case ConnectivityManager.TYPE_WIFI:
+ case ConnectivityManager.TYPE_WIMAX:
+ return CONNECTIVITY_WIFI;
+ case ConnectivityManager.TYPE_VPN:
+ return CONNECTIVITY_VPN;
+ case ConnectivityManager.TYPE_MOBILE:
+ case ConnectivityManager.TYPE_MOBILE_DUN:
+ case ConnectivityManager.TYPE_MOBILE_HIPRI:
+ return CONNECTIVITY_MOBILE;
+ default:
+ return CONNECTIVITY_NONE;
+ }
+ }
+
+ public ConnectivityManager getConnectivityManager() {
+ return connectivityManager;
+ }
+}
diff --git a/lib/third_party/connectivity_plus/connectivity_plus/android/src/main/java/dev/fluttercommunity/plus/connectivity/ConnectivityBroadcastReceiver.java b/lib/third_party/connectivity_plus/connectivity_plus/android/src/main/java/dev/fluttercommunity/plus/connectivity/ConnectivityBroadcastReceiver.java
new file mode 100644
index 00000000..22ecacd7
--- /dev/null
+++ b/lib/third_party/connectivity_plus/connectivity_plus/android/src/main/java/dev/fluttercommunity/plus/connectivity/ConnectivityBroadcastReceiver.java
@@ -0,0 +1,94 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package dev.fluttercommunity.plus.connectivity;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
+import io.flutter.plugin.common.EventChannel;
+
+/**
+ * The ConnectivityBroadcastReceiver receives the connectivity updates and send them to the UIThread
+ * through an {@link EventChannel.EventSink}
+ *
+ * Use {@link
+ * io.flutter.plugin.common.EventChannel#setStreamHandler(io.flutter.plugin.common.EventChannel.StreamHandler)}
+ * to set up the receiver.
+ */
+public class ConnectivityBroadcastReceiver extends BroadcastReceiver
+ implements EventChannel.StreamHandler {
+ private final Context context;
+ private final Connectivity connectivity;
+ private EventChannel.EventSink events;
+ private final Handler mainHandler = new Handler(Looper.getMainLooper());
+ private ConnectivityManager.NetworkCallback networkCallback;
+ public static final String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";
+
+ public ConnectivityBroadcastReceiver(Context context, Connectivity connectivity) {
+ this.context = context;
+ this.connectivity = connectivity;
+ }
+
+ @Override
+ public void onListen(Object arguments, EventChannel.EventSink events) {
+ this.events = events;
+ if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ networkCallback =
+ new ConnectivityManager.NetworkCallback() {
+ @Override
+ public void onAvailable(Network network) {
+ sendEvent();
+ }
+
+ @Override
+ public void onLost(Network network) {
+ sendEvent(Connectivity.CONNECTIVITY_NONE);
+ }
+ };
+ connectivity.getConnectivityManager().registerDefaultNetworkCallback(networkCallback);
+ } else {
+ context.registerReceiver(this, new IntentFilter(CONNECTIVITY_ACTION));
+ }
+ }
+
+ @Override
+ public void onCancel(Object arguments) {
+ if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ if (networkCallback != null) {
+ connectivity.getConnectivityManager().unregisterNetworkCallback(networkCallback);
+ networkCallback = null;
+ }
+ } else {
+ try {
+ context.unregisterReceiver(this);
+ } catch (Exception e) {
+ //listen never called, ignore the error
+ }
+ }
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (events != null) {
+ events.success(connectivity.getNetworkType());
+ }
+ }
+
+ private void sendEvent() {
+ Runnable runnable = () -> events.success(connectivity.getNetworkType());
+ mainHandler.post(runnable);
+ }
+
+ private void sendEvent(final String networkType) {
+ Runnable runnable = () -> events.success(networkType);
+ mainHandler.post(runnable);
+ }
+}
diff --git a/lib/third_party/connectivity_plus/connectivity_plus/android/src/main/java/dev/fluttercommunity/plus/connectivity/ConnectivityMethodChannelHandler.java b/lib/third_party/connectivity_plus/connectivity_plus/android/src/main/java/dev/fluttercommunity/plus/connectivity/ConnectivityMethodChannelHandler.java
new file mode 100644
index 00000000..ad12f66d
--- /dev/null
+++ b/lib/third_party/connectivity_plus/connectivity_plus/android/src/main/java/dev/fluttercommunity/plus/connectivity/ConnectivityMethodChannelHandler.java
@@ -0,0 +1,37 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package dev.fluttercommunity.plus.connectivity;
+
+import androidx.annotation.NonNull;
+import io.flutter.plugin.common.MethodCall;
+import io.flutter.plugin.common.MethodChannel;
+
+/**
+ * The handler receives {@link MethodCall}s from the UIThread, gets the related information from
+ * a @{@link Connectivity}, and then send the result back to the UIThread through the {@link
+ * MethodChannel.Result}.
+ */
+class ConnectivityMethodChannelHandler implements MethodChannel.MethodCallHandler {
+
+ private final Connectivity connectivity;
+
+ /**
+ * Construct the ConnectivityMethodChannelHandler with a {@code connectivity}. The {@code
+ * connectivity} must not be null.
+ */
+ ConnectivityMethodChannelHandler(Connectivity connectivity) {
+ assert (connectivity != null);
+ this.connectivity = connectivity;
+ }
+
+ @Override
+ public void onMethodCall(MethodCall call, @NonNull MethodChannel.Result result) {
+ if ("check".equals(call.method)) {
+ result.success(connectivity.getNetworkType());
+ } else {
+ result.notImplemented();
+ }
+ }
+}
diff --git a/lib/third_party/connectivity_plus/connectivity_plus/android/src/main/java/dev/fluttercommunity/plus/connectivity/ConnectivityPlugin.java b/lib/third_party/connectivity_plus/connectivity_plus/android/src/main/java/dev/fluttercommunity/plus/connectivity/ConnectivityPlugin.java
new file mode 100644
index 00000000..666cfb83
--- /dev/null
+++ b/lib/third_party/connectivity_plus/connectivity_plus/android/src/main/java/dev/fluttercommunity/plus/connectivity/ConnectivityPlugin.java
@@ -0,0 +1,56 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package dev.fluttercommunity.plus.connectivity;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import androidx.annotation.NonNull;
+import io.flutter.embedding.engine.plugins.FlutterPlugin;
+import io.flutter.plugin.common.BinaryMessenger;
+import io.flutter.plugin.common.EventChannel;
+import io.flutter.plugin.common.MethodChannel;
+
+/** ConnectivityPlugin */
+public class ConnectivityPlugin implements FlutterPlugin {
+
+ private MethodChannel methodChannel;
+ private EventChannel eventChannel;
+ private ConnectivityBroadcastReceiver receiver;
+
+ @Override
+ public void onAttachedToEngine(FlutterPluginBinding binding) {
+ setupChannels(binding.getBinaryMessenger(), binding.getApplicationContext());
+ }
+
+ @Override
+ public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
+ teardownChannels();
+ }
+
+ private void setupChannels(BinaryMessenger messenger, Context context) {
+ methodChannel = new MethodChannel(messenger, "dev.fluttercommunity.plus/connectivity");
+ eventChannel = new EventChannel(messenger, "dev.fluttercommunity.plus/connectivity_status");
+ ConnectivityManager connectivityManager =
+ (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+
+ Connectivity connectivity = new Connectivity(connectivityManager);
+
+ ConnectivityMethodChannelHandler methodChannelHandler =
+ new ConnectivityMethodChannelHandler(connectivity);
+ receiver = new ConnectivityBroadcastReceiver(context, connectivity);
+
+ methodChannel.setMethodCallHandler(methodChannelHandler);
+ eventChannel.setStreamHandler(receiver);
+ }
+
+ private void teardownChannels() {
+ methodChannel.setMethodCallHandler(null);
+ eventChannel.setStreamHandler(null);
+ receiver.onCancel(null);
+ methodChannel = null;
+ eventChannel = null;
+ receiver = null;
+ }
+}
diff --git a/lib/third_party/connectivity_plus/connectivity_plus/ios/Assets/.gitkeep b/lib/third_party/connectivity_plus/connectivity_plus/ios/Assets/.gitkeep
new file mode 100644
index 00000000..e69de29b
diff --git a/lib/third_party/connectivity_plus/connectivity_plus/ios/Classes/ConnectivityPlusPlugin.h b/lib/third_party/connectivity_plus/connectivity_plus/ios/Classes/ConnectivityPlusPlugin.h
new file mode 100644
index 00000000..e5d0ccbd
--- /dev/null
+++ b/lib/third_party/connectivity_plus/connectivity_plus/ios/Classes/ConnectivityPlusPlugin.h
@@ -0,0 +1,4 @@
+#import
+
+@interface ConnectivityPlusPlugin : NSObject
+@end
diff --git a/lib/third_party/connectivity_plus/connectivity_plus/ios/Classes/ConnectivityPlusPlugin.m b/lib/third_party/connectivity_plus/connectivity_plus/ios/Classes/ConnectivityPlusPlugin.m
new file mode 100644
index 00000000..43af3800
--- /dev/null
+++ b/lib/third_party/connectivity_plus/connectivity_plus/ios/Classes/ConnectivityPlusPlugin.m
@@ -0,0 +1,15 @@
+#import "ConnectivityPlusPlugin.h"
+#if __has_include()
+#import
+#else
+// Support project import fallback if the generated compatibility header
+// is not copied when this plugin is created as a library.
+// https://forums.swift.org/t/swift-static-libraries-dont-copy-generated-objective-c-header/19816
+#import "connectivity_plus-Swift.h"
+#endif
+
+@implementation ConnectivityPlusPlugin
++ (void)registerWithRegistrar:(NSObject *)registrar {
+ [SwiftConnectivityPlusPlugin registerWithRegistrar:registrar];
+}
+@end
diff --git a/lib/third_party/connectivity_plus/connectivity_plus/ios/Classes/ConnectivityProvider.swift b/lib/third_party/connectivity_plus/connectivity_plus/ios/Classes/ConnectivityProvider.swift
new file mode 100644
index 00000000..38156822
--- /dev/null
+++ b/lib/third_party/connectivity_plus/connectivity_plus/ios/Classes/ConnectivityProvider.swift
@@ -0,0 +1,21 @@
+import Foundation
+
+public enum ConnectivityType {
+ case none
+ case wiredEthernet
+ case wifi
+ case cellular
+ case other
+}
+
+public protocol ConnectivityProvider: NSObjectProtocol {
+ typealias ConnectivityUpdateHandler = (ConnectivityType) -> Void
+
+ var currentConnectivityType: ConnectivityType { get }
+
+ var connectivityUpdateHandler: ConnectivityUpdateHandler? { get set }
+
+ func start()
+
+ func stop()
+}
diff --git a/lib/third_party/connectivity_plus/connectivity_plus/ios/Classes/PathMonitorConnectivityProvider.swift b/lib/third_party/connectivity_plus/connectivity_plus/ios/Classes/PathMonitorConnectivityProvider.swift
new file mode 100644
index 00000000..5447780d
--- /dev/null
+++ b/lib/third_party/connectivity_plus/connectivity_plus/ios/Classes/PathMonitorConnectivityProvider.swift
@@ -0,0 +1,60 @@
+import Foundation
+import Network
+
+@available(iOS 12, *)
+public class PathMonitorConnectivityProvider: NSObject, ConnectivityProvider {
+
+ private let queue = DispatchQueue.global(qos: .background)
+
+ private var _pathMonitor: NWPathMonitor?
+
+ public var currentConnectivityType: ConnectivityType {
+ let path = ensurePathMonitor().currentPath
+ // .satisfied means that the network is available
+ 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
+ }
+ }
+ return .none
+ }
+
+ public var connectivityUpdateHandler: ConnectivityUpdateHandler?
+
+ override init() {
+ super.init()
+ _ = ensurePathMonitor()
+ }
+
+ public func start() {
+ _ = ensurePathMonitor()
+ }
+
+ public func stop() {
+ _pathMonitor?.cancel()
+ _pathMonitor = nil
+ }
+
+ @discardableResult
+ private func ensurePathMonitor() -> NWPathMonitor {
+ if (_pathMonitor == nil) {
+ let pathMonitor = NWPathMonitor()
+ pathMonitor.start(queue: queue)
+ pathMonitor.pathUpdateHandler = pathUpdateHandler
+ _pathMonitor = pathMonitor
+ }
+ return _pathMonitor!
+ }
+
+ private func pathUpdateHandler(path: NWPath) {
+ connectivityUpdateHandler?(currentConnectivityType)
+ }
+}
diff --git a/lib/third_party/connectivity_plus/connectivity_plus/ios/Classes/ReachabilityConnectivityProvider.swift b/lib/third_party/connectivity_plus/connectivity_plus/ios/Classes/ReachabilityConnectivityProvider.swift
new file mode 100644
index 00000000..4398a12e
--- /dev/null
+++ b/lib/third_party/connectivity_plus/connectivity_plus/ios/Classes/ReachabilityConnectivityProvider.swift
@@ -0,0 +1,59 @@
+import Foundation
+import Reachability
+
+public class ReachabilityConnectivityProvider: NSObject, ConnectivityProvider {
+ private var _reachability: Reachability?
+
+ public var currentConnectivityType: ConnectivityType {
+ let reachability = ensureReachability()
+ switch reachability.connection {
+ case .wifi:
+ return .wifi
+ case .cellular:
+ return .cellular
+ default:
+ return .none
+ }
+ }
+
+ public var connectivityUpdateHandler: ConnectivityUpdateHandler?
+
+ override init() {
+ super.init()
+ _ = ensureReachability()
+ }
+
+ public func start() {
+ let reachability = ensureReachability()
+
+ NotificationCenter.default.addObserver(
+ self,
+ selector: #selector(reachabilityChanged),
+ name: .reachabilityChanged,
+ object: reachability)
+
+ try? reachability.startNotifier()
+ }
+
+ public func stop() {
+ NotificationCenter.default.removeObserver(
+ self,
+ name: .reachabilityChanged,
+ object: _reachability)
+
+ _reachability?.stopNotifier()
+ _reachability = nil
+ }
+
+ private func ensureReachability() -> Reachability {
+ if (_reachability == nil) {
+ let reachability = try? Reachability()
+ _reachability = reachability
+ }
+ return _reachability!
+ }
+
+ @objc private func reachabilityChanged(notification: NSNotification) {
+ connectivityUpdateHandler?(currentConnectivityType)
+ }
+}
diff --git a/lib/third_party/connectivity_plus/connectivity_plus/ios/Classes/SwiftConnectivityPlusPlugin.swift b/lib/third_party/connectivity_plus/connectivity_plus/ios/Classes/SwiftConnectivityPlusPlugin.swift
new file mode 100644
index 00000000..c969b4e5
--- /dev/null
+++ b/lib/third_party/connectivity_plus/connectivity_plus/ios/Classes/SwiftConnectivityPlusPlugin.swift
@@ -0,0 +1,89 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+import Flutter
+
+public class SwiftConnectivityPlusPlugin: NSObject, FlutterPlugin, FlutterStreamHandler {
+ private let connectivityProvider: ConnectivityProvider
+ private var eventSink: FlutterEventSink?
+
+ init(connectivityProvider: ConnectivityProvider) {
+ self.connectivityProvider = connectivityProvider
+ super.init()
+ self.connectivityProvider.connectivityUpdateHandler = connectivityUpdateHandler
+ }
+
+ public static func register(with registrar: FlutterPluginRegistrar) {
+ let channel = FlutterMethodChannel(
+ name: "dev.fluttercommunity.plus/connectivity",
+ binaryMessenger: registrar.messenger())
+
+ let streamChannel = FlutterEventChannel(
+ name: "dev.fluttercommunity.plus/connectivity_status",
+ binaryMessenger: registrar.messenger())
+
+ let connectivityProvider: ConnectivityProvider
+ if #available(iOS 12, *) {
+ connectivityProvider = PathMonitorConnectivityProvider()
+ } else {
+ connectivityProvider = ReachabilityConnectivityProvider()
+ }
+
+ let instance = SwiftConnectivityPlusPlugin(connectivityProvider: connectivityProvider)
+ streamChannel.setStreamHandler(instance)
+
+ registrar.addMethodCallDelegate(instance, channel: channel)
+ }
+
+ public func detachFromEngine(for registrar: FlutterPluginRegistrar) {
+ eventSink = nil
+ connectivityProvider.stop()
+ }
+
+ public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
+ switch call.method {
+ case "check":
+ result(statusFrom(connectivityType: connectivityProvider.currentConnectivityType))
+ default:
+ result(FlutterMethodNotImplemented)
+ }
+ }
+
+ private func statusFrom(connectivityType: ConnectivityType) -> String {
+ switch connectivityType {
+ case .wifi:
+ return "wifi"
+ case .cellular:
+ return "mobile"
+ case .wiredEthernet:
+ return "ethernet"
+ case .other:
+ return "other"
+ case .none:
+ return "none"
+ }
+ }
+
+ public func onListen(
+ withArguments _: Any?,
+ eventSink events: @escaping FlutterEventSink
+ ) -> FlutterError? {
+ eventSink = events
+ connectivityProvider.start()
+ connectivityUpdateHandler(connectivityType: connectivityProvider.currentConnectivityType)
+ return nil
+ }
+
+ private func connectivityUpdateHandler(connectivityType: ConnectivityType) {
+ DispatchQueue.main.async {
+ self.eventSink?(self.statusFrom(connectivityType: connectivityType))
+ }
+ }
+
+ public func onCancel(withArguments _: Any?) -> FlutterError? {
+ connectivityProvider.stop()
+ eventSink = nil
+ return nil
+ }
+}
diff --git a/lib/third_party/connectivity_plus/connectivity_plus/ios/connectivity_plus.podspec b/lib/third_party/connectivity_plus/connectivity_plus/ios/connectivity_plus.podspec
new file mode 100644
index 00000000..f591b337
--- /dev/null
+++ b/lib/third_party/connectivity_plus/connectivity_plus/ios/connectivity_plus.podspec
@@ -0,0 +1,24 @@
+#
+# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html
+#
+Pod::Spec.new do |s|
+ s.name = 'connectivity_plus'
+ s.version = '0.0.1'
+ s.summary = 'Flutter Connectivity'
+ s.description = <<-DESC
+This plugin allows Flutter apps to discover network connectivity and configure themselves accordingly.
+Downloaded by pub (not CocoaPods).
+ DESC
+ s.homepage = 'https://plus.fluttercommunity.dev/'
+ s.license = { :type => 'BSD', :file => '../LICENSE' }
+ s.author = { 'Flutter Community Team' => 'authors@fluttercommunity.dev' }
+ s.source = { :http => 'https://github.com/fluttercommunity/plus_plugins' }
+ s.documentation_url = 'https://pub.dev/packages/connectivity_plus'
+ s.source_files = 'Classes/**/*'
+ s.public_header_files = 'Classes/**/*.h'
+ s.dependency 'Flutter'
+ s.dependency 'ReachabilitySwift'
+ s.platform = :ios, '9.0'
+ s.swift_version = '5.0'
+ s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS[sdk=iphonesimulator*]' => 'x86_64' }
+end
diff --git a/lib/third_party/connectivity_plus/connectivity_plus/lib/connectivity_plus.dart b/lib/third_party/connectivity_plus/connectivity_plus/lib/connectivity_plus.dart
new file mode 100644
index 00000000..38cf3515
--- /dev/null
+++ b/lib/third_party/connectivity_plus/connectivity_plus/lib/connectivity_plus.dart
@@ -0,0 +1,55 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:connectivity_plus_platform_interface/connectivity_plus_platform_interface.dart';
+
+// Export enums from the platform_interface so plugin users can use them directly.
+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';
+
+/// Discover network connectivity configurations: Distinguish between WI-FI and cellular, check WI-FI status and more.
+class Connectivity {
+ /// Constructs a singleton instance of [Connectivity].
+ ///
+ /// [Connectivity] is designed to work as a singleton.
+ // When a second instance is created, the first instance will not be able to listen to the
+ // EventChannel because it is overridden. Forcing the class to be a singleton class can prevent
+ // misuse of creating a second instance from a programmer.
+ factory Connectivity() {
+ _singleton ??= Connectivity._();
+ return _singleton!;
+ }
+
+ Connectivity._();
+
+ static Connectivity? _singleton;
+
+ static ConnectivityPlatform get _platform {
+ return ConnectivityPlatform.instance;
+ }
+
+ /// Fires whenever the connectivity state changes.
+ ///
+ /// 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 {
+ return _platform.onConnectivityChanged;
+ }
+
+ /// Checks the connection status of the device.
+ ///
+ /// Do not use the result of this function to decide whether you can reliably
+ /// make a network request. It only gives you the radio status.
+ ///
+ /// Instead listen for connectivity changes via [onConnectivityChanged] stream.
+ Future checkConnectivity() {
+ return _platform.checkConnectivity();
+ }
+}
diff --git a/lib/third_party/connectivity_plus/connectivity_plus/lib/src/connectivity_plus_linux.dart b/lib/third_party/connectivity_plus/connectivity_plus/lib/src/connectivity_plus_linux.dart
new file mode 100644
index 00000000..bca6cdfe
--- /dev/null
+++ b/lib/third_party/connectivity_plus/connectivity_plus/lib/src/connectivity_plus_linux.dart
@@ -0,0 +1,85 @@
+import 'dart:async';
+
+import 'package:connectivity_plus_platform_interface/connectivity_plus_platform_interface.dart';
+import 'package:meta/meta.dart';
+import 'package:cwtch/third_party/nm/nm.dart';
+
+// Used internally
+// ignore_for_file: public_member_api_docs
+
+@visibleForTesting
+typedef NetworkManagerClientFactory = NetworkManagerClient Function();
+
+/// The Linux implementation of ConnectivityPlatform.
+class ConnectivityPlusLinuxPlugin extends ConnectivityPlatform {
+ /// Register this dart class as the platform implementation for linux
+ static void registerWith() {
+ ConnectivityPlatform.instance = ConnectivityPlusLinuxPlugin();
+ }
+
+ /// Checks the connection status of the device.
+ @override
+ Future checkConnectivity() async {
+ final client = createClient();
+ await client.connect();
+ final connectivity = _getConnectivity(client);
+ await client.close();
+ return connectivity;
+ }
+
+ NetworkManagerClient? _client;
+ StreamController? _controller;
+
+ /// Returns a Stream of ConnectivityResults changes.
+ @override
+ Stream get onConnectivityChanged {
+ _controller ??= StreamController.broadcast(
+ onListen: _startListenConnectivity,
+ onCancel: _stopListenConnectivity,
+ );
+ return _controller!.stream;
+ }
+
+ ConnectivityResult _getConnectivity(NetworkManagerClient client) {
+ if (client.connectivity != NetworkManagerConnectivityState.full) {
+ 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;
+ }
+ return ConnectivityResult.mobile;
+ }
+
+ Future _startListenConnectivity() async {
+ _client ??= createClient();
+ await _client!.connect();
+ _addConnectivity(_client!);
+ _client!.propertiesChanged.listen((properties) {
+ if (properties.contains('Connectivity')) {
+ _addConnectivity(_client!);
+ }
+ });
+ }
+
+ void _addConnectivity(NetworkManagerClient client) {
+ _controller!.add(_getConnectivity(client));
+ }
+
+ Future _stopListenConnectivity() async {
+ await _client?.close();
+ _client = null;
+ }
+
+ @visibleForTesting
+ // ignore: prefer_function_declarations_over_variables
+ NetworkManagerClientFactory createClient = () => NetworkManagerClient();
+}
diff --git a/lib/third_party/connectivity_plus/connectivity_plus/lib/src/connectivity_plus_web.dart b/lib/third_party/connectivity_plus/connectivity_plus/lib/src/connectivity_plus_web.dart
new file mode 100644
index 00000000..28c1db1d
--- /dev/null
+++ b/lib/third_party/connectivity_plus/connectivity_plus/lib/src/connectivity_plus_web.dart
@@ -0,0 +1,27 @@
+import 'package:connectivity_plus_platform_interface/connectivity_plus_platform_interface.dart';
+import 'package:flutter_web_plugins/flutter_web_plugins.dart';
+
+import 'web/network_information_api_connectivity_plugin.dart';
+import 'web/dart_html_connectivity_plugin.dart';
+
+/// The web implementation of the ConnectivityPlatform of the Connectivity plugin.
+class ConnectivityPlusWebPlugin extends ConnectivityPlatform {
+ /// Factory method that initializes the connectivity plugin platform with an instance
+ /// of the plugin for the web.
+ static void registerWith(Registrar registrar) {
+ // Since the `NetworkInformationApi` is currently an experimental API and
+ // does not provide a reliable way to check a connectivity change
+ // from an onnline state to an offline state,
+ // its implementation is disabled for now.
+ // See also: https://developer.mozilla.org/en-US/docs/Web/API/Network_Information_API
+ //
+ // TODO: use `NetworkInformationApiConnectivityPlugin.isSupported()` when it becomes a stable DOM API.
+ const isSupported = false;
+
+ if (isSupported) {
+ ConnectivityPlatform.instance = NetworkInformationApiConnectivityPlugin();
+ } else {
+ ConnectivityPlatform.instance = DartHtmlConnectivityPlugin();
+ }
+ }
+}
diff --git a/lib/third_party/connectivity_plus/connectivity_plus/lib/src/web/dart_html_connectivity_plugin.dart b/lib/third_party/connectivity_plus/connectivity_plus/lib/src/web/dart_html_connectivity_plugin.dart
new file mode 100644
index 00000000..d5c0c68b
--- /dev/null
+++ b/lib/third_party/connectivity_plus/connectivity_plus/lib/src/web/dart_html_connectivity_plugin.dart
@@ -0,0 +1,35 @@
+import 'dart:async';
+import 'dart:html' as html show window;
+
+import 'package:connectivity_plus_platform_interface/connectivity_plus_platform_interface.dart';
+
+import '../connectivity_plus_web.dart';
+
+/// The web implementation of the ConnectivityPlatform of the Connectivity plugin.
+class DartHtmlConnectivityPlugin extends ConnectivityPlusWebPlugin {
+ /// Checks the connection status of the device.
+ @override
+ Future checkConnectivity() async {
+ return (html.window.navigator.onLine ?? false)
+ ? ConnectivityResult.wifi
+ : ConnectivityResult.none;
+ }
+
+ StreamController? _connectivityResult;
+
+ /// Returns a Stream of ConnectivityResults changes.
+ @override
+ Stream get onConnectivityChanged {
+ if (_connectivityResult == null) {
+ _connectivityResult = StreamController.broadcast();
+ // Fallback to dart:html window.onOnline / window.onOffline
+ html.window.onOnline.listen((event) {
+ _connectivityResult!.add(ConnectivityResult.wifi);
+ });
+ html.window.onOffline.listen((event) {
+ _connectivityResult!.add(ConnectivityResult.none);
+ });
+ }
+ return _connectivityResult!.stream;
+ }
+}
diff --git a/lib/third_party/connectivity_plus/connectivity_plus/lib/src/web/network_information_api_connectivity_plugin.dart b/lib/third_party/connectivity_plus/connectivity_plus/lib/src/web/network_information_api_connectivity_plugin.dart
new file mode 100644
index 00000000..bc822a76
--- /dev/null
+++ b/lib/third_party/connectivity_plus/connectivity_plus/lib/src/web/network_information_api_connectivity_plugin.dart
@@ -0,0 +1,92 @@
+import 'dart:async';
+import 'dart:html' as html show window, NetworkInformation;
+import 'dart:js_util';
+
+import 'package:connectivity_plus/src/web/utils/connectivity_result.dart';
+import 'package:connectivity_plus_platform_interface/connectivity_plus_platform_interface.dart';
+import 'package:flutter/foundation.dart';
+import 'package:js/js.dart';
+
+import '../connectivity_plus_web.dart';
+
+/// The web implementation of the ConnectivityPlatform of the Connectivity plugin.
+class NetworkInformationApiConnectivityPlugin
+ extends ConnectivityPlusWebPlugin {
+ final html.NetworkInformation _networkInformation;
+
+ /// A check to determine if this version of the plugin can be used.
+ static bool isSupported() => html.window.navigator.connection != null;
+
+ /// The constructor of the plugin.
+ NetworkInformationApiConnectivityPlugin()
+ : this.withConnection(html.window.navigator.connection!);
+
+ /// Creates the plugin, with an override of the NetworkInformation object.
+ @visibleForTesting
+ NetworkInformationApiConnectivityPlugin.withConnection(
+ html.NetworkInformation connection)
+ : _networkInformation = connection;
+
+ /// Checks the connection status of the device.
+ @override
+ Future checkConnectivity() async {
+ return networkInformationToConnectivityResult(_networkInformation);
+ }
+
+ StreamController? _connectivityResultStreamController;
+ late Stream _connectivityResultStream;
+
+ /// Returns a Stream of ConnectivityResults changes.
+ @override
+ Stream get onConnectivityChanged {
+ // use fallback implementation if [_connectionSupported] is not availible
+ if (_connectionSupported == null) {
+ return _webPseudoStream();
+ }
+ if (_connectivityResultStreamController == null) {
+ _connectivityResultStreamController =
+ StreamController();
+ setProperty(_networkInformation, 'onchange', allowInterop((_) {
+ _connectivityResultStreamController!
+ .add(networkInformationToConnectivityResult(_networkInformation));
+ }));
+ // ignore: todo
+ // TODO: Implement the above with _networkInformation.onChange:
+ // _networkInformation.onChange.listen((_) {
+ // _connectivityResult
+ // .add(networkInformationToConnectivityResult(_networkInformation));
+ // });
+ // Once we can detect when to *cancel* a subscription to the _networkInformation
+ // onChange Stream upon hot restart.
+ // https://github.com/dart-lang/sdk/issues/42679
+ _connectivityResultStream =
+ _connectivityResultStreamController!.stream.asBroadcastStream();
+ }
+ return _connectivityResultStream;
+ }
+
+ /// stores the last fallback network state
+ ConnectivityResult? _lastFallbackState;
+
+ /// periodically checks the current network state
+ Stream _webPseudoStream() {
+ final StreamController webStream =
+ StreamController.broadcast();
+ Timer.periodic(
+ const Duration(milliseconds: 250),
+ (timer) async {
+ final result = await checkConnectivity();
+ if (result != _lastFallbackState) {
+ webStream.add(result);
+ }
+ },
+ );
+ return webStream.stream;
+ }
+}
+
+/// accesses the JS-native `navigator.connection`
+///
+/// ensures `navigator.connection.onchange` is availible
+@JS("navigator.connection")
+external get _connectionSupported;
diff --git a/lib/third_party/connectivity_plus/connectivity_plus/lib/src/web/utils/connectivity_result.dart b/lib/third_party/connectivity_plus/connectivity_plus/lib/src/web/utils/connectivity_result.dart
new file mode 100644
index 00000000..004cba81
--- /dev/null
+++ b/lib/third_party/connectivity_plus/connectivity_plus/lib/src/web/utils/connectivity_result.dart
@@ -0,0 +1,53 @@
+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(
+ html.NetworkInformation info,
+) {
+ if (info.downlink == 0 && info.rtt == 0) {
+ return ConnectivityResult.none;
+ }
+ if (info.type != null) {
+ return _typeToConnectivityResult(info.type!);
+ }
+ if (info.effectiveType != null) {
+ return _effectiveTypeToConnectivityResult(info.effectiveType!);
+ }
+ return ConnectivityResult.none;
+}
+
+ConnectivityResult _effectiveTypeToConnectivityResult(String effectiveType) {
+ // Possible values:
+ /*'2g'|'3g'|'4g'|'slow-2g'*/
+ switch (effectiveType) {
+ case 'slow-2g':
+ case '2g':
+ case '3g':
+ case '4g':
+ return ConnectivityResult.mobile;
+ default:
+ return ConnectivityResult.wifi;
+ }
+}
+
+ConnectivityResult _typeToConnectivityResult(String type) {
+ // Possible values:
+ /*'bluetooth'|'cellular'|'ethernet'|'mixed'|'none'|'other'|'unknown'|'wifi'|'wimax'*/
+ switch (type) {
+ case 'none':
+ return ConnectivityResult.none;
+ case 'bluetooth':
+ return ConnectivityResult.bluetooth;
+ case 'cellular':
+ case 'mixed':
+ case 'other':
+ case 'unknown':
+ return ConnectivityResult.mobile;
+ case 'ethernet':
+ return ConnectivityResult.ethernet;
+ default:
+ return ConnectivityResult.wifi;
+ }
+}
diff --git a/lib/third_party/connectivity_plus/connectivity_plus/macos/Classes/ConnectivityPlugin.swift b/lib/third_party/connectivity_plus/connectivity_plus/macos/Classes/ConnectivityPlugin.swift
new file mode 100644
index 00000000..8be217e9
--- /dev/null
+++ b/lib/third_party/connectivity_plus/connectivity_plus/macos/Classes/ConnectivityPlugin.swift
@@ -0,0 +1,90 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source is governed by a BSD-style license that can
+// be found in the LICENSE file.
+
+import Cocoa
+import FlutterMacOS
+
+public class ConnectivityPlugin: NSObject, FlutterPlugin, FlutterStreamHandler {
+ private let connectivityProvider: ConnectivityProvider
+ private var eventSink: FlutterEventSink?
+
+ init(connectivityProvider: ConnectivityProvider) {
+ self.connectivityProvider = connectivityProvider
+ super.init()
+ self.connectivityProvider.connectivityUpdateHandler = connectivityUpdateHandler
+ }
+
+ public static func register(with registrar: FlutterPluginRegistrar) {
+ let channel = FlutterMethodChannel(
+ name: "dev.fluttercommunity.plus/connectivity",
+ binaryMessenger: registrar.messenger)
+
+ let streamChannel = FlutterEventChannel(
+ name: "dev.fluttercommunity.plus/connectivity_status",
+ binaryMessenger: registrar.messenger)
+
+ let connectivityProvider: ConnectivityProvider
+ if #available(macOS 10.14, *) {
+ connectivityProvider = PathMonitorConnectivityProvider()
+ } else {
+ connectivityProvider = ReachabilityConnectivityProvider()
+ }
+
+ let instance = ConnectivityPlugin(connectivityProvider: connectivityProvider)
+ streamChannel.setStreamHandler(instance)
+
+ registrar.addMethodCallDelegate(instance, channel: channel)
+ }
+
+ public func detachFromEngine(for registrar: FlutterPluginRegistrar) {
+ eventSink = nil
+ connectivityProvider.stop()
+ }
+
+ public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
+ switch call.method {
+ case "check":
+ result(statusFrom(connectivityType: connectivityProvider.currentConnectivityType))
+ default:
+ result(FlutterMethodNotImplemented)
+ }
+ }
+
+ private func statusFrom(connectivityType: ConnectivityType) -> String {
+ switch connectivityType {
+ case .wifi:
+ return "wifi"
+ case .cellular:
+ return "mobile"
+ case .wiredEthernet:
+ return "ethernet"
+ case .other:
+ return "other"
+ case .none:
+ return "none"
+ }
+ }
+
+ public func onListen(
+ withArguments _: Any?,
+ eventSink events: @escaping FlutterEventSink
+ ) -> FlutterError? {
+ eventSink = events
+ connectivityProvider.start()
+ connectivityUpdateHandler(connectivityType: connectivityProvider.currentConnectivityType)
+ return nil
+ }
+
+ private func connectivityUpdateHandler(connectivityType: ConnectivityType) {
+ DispatchQueue.main.async {
+ self.eventSink?(self.statusFrom(connectivityType: connectivityType))
+ }
+ }
+
+ public func onCancel(withArguments _: Any?) -> FlutterError? {
+ connectivityProvider.stop()
+ eventSink = nil
+ return nil
+ }
+}
diff --git a/lib/third_party/connectivity_plus/connectivity_plus/macos/Classes/ConnectivityProvider.swift b/lib/third_party/connectivity_plus/connectivity_plus/macos/Classes/ConnectivityProvider.swift
new file mode 100644
index 00000000..38156822
--- /dev/null
+++ b/lib/third_party/connectivity_plus/connectivity_plus/macos/Classes/ConnectivityProvider.swift
@@ -0,0 +1,21 @@
+import Foundation
+
+public enum ConnectivityType {
+ case none
+ case wiredEthernet
+ case wifi
+ case cellular
+ case other
+}
+
+public protocol ConnectivityProvider: NSObjectProtocol {
+ typealias ConnectivityUpdateHandler = (ConnectivityType) -> Void
+
+ var currentConnectivityType: ConnectivityType { get }
+
+ var connectivityUpdateHandler: ConnectivityUpdateHandler? { get set }
+
+ func start()
+
+ func stop()
+}
diff --git a/lib/third_party/connectivity_plus/connectivity_plus/macos/Classes/PathMonitorConnectivityProvider.swift b/lib/third_party/connectivity_plus/connectivity_plus/macos/Classes/PathMonitorConnectivityProvider.swift
new file mode 100644
index 00000000..5b86a0d8
--- /dev/null
+++ b/lib/third_party/connectivity_plus/connectivity_plus/macos/Classes/PathMonitorConnectivityProvider.swift
@@ -0,0 +1,57 @@
+import Foundation
+import Network
+
+@available(macOS 10.14, *)
+public class PathMonitorConnectivityProvider: NSObject, ConnectivityProvider {
+
+ private let queue = DispatchQueue.global(qos: .background)
+
+ private var _pathMonitor: NWPathMonitor?
+
+ public var currentConnectivityType: ConnectivityType {
+ let path = ensurePathMonitor().currentPath
+ // .satisfied means that the network is available
+ 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
+ }
+ }
+ return .none
+ }
+
+ public var connectivityUpdateHandler: ConnectivityUpdateHandler?
+
+ override init() {
+ super.init()
+ _ = ensurePathMonitor()
+ }
+
+ public func start() {
+ _ = ensurePathMonitor()
+ }
+
+ public func stop() {
+ _pathMonitor?.cancel()
+ _pathMonitor = nil
+ }
+
+ private func ensurePathMonitor() -> NWPathMonitor {
+ if (_pathMonitor == nil) {
+ let pathMonitor = NWPathMonitor()
+ pathMonitor.start(queue: queue)
+ pathMonitor.pathUpdateHandler = pathUpdateHandler
+ _pathMonitor = pathMonitor
+ }
+ return _pathMonitor!
+ }
+
+ private func pathUpdateHandler(path: NWPath) {
+ connectivityUpdateHandler?(currentConnectivityType)
+ }
+}
diff --git a/lib/third_party/connectivity_plus/connectivity_plus/macos/Classes/ReachabilityConnectivityProvider.swift b/lib/third_party/connectivity_plus/connectivity_plus/macos/Classes/ReachabilityConnectivityProvider.swift
new file mode 100644
index 00000000..4398a12e
--- /dev/null
+++ b/lib/third_party/connectivity_plus/connectivity_plus/macos/Classes/ReachabilityConnectivityProvider.swift
@@ -0,0 +1,59 @@
+import Foundation
+import Reachability
+
+public class ReachabilityConnectivityProvider: NSObject, ConnectivityProvider {
+ private var _reachability: Reachability?
+
+ public var currentConnectivityType: ConnectivityType {
+ let reachability = ensureReachability()
+ switch reachability.connection {
+ case .wifi:
+ return .wifi
+ case .cellular:
+ return .cellular
+ default:
+ return .none
+ }
+ }
+
+ public var connectivityUpdateHandler: ConnectivityUpdateHandler?
+
+ override init() {
+ super.init()
+ _ = ensureReachability()
+ }
+
+ public func start() {
+ let reachability = ensureReachability()
+
+ NotificationCenter.default.addObserver(
+ self,
+ selector: #selector(reachabilityChanged),
+ name: .reachabilityChanged,
+ object: reachability)
+
+ try? reachability.startNotifier()
+ }
+
+ public func stop() {
+ NotificationCenter.default.removeObserver(
+ self,
+ name: .reachabilityChanged,
+ object: _reachability)
+
+ _reachability?.stopNotifier()
+ _reachability = nil
+ }
+
+ private func ensureReachability() -> Reachability {
+ if (_reachability == nil) {
+ let reachability = try? Reachability()
+ _reachability = reachability
+ }
+ return _reachability!
+ }
+
+ @objc private func reachabilityChanged(notification: NSNotification) {
+ connectivityUpdateHandler?(currentConnectivityType)
+ }
+}
diff --git a/lib/third_party/connectivity_plus/connectivity_plus/macos/connectivity_plus.podspec b/lib/third_party/connectivity_plus/connectivity_plus/macos/connectivity_plus.podspec
new file mode 100644
index 00000000..4d155d2c
--- /dev/null
+++ b/lib/third_party/connectivity_plus/connectivity_plus/macos/connectivity_plus.podspec
@@ -0,0 +1,22 @@
+#
+# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html
+#
+Pod::Spec.new do |s|
+ s.name = 'connectivity_plus'
+ s.version = '0.0.1'
+ s.summary = 'Flutter plugin for checking connectivity'
+ s.description = <<-DESC
+ Desktop implementation of the connectivity plugin
+ DESC
+ s.homepage = 'https://plus.fluttercommunity.dev/'
+ s.license = { :type => 'BSD', :file => '../LICENSE' }
+ s.author = { 'Flutter Community Team' => 'authors@fluttercommunity.dev' }
+ s.source = { :http => 'https://github.com/fluttercommunity/plus_plugins' }
+ s.documentation_url = 'https://pub.dev/packages/connectivity_plus'
+ s.source_files = 'Classes/**/*'
+ s.dependency 'FlutterMacOS'
+ s.dependency 'ReachabilitySwift'
+
+ s.platform = :osx
+ s.osx.deployment_target = '10.11'
+end
diff --git a/lib/third_party/connectivity_plus/connectivity_plus/pubspec.yaml b/lib/third_party/connectivity_plus/connectivity_plus/pubspec.yaml
new file mode 100644
index 00000000..5d9218ab
--- /dev/null
+++ b/lib/third_party/connectivity_plus/connectivity_plus/pubspec.yaml
@@ -0,0 +1,48 @@
+name: connectivity_plus
+description: Flutter plugin for discovering the state of the network (WiFi & mobile/cellular) connectivity on Android and iOS.
+version: 3.0.6
+homepage: https://plus.fluttercommunity.dev/
+repository: https://github.com/fluttercommunity/plus_plugins/tree/main/packages/
+issue_tracker: https://github.com/fluttercommunity/plus_plugins/labels/connectivity_plus
+
+environment:
+ sdk: ">=2.12.0 <3.0.0"
+ flutter: ">=2.11.0"
+
+flutter:
+ plugin:
+ platforms:
+ android:
+ package: dev.fluttercommunity.plus.connectivity
+ pluginClass: ConnectivityPlugin
+ ios:
+ pluginClass: ConnectivityPlusPlugin
+ linux:
+ dartPluginClass: ConnectivityPlusLinuxPlugin
+ macos:
+ pluginClass: ConnectivityPlugin
+ web:
+ pluginClass: ConnectivityPlusWebPlugin
+ fileName: src/connectivity_plus_web.dart
+ windows:
+ pluginClass: ConnectivityPlusWindowsPlugin
+
+dependencies:
+ flutter:
+ sdk: flutter
+ flutter_web_plugins:
+ sdk: flutter
+ connectivity_plus_platform_interface:
+ path: ./../connectivity_plus_platform_interface
+ js: ^0.6.3
+ meta: ^1.8.0
+
+dev_dependencies:
+ flutter_test:
+ sdk: flutter
+ build_runner: ^2.1.11
+ dbus: ^0.7.5
+ flutter_lints: ^2.0.1
+ mockito: ^5.2.0
+ plugin_platform_interface: ^2.1.2
+ test: ^1.21.1
diff --git a/lib/third_party/connectivity_plus/connectivity_plus/windows/.gitignore b/lib/third_party/connectivity_plus/connectivity_plus/windows/.gitignore
new file mode 100644
index 00000000..b3eb2be1
--- /dev/null
+++ b/lib/third_party/connectivity_plus/connectivity_plus/windows/.gitignore
@@ -0,0 +1,17 @@
+flutter/
+
+# Visual Studio user-specific files.
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# Visual Studio build-related files.
+x64/
+x86/
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!*.[Cc]ache/
diff --git a/lib/third_party/connectivity_plus/connectivity_plus/windows/CMakeLists.txt b/lib/third_party/connectivity_plus/connectivity_plus/windows/CMakeLists.txt
new file mode 100644
index 00000000..ac315ccd
--- /dev/null
+++ b/lib/third_party/connectivity_plus/connectivity_plus/windows/CMakeLists.txt
@@ -0,0 +1,29 @@
+cmake_minimum_required(VERSION 3.15)
+set(PROJECT_NAME "connectivity_plus")
+project(${PROJECT_NAME} LANGUAGES CXX)
+
+# This value is used when generating builds using this plugin, so it must
+# not be changed
+set(PLUGIN_NAME "connectivity_plus_plugin")
+
+add_library(${PLUGIN_NAME} SHARED
+ "connectivity_plus_plugin.cpp"
+ "network_manager.cpp"
+)
+apply_standard_settings(${PLUGIN_NAME})
+set_target_properties(${PLUGIN_NAME} PROPERTIES
+ CXX_VISIBILITY_PRESET hidden)
+target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL)
+target_include_directories(${PLUGIN_NAME} INTERFACE
+ "${CMAKE_CURRENT_SOURCE_DIR}/include")
+target_link_libraries(${PLUGIN_NAME} PRIVATE
+ flutter
+ flutter_wrapper_plugin
+ iphlpapi
+)
+
+# List of absolute paths to libraries that should be bundled with the plugin
+set(connectivity_plus_bundled_libraries
+ ""
+ PARENT_SCOPE
+)
diff --git a/lib/third_party/connectivity_plus/connectivity_plus/windows/connectivity_plus_plugin.cpp b/lib/third_party/connectivity_plus/connectivity_plus/windows/connectivity_plus_plugin.cpp
new file mode 100644
index 00000000..40742035
--- /dev/null
+++ b/lib/third_party/connectivity_plus/connectivity_plus/windows/connectivity_plus_plugin.cpp
@@ -0,0 +1,171 @@
+// clang-format off
+#include "include/connectivity_plus/network_manager.h"
+// clang-format on
+#include "include/connectivity_plus/connectivity_plus_windows_plugin.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+namespace {
+
+typedef flutter::EventChannel FlEventChannel;
+typedef flutter::EventSink FlEventSink;
+typedef flutter::MethodCall FlMethodCall;
+typedef flutter::MethodResult FlMethodResult;
+typedef flutter::MethodChannel FlMethodChannel;
+typedef flutter::StreamHandler FlStreamHandler;
+typedef flutter::StreamHandlerError
+ FlStreamHandlerError;
+
+class ConnectivityPlusWindowsPlugin : public flutter::Plugin {
+public:
+ ConnectivityPlusWindowsPlugin();
+ virtual ~ConnectivityPlusWindowsPlugin();
+
+ std::shared_ptr GetManager() const;
+
+ static void RegisterWithRegistrar(flutter::PluginRegistrarWindows *registrar);
+
+private:
+ void HandleMethodCall(const FlMethodCall &method_call,
+ std::unique_ptr result);
+
+ std::shared_ptr manager;
+};
+
+class ConnectivityStreamHandler : public FlStreamHandler {
+public:
+ ConnectivityStreamHandler(std::shared_ptr manager);
+ virtual ~ConnectivityStreamHandler();
+
+protected:
+ void AddConnectivityEvent();
+
+ std::unique_ptr
+ OnListenInternal(const flutter::EncodableValue *arguments,
+ std::unique_ptr &&sink) override;
+
+ std::unique_ptr
+ OnCancelInternal(const flutter::EncodableValue *arguments) override;
+
+private:
+ std::shared_ptr manager;
+ std::unique_ptr sink;
+};
+
+ConnectivityPlusWindowsPlugin::ConnectivityPlusWindowsPlugin() {
+ manager = std::make_shared();
+ manager->Init();
+}
+
+ConnectivityPlusWindowsPlugin::~ConnectivityPlusWindowsPlugin() {
+ manager->Cleanup();
+}
+
+std::shared_ptr
+ConnectivityPlusWindowsPlugin::GetManager() const {
+ return manager;
+}
+
+void ConnectivityPlusWindowsPlugin::RegisterWithRegistrar(
+ flutter::PluginRegistrarWindows *registrar) {
+ auto plugin = std::make_unique();
+
+ auto methodChannel =
+ std::make_unique>(
+ registrar->messenger(), "dev.fluttercommunity.plus/connectivity",
+ &flutter::StandardMethodCodec::GetInstance());
+
+ methodChannel->SetMethodCallHandler(
+ [plugin_pointer = plugin.get()](const auto &call, auto result) {
+ plugin_pointer->HandleMethodCall(call, std::move(result));
+ });
+
+ auto eventChannel = std::make_unique(
+ registrar->messenger(), "dev.fluttercommunity.plus/connectivity_status",
+ &flutter::StandardMethodCodec::GetInstance());
+
+ eventChannel->SetStreamHandler(
+ std::make_unique(plugin->GetManager()));
+
+ registrar->AddPlugin(std::move(plugin));
+}
+
+static std::string ConnectivityToString(ConnectivityType connectivityType) {
+ switch (connectivityType) {
+ case ConnectivityType::WiFi:
+ return "wifi";
+ case ConnectivityType::Ethernet:
+ return "ethernet";
+ case ConnectivityType::None:
+ default:
+ return "none";
+ }
+}
+
+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());
+ result->Success(flutter::EncodableValue(connectivity));
+ } else {
+ result->NotImplemented();
+ }
+}
+
+ConnectivityStreamHandler::ConnectivityStreamHandler(
+ std::shared_ptr manager)
+ : manager(manager) {}
+
+ConnectivityStreamHandler::~ConnectivityStreamHandler() {}
+
+void ConnectivityStreamHandler::AddConnectivityEvent() {
+ std::string connectivity =
+ ConnectivityToString(manager->GetConnectivityType());
+ sink->Success(flutter::EncodableValue(connectivity));
+}
+
+std::unique_ptr
+ConnectivityStreamHandler::OnListenInternal(
+ const flutter::EncodableValue *arguments,
+ std::unique_ptr &&events) {
+ sink = std::move(events);
+
+ auto callback =
+ std::bind(&ConnectivityStreamHandler::AddConnectivityEvent, this);
+
+ if (!manager->StartListen(callback)) {
+ return std::make_unique(
+ std::to_string(manager->GetError()), "NetworkManager::StartListen",
+ nullptr);
+ }
+
+ AddConnectivityEvent();
+ return nullptr;
+}
+
+std::unique_ptr
+ConnectivityStreamHandler::OnCancelInternal(
+ const flutter::EncodableValue *arguments) {
+ manager->StopListen();
+ sink.reset();
+ return nullptr;
+}
+
+} // namespace
+
+void ConnectivityPlusWindowsPluginRegisterWithRegistrar(
+ FlutterDesktopPluginRegistrarRef registrar) {
+ ConnectivityPlusWindowsPlugin::RegisterWithRegistrar(
+ flutter::PluginRegistrarManager::GetInstance()
+ ->GetRegistrar(registrar));
+}
diff --git a/lib/third_party/connectivity_plus/connectivity_plus/windows/include/connectivity_plus/connectivity_plus_windows_plugin.h b/lib/third_party/connectivity_plus/connectivity_plus/windows/include/connectivity_plus/connectivity_plus_windows_plugin.h
new file mode 100644
index 00000000..17af4474
--- /dev/null
+++ b/lib/third_party/connectivity_plus/connectivity_plus/windows/include/connectivity_plus/connectivity_plus_windows_plugin.h
@@ -0,0 +1,23 @@
+#ifndef FLUTTER_PLUGIN_CONNECTIVITY_WINDOWS_PLUS_PLUGIN_H_
+#define FLUTTER_PLUGIN_CONNECTIVITY_WINDOWS_PLUS_PLUGIN_H_
+
+#include
+
+#ifdef FLUTTER_PLUGIN_IMPL
+#define FLUTTER_PLUGIN_EXPORT __declspec(dllexport)
+#else
+#define FLUTTER_PLUGIN_EXPORT __declspec(dllimport)
+#endif
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+FLUTTER_PLUGIN_EXPORT void ConnectivityPlusWindowsPluginRegisterWithRegistrar(
+ FlutterDesktopPluginRegistrarRef registrar);
+
+#if defined(__cplusplus)
+} // extern "C"
+#endif
+
+#endif // FLUTTER_PLUGIN_CONNECTIVITY_PLUS_WINDOWS_PLUGIN_H_
diff --git a/lib/third_party/connectivity_plus/connectivity_plus/windows/include/connectivity_plus/network_manager.h b/lib/third_party/connectivity_plus/connectivity_plus/windows/include/connectivity_plus/network_manager.h
new file mode 100644
index 00000000..51a7e9b3
--- /dev/null
+++ b/lib/third_party/connectivity_plus/connectivity_plus/windows/include/connectivity_plus/network_manager.h
@@ -0,0 +1,49 @@
+#ifndef NETWORK_MANAGER_H
+#define NETWORK_MANAGER_H
+
+// clang-format off
+#include
+// clang-format on
+#include
+
+#include
+#include
+
+enum class ConnectivityType { None, Ethernet, WiFi };
+
+class NetworkListener;
+struct IConnectionPoint;
+struct IConnectionPointContainer;
+struct INetworkListManager;
+struct IUnknown;
+
+typedef std::function NetworkCallback;
+
+class NetworkManager {
+public:
+ NetworkManager();
+ ~NetworkManager();
+
+ bool Init();
+ void Cleanup();
+
+ ConnectivityType GetConnectivityType() const;
+
+ bool StartListen(NetworkCallback pCallback);
+ void StopListen();
+
+ bool HasError() const;
+ int GetError() const;
+
+private:
+ std::vector GetConnectedAdapterIds() const;
+
+ DWORD dwCookie = 0;
+ IUnknown *pUnknown = NULL;
+ INetworkListManager *pNetworkListManager = NULL;
+ IConnectionPointContainer *pCPContainer = NULL;
+ IConnectionPoint *pConnectPoint = NULL;
+ NetworkListener *pListener = NULL;
+};
+
+#endif // NETWORK_MANAGER_H
diff --git a/lib/third_party/connectivity_plus/connectivity_plus/windows/network_manager.cpp b/lib/third_party/connectivity_plus/connectivity_plus/windows/network_manager.cpp
new file mode 100644
index 00000000..e009c7ed
--- /dev/null
+++ b/lib/third_party/connectivity_plus/connectivity_plus/windows/network_manager.cpp
@@ -0,0 +1,243 @@
+// based on
+// https://github.com/PurpleI2P/i2pd/blob/master/Win32/Win32NetState.cpp
+
+/*
+ * Copyright (c) 2013-2020, The PurpleI2P Project
+ *
+ * This file is part of Purple i2pd project and licensed under BSD3
+ *
+ * See full license text in LICENSE file at top of project tree
+ */
+
+#include "include/connectivity_plus/network_manager.h"
+
+#include
+#include
+#include
+
+#include
+#include
+
+class NetworkListener final : public INetworkEvents {
+public:
+ NetworkListener(NetworkCallback pCb) : pCallback(pCb) {}
+
+ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) {
+ AddRef();
+
+ HRESULT hr = S_OK;
+ if (IsEqualIID(riid, IID_IUnknown)) {
+ *ppvObject = (IUnknown *)this;
+ } else if (IsEqualIID(riid, IID_INetworkEvents)) {
+ *ppvObject = (INetworkEvents *)this;
+ } else {
+ hr = E_NOINTERFACE;
+ }
+ return hr;
+ }
+
+ ULONG STDMETHODCALLTYPE AddRef() { return InterlockedIncrement(&lRef); }
+
+ ULONG STDMETHODCALLTYPE Release() {
+ LONG lAddend = InterlockedDecrement(&lRef);
+ if (lRef == 0) {
+ delete this;
+ }
+ return lAddend;
+ }
+
+ HRESULT STDMETHODCALLTYPE NetworkAdded(GUID networkId) { return S_OK; }
+
+ HRESULT STDMETHODCALLTYPE
+ NetworkConnectivityChanged(GUID networkId, NLM_CONNECTIVITY newConnectivity) {
+ Callback();
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE NetworkDeleted(GUID networkId) { return S_OK; }
+
+ HRESULT STDMETHODCALLTYPE
+ NetworkPropertyChanged(GUID networkId, NLM_NETWORK_PROPERTY_CHANGE flags) {
+ if (flags & NLM_NETWORK_PROPERTY_CHANGE_CONNECTION) {
+ Callback();
+ }
+ return S_OK;
+ }
+
+ void Callback() {
+ assert(pCallback);
+ pCallback();
+ }
+
+private:
+ volatile LONG lRef = 1;
+ NetworkCallback pCallback = nullptr;
+};
+
+NetworkManager::NetworkManager() {}
+
+NetworkManager::~NetworkManager() {
+ StopListen();
+ Cleanup();
+}
+
+bool NetworkManager::Init() {
+ CoInitialize(NULL);
+
+ HRESULT hr = CoCreateInstance(CLSID_NetworkListManager, NULL, CLSCTX_ALL,
+ IID_IUnknown, (void **)&pUnknown);
+ if (SUCCEEDED(hr)) {
+ hr = pUnknown->QueryInterface(IID_INetworkListManager,
+ (void **)&pNetworkListManager);
+ }
+ return SUCCEEDED(hr);
+}
+
+void NetworkManager::Cleanup() {
+ if (pNetworkListManager) {
+ pNetworkListManager->Release();
+ pNetworkListManager = NULL;
+ }
+
+ if (pUnknown) {
+ pUnknown->Release();
+ pUnknown = NULL;
+ }
+
+ CoUninitialize();
+}
+
+std::vector NetworkManager::GetConnectedAdapterIds() const {
+ std::vector adapterIds;
+
+ IEnumNetworkConnections *connections = NULL;
+ HRESULT hr = pNetworkListManager->GetNetworkConnections(&connections);
+ if (hr == S_OK) {
+ while (true) {
+ INetworkConnection *connection = NULL;
+ hr = connections->Next(1, &connection, NULL);
+ if (hr != S_OK) {
+ break;
+ }
+
+ VARIANT_BOOL isConnected = VARIANT_FALSE;
+ hr = connection->get_IsConnectedToInternet(&isConnected);
+ if (hr == S_OK && isConnected == VARIANT_TRUE) {
+ GUID guid;
+ hr = connection->GetAdapterId(&guid);
+ if (hr == S_OK) {
+ adapterIds.push_back(std::move(guid));
+ }
+ }
+ connection->Release();
+ }
+ connections->Release();
+ }
+
+ return adapterIds;
+}
+
+ConnectivityType NetworkManager::GetConnectivityType() const {
+ ULONG bufferSize = 15 * 1024;
+ ULONG flags = GAA_FLAG_SKIP_UNICAST | GAA_FLAG_SKIP_ANYCAST |
+ GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER |
+ GAA_FLAG_SKIP_FRIENDLY_NAME;
+ std::vector buffer(bufferSize);
+ PIP_ADAPTER_ADDRESSES addresses =
+ reinterpret_cast(&buffer.front());
+ DWORD rc = GetAdaptersAddresses(AF_UNSPEC, flags, 0, addresses, &bufferSize);
+ if (rc == ERROR_BUFFER_OVERFLOW) {
+ buffer.resize(bufferSize);
+ addresses = reinterpret_cast(&buffer.front());
+ rc = GetAdaptersAddresses(AF_UNSPEC, flags, 0, addresses, &bufferSize);
+ }
+
+ if (rc != NO_ERROR) {
+ return ConnectivityType::None;
+ }
+
+ std::vector adapterIds = GetConnectedAdapterIds();
+ if (adapterIds.empty()) {
+ return ConnectivityType::None;
+ }
+
+ std::set connectivities;
+ for (; addresses != NULL; addresses = addresses->Next) {
+ NET_LUID luid;
+ rc = ConvertInterfaceIndexToLuid(addresses->IfIndex, &luid);
+ if (rc != NO_ERROR) {
+ continue;
+ }
+
+ GUID guid;
+ rc = ConvertInterfaceLuidToGuid(&luid, &guid);
+ if (rc != NO_ERROR) {
+ continue;
+ }
+
+ if (std::find(adapterIds.begin(), adapterIds.end(), guid) !=
+ adapterIds.end()) {
+ switch (addresses->IfType) {
+ case IF_TYPE_ETHERNET_CSMACD:
+ connectivities.insert(ConnectivityType::Ethernet);
+ break;
+ default:
+ connectivities.insert(ConnectivityType::WiFi);
+ break;
+ }
+ }
+ }
+
+ if (connectivities.find(ConnectivityType::WiFi) != connectivities.end()) {
+ return ConnectivityType::WiFi;
+ }
+
+ if (connectivities.find(ConnectivityType::Ethernet) != connectivities.end()) {
+ return ConnectivityType::Ethernet;
+ }
+
+ return ConnectivityType::None;
+}
+
+bool NetworkManager::StartListen(NetworkCallback pCallback) {
+ if (!pCallback || pListener) {
+ return false;
+ }
+
+ HRESULT hr = pNetworkListManager->QueryInterface(
+ IID_IConnectionPointContainer, (void **)&pCPContainer);
+ if (SUCCEEDED(hr)) {
+ hr = pCPContainer->FindConnectionPoint(IID_INetworkEvents, &pConnectPoint);
+ if (SUCCEEDED(hr)) {
+ pListener = new NetworkListener(pCallback);
+ hr = pConnectPoint->Advise((IUnknown *)pListener, &dwCookie);
+ if (SUCCEEDED(hr)) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void NetworkManager::StopListen() {
+ if (pConnectPoint) {
+ pConnectPoint->Unadvise(dwCookie);
+ pConnectPoint->Release();
+ pConnectPoint = NULL;
+ dwCookie = 0;
+ }
+
+ if (pCPContainer) {
+ pCPContainer->Release();
+ pCPContainer = NULL;
+ }
+
+ if (pListener) {
+ pListener->Release();
+ pListener = NULL;
+ }
+}
+
+bool NetworkManager::HasError() const { return GetLastError() != 0; }
+
+int NetworkManager::GetError() const { return GetLastError(); }
diff --git a/lib/third_party/connectivity_plus/connectivity_plus_platform_interface/.gitattributes b/lib/third_party/connectivity_plus/connectivity_plus_platform_interface/.gitattributes
new file mode 100644
index 00000000..31fda4bf
--- /dev/null
+++ b/lib/third_party/connectivity_plus/connectivity_plus_platform_interface/.gitattributes
@@ -0,0 +1,25 @@
+# Auto detect text files and perform LF normalization
+* text=auto
+
+# Always perform LF normalization on these files
+*.dart text
+*.gradle text
+*.html text
+*.java text
+*.json text
+*.md text
+*.py text
+*.sh text
+*.txt text
+*.xml text
+*.yaml text
+
+# Make sure that these Windows files always have CRLF line endings in checkout
+*.bat text eol=crlf
+*.ps1 text eol=crlf
+
+# Never perform LF normalization on these files
+*.ico binary
+*.jar binary
+*.png binary
+*.zip binary
diff --git a/lib/third_party/connectivity_plus/connectivity_plus_platform_interface/.gitignore b/lib/third_party/connectivity_plus/connectivity_plus_platform_interface/.gitignore
new file mode 100644
index 00000000..bfb8a70b
--- /dev/null
+++ b/lib/third_party/connectivity_plus/connectivity_plus_platform_interface/.gitignore
@@ -0,0 +1,46 @@
+.DS_Store
+.atom/
+.idea/
+.vscode/
+
+.packages
+.pub/
+.dart_tool/
+pubspec.lock
+flutter_export_environment.sh
+
+examples/all_plugins/pubspec.yaml
+
+Podfile
+Podfile.lock
+Pods/
+.symlinks/
+**/Flutter/App.framework/
+**/Flutter/ephemeral/
+**/Flutter/Flutter.framework/
+**/Flutter/Generated.xcconfig
+**/Flutter/flutter_assets/
+
+ServiceDefinitions.json
+xcuserdata/
+**/DerivedData/
+
+local.properties
+keystore.properties
+.gradle/
+gradlew
+gradlew.bat
+gradle-wrapper.jar
+.flutter-plugins-dependencies
+*.iml
+
+GeneratedPluginRegistrant.h
+GeneratedPluginRegistrant.m
+GeneratedPluginRegistrant.java
+GeneratedPluginRegistrant.swift
+build/
+.flutter-plugins
+
+.project
+.classpath
+.settings
diff --git a/lib/third_party/connectivity_plus/connectivity_plus_platform_interface/CHANGELOG.md b/lib/third_party/connectivity_plus/connectivity_plus_platform_interface/CHANGELOG.md
new file mode 100644
index 00000000..c4ae2750
--- /dev/null
+++ b/lib/third_party/connectivity_plus/connectivity_plus_platform_interface/CHANGELOG.md
@@ -0,0 +1,94 @@
+## 1.2.4
+
+ - **FIX**: Do not return ConnectivityResult.none on iOS and MacOS with VPN (#1335).
+
+## 1.2.3
+
+ - **FIX**: Increase min Flutter version to fix dartPluginClass registration (#1275).
+
+## 1.2.2
+
+- Add missing VPN enum
+
+## 1.2.1
+
+- Update flutter_lints to 2.0.1
+- Update plugin_platform_interface to 2.1.2
+- Fix analyzer issues
+
+## 1.2.0
+
+- Add bluetooth as connectivity result
+
+## 1.1.1
+
+- Dependencies update
+
+## 1.1.0
+
+- Add ethernet as connectivity result
+
+## 1.0.2
+
+- Update connectivity plus
+
+## 1.0.1
+
+- Improve documentation
+
+## 1.0.0
+
+- Migrated to null safety
+
+## 0.4.1
+
+- Address pub score
+
+## 0.4.0
+
+- Removed members that were moved to network_info_plus
+
+## 0.3.0
+
+- Renamed method channel
+
+## 0.2.0
+
+- Transfer to plus-plugins monorepo
+
+## 0.1.7
+
+- Transfer package to Flutter Community under new name `connectivity_plus_platform_interface`.
+
+## 0.1.6
+
+- Update lower bound of dart dependency to 2.1.0.
+
+## 0.1.5
+
+- Remove dart:io Platform checks from the MethodChannel implementation. This is
+ tripping the analysis of other versions of the plugin.
+
+## 0.1.4
+
+- Bump the minimum Flutter version to 1.12.13+hotfix.5.
+
+## 0.1.3
+
+- Make the pedantic dev_dependency explicit.
+
+## 0.1.2
+
+- Bring ConnectivityResult and LocationAuthorizationStatus enums from the core package.
+- Use the above Enums as return values for ConnectivityPlatformInterface methods.
+- Modify the MethodChannel implementation so it returns the right types.
+- Bring all utility methods, asserts and other logic that is only needed on the MethodChannel implementation from the core package.
+- Bring MethodChannel unit tests from core package.
+
+## 0.1.1
+
+- Fix README.md link.
+
+## 0.1.0
+
+- Initial release.
diff --git a/lib/third_party/connectivity_plus/connectivity_plus_platform_interface/LICENSE b/lib/third_party/connectivity_plus/connectivity_plus_platform_interface/LICENSE
new file mode 100644
index 00000000..687fb496
--- /dev/null
+++ b/lib/third_party/connectivity_plus/connectivity_plus_platform_interface/LICENSE
@@ -0,0 +1,27 @@
+Copyright 2020 The Chromium Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/lib/third_party/connectivity_plus/connectivity_plus_platform_interface/README.md b/lib/third_party/connectivity_plus/connectivity_plus_platform_interface/README.md
new file mode 100644
index 00000000..dfdb11d1
--- /dev/null
+++ b/lib/third_party/connectivity_plus/connectivity_plus_platform_interface/README.md
@@ -0,0 +1,12 @@
+# Connectivity Plus Platform Interface
+
+[![Flutter Community: connectivity_plus_platform_interface](https://fluttercommunity.dev/_github/header/connectivity_plus_platform_interface)](https://github.com/fluttercommunity/community)
+
+[![pub package](https://img.shields.io/pub/v/connectivity_plus_platform_interface.svg)](https://pub.dev/packages/connectivity_plus_platform_interface)
+
+A common platform interface for [`connectivity_plus`](https://pub.dev/packages/connectivity_plus).
+
+## Usage
+
+This package is already included as part of the `connectivity_plus` package dependency, and will
+be included when using `connectivity_plus` as normal.
diff --git a/lib/third_party/connectivity_plus/connectivity_plus_platform_interface/lib/connectivity_plus_platform_interface.dart b/lib/third_party/connectivity_plus/connectivity_plus_platform_interface/lib/connectivity_plus_platform_interface.dart
new file mode 100644
index 00000000..76d98666
--- /dev/null
+++ b/lib/third_party/connectivity_plus/connectivity_plus_platform_interface/lib/connectivity_plus_platform_interface.dart
@@ -0,0 +1,51 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:plugin_platform_interface/plugin_platform_interface.dart';
+
+import 'method_channel_connectivity.dart';
+import 'src/enums.dart';
+
+export 'src/enums.dart';
+
+/// The interface that implementations of connectivity must implement.
+///
+/// Platform implementations should extend this class rather than implement it as `Connectivity`
+/// does not consider newly added methods to be breaking changes. Extending this class
+/// (using `extends`) ensures that the subclass will get the default implementation, while
+/// platform implementations that `implements` this interface will be broken by newly added
+/// [ConnectivityPlatform] methods.
+abstract class ConnectivityPlatform extends PlatformInterface {
+ /// Constructs a ConnectivityPlatform.
+ ConnectivityPlatform() : super(token: _token);
+
+ static final Object _token = Object();
+
+ static ConnectivityPlatform _instance = MethodChannelConnectivity();
+
+ /// The default instance of [ConnectivityPlatform] to use.
+ ///
+ /// Defaults to [MethodChannelConnectivity].
+ static ConnectivityPlatform get instance => _instance;
+
+ /// Platform-specific plugins should set this with their own platform-specific
+ /// class that extends [ConnectivityPlatform] when they register themselves.
+ static set instance(ConnectivityPlatform instance) {
+ PlatformInterface.verifyToken(instance, _token);
+ _instance = instance;
+ }
+
+ /// Checks the connection status of the device.
+ Future checkConnectivity() {
+ throw UnimplementedError('checkConnectivity() has not been implemented.');
+ }
+
+ /// Returns a Stream of ConnectivityResults changes.
+ Stream get onConnectivityChanged {
+ throw UnimplementedError(
+ 'get onConnectivityChanged has not been implemented.');
+ }
+}
diff --git a/lib/third_party/connectivity_plus/connectivity_plus_platform_interface/lib/method_channel_connectivity.dart b/lib/third_party/connectivity_plus/connectivity_plus_platform_interface/lib/method_channel_connectivity.dart
new file mode 100644
index 00000000..1c053d91
--- /dev/null
+++ b/lib/third_party/connectivity_plus/connectivity_plus_platform_interface/lib/method_channel_connectivity.dart
@@ -0,0 +1,43 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:connectivity_plus_platform_interface/connectivity_plus_platform_interface.dart';
+import 'package:flutter/services.dart';
+import 'package:meta/meta.dart';
+
+import 'src/utils.dart';
+
+/// An implementation of [ConnectivityPlatform] that uses method channels.
+class MethodChannelConnectivity extends ConnectivityPlatform {
+ /// The method channel used to interact with the native platform.
+ @visibleForTesting
+ MethodChannel methodChannel =
+ const MethodChannel('dev.fluttercommunity.plus/connectivity');
+
+ /// The event channel used to receive ConnectivityResult changes from the native platform.
+ @visibleForTesting
+ EventChannel eventChannel =
+ const EventChannel('dev.fluttercommunity.plus/connectivity_status');
+
+ Stream? _onConnectivityChanged;
+
+ /// Fires whenever the connectivity state changes.
+ @override
+ Stream get onConnectivityChanged {
+ _onConnectivityChanged ??= eventChannel
+ .receiveBroadcastStream()
+ .map((dynamic result) => result.toString())
+ .map(parseConnectivityResult);
+ return _onConnectivityChanged!;
+ }
+
+ @override
+ Future checkConnectivity() {
+ return methodChannel
+ .invokeMethod('check')
+ .then((value) => parseConnectivityResult(value ?? ''));
+ }
+}
diff --git a/lib/third_party/connectivity_plus/connectivity_plus_platform_interface/lib/src/enums.dart b/lib/third_party/connectivity_plus/connectivity_plus_platform_interface/lib/src/enums.dart
new file mode 100644
index 00000000..7f586995
--- /dev/null
+++ b/lib/third_party/connectivity_plus/connectivity_plus_platform_interface/lib/src/enums.dart
@@ -0,0 +1,27 @@
+/// Connection status check result.
+enum ConnectivityResult {
+ /// Bluetooth: Device connected via bluetooth
+ bluetooth,
+
+ /// WiFi: Device connected via Wi-Fi
+ wifi,
+
+ /// Ethernet: Device connected to ethernet network
+ ethernet,
+
+ /// Mobile: Device connected to cellular network
+ mobile,
+
+ /// None: Device not connected to any network
+ none,
+
+ /// VPN: Device connected to a VPN
+ ///
+ /// Note for iOS and macOS:
+ /// There is no separate network interface type for [vpn].
+ /// It returns [other] on any device (also simulator).
+ vpn,
+
+ /// Other: Device is connected to an unknown network
+ other
+}
diff --git a/lib/third_party/connectivity_plus/connectivity_plus_platform_interface/lib/src/utils.dart b/lib/third_party/connectivity_plus/connectivity_plus_platform_interface/lib/src/utils.dart
new file mode 100644
index 00000000..4032537d
--- /dev/null
+++ b/lib/third_party/connectivity_plus/connectivity_plus_platform_interface/lib/src/utils.dart
@@ -0,0 +1,22 @@
+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;
+ }
+}
diff --git a/lib/third_party/connectivity_plus/connectivity_plus_platform_interface/pubspec.yaml b/lib/third_party/connectivity_plus/connectivity_plus_platform_interface/pubspec.yaml
new file mode 100644
index 00000000..8c4c5efa
--- /dev/null
+++ b/lib/third_party/connectivity_plus/connectivity_plus_platform_interface/pubspec.yaml
@@ -0,0 +1,20 @@
+name: connectivity_plus_platform_interface
+description: A common platform interface for the connectivity_plus plugin.
+version: 1.2.4
+homepage: https://plus.fluttercommunity.dev/
+repository: https://github.com/fluttercommunity/plus_plugins/tree/main/packages/
+
+environment:
+ sdk: ">=2.12.0 <3.0.0"
+ flutter: ">=2.11.0"
+
+dependencies:
+ flutter:
+ sdk: flutter
+ meta: ^1.7.0
+ plugin_platform_interface: ^2.1.2
+
+dev_dependencies:
+ flutter_test:
+ sdk: flutter
+ flutter_lints: ^2.0.1
diff --git a/lib/third_party/nm/LICENSE b/lib/third_party/nm/LICENSE
new file mode 100644
index 00000000..14e2f777
--- /dev/null
+++ b/lib/third_party/nm/LICENSE
@@ -0,0 +1,373 @@
+Mozilla Public License Version 2.0
+==================================
+
+1. Definitions
+--------------
+
+1.1. "Contributor"
+ means each individual or legal entity that creates, contributes to
+ the creation of, or owns Covered Software.
+
+1.2. "Contributor Version"
+ means the combination of the Contributions of others (if any) used
+ by a Contributor and that particular Contributor's Contribution.
+
+1.3. "Contribution"
+ means Covered Software of a particular Contributor.
+
+1.4. "Covered Software"
+ means Source Code Form to which the initial Contributor has attached
+ the notice in Exhibit A, the Executable Form of such Source Code
+ Form, and Modifications of such Source Code Form, in each case
+ including portions thereof.
+
+1.5. "Incompatible With Secondary Licenses"
+ means
+
+ (a) that the initial Contributor has attached the notice described
+ in Exhibit B to the Covered Software; or
+
+ (b) that the Covered Software was made available under the terms of
+ version 1.1 or earlier of the License, but not also under the
+ terms of a Secondary License.
+
+1.6. "Executable Form"
+ means any form of the work other than Source Code Form.
+
+1.7. "Larger Work"
+ means a work that combines Covered Software with other material, in
+ a separate file or files, that is not Covered Software.
+
+1.8. "License"
+ means this document.
+
+1.9. "Licensable"
+ means having the right to grant, to the maximum extent possible,
+ whether at the time of the initial grant or subsequently, any and
+ all of the rights conveyed by this License.
+
+1.10. "Modifications"
+ means any of the following:
+
+ (a) any file in Source Code Form that results from an addition to,
+ deletion from, or modification of the contents of Covered
+ Software; or
+
+ (b) any new file in Source Code Form that contains any Covered
+ Software.
+
+1.11. "Patent Claims" of a Contributor
+ means any patent claim(s), including without limitation, method,
+ process, and apparatus claims, in any patent Licensable by such
+ Contributor that would be infringed, but for the grant of the
+ License, by the making, using, selling, offering for sale, having
+ made, import, or transfer of either its Contributions or its
+ Contributor Version.
+
+1.12. "Secondary License"
+ means either the GNU General Public License, Version 2.0, the GNU
+ Lesser General Public License, Version 2.1, the GNU Affero General
+ Public License, Version 3.0, or any later versions of those
+ licenses.
+
+1.13. "Source Code Form"
+ means the form of the work preferred for making modifications.
+
+1.14. "You" (or "Your")
+ means an individual or a legal entity exercising rights under this
+ License. For legal entities, "You" includes any entity that
+ controls, is controlled by, or is under common control with You. For
+ purposes of this definition, "control" means (a) the power, direct
+ or indirect, to cause the direction or management of such entity,
+ whether by contract or otherwise, or (b) ownership of more than
+ fifty percent (50%) of the outstanding shares or beneficial
+ ownership of such entity.
+
+2. License Grants and Conditions
+--------------------------------
+
+2.1. Grants
+
+Each Contributor hereby grants You a world-wide, royalty-free,
+non-exclusive license:
+
+(a) under intellectual property rights (other than patent or trademark)
+ Licensable by such Contributor to use, reproduce, make available,
+ modify, display, perform, distribute, and otherwise exploit its
+ Contributions, either on an unmodified basis, with Modifications, or
+ as part of a Larger Work; and
+
+(b) under Patent Claims of such Contributor to make, use, sell, offer
+ for sale, have made, import, and otherwise transfer either its
+ Contributions or its Contributor Version.
+
+2.2. Effective Date
+
+The licenses granted in Section 2.1 with respect to any Contribution
+become effective for each Contribution on the date the Contributor first
+distributes such Contribution.
+
+2.3. Limitations on Grant Scope
+
+The licenses granted in this Section 2 are the only rights granted under
+this License. No additional rights or licenses will be implied from the
+distribution or licensing of Covered Software under this License.
+Notwithstanding Section 2.1(b) above, no patent license is granted by a
+Contributor:
+
+(a) for any code that a Contributor has removed from Covered Software;
+ or
+
+(b) for infringements caused by: (i) Your and any other third party's
+ modifications of Covered Software, or (ii) the combination of its
+ Contributions with other software (except as part of its Contributor
+ Version); or
+
+(c) under Patent Claims infringed by Covered Software in the absence of
+ its Contributions.
+
+This License does not grant any rights in the trademarks, service marks,
+or logos of any Contributor (except as may be necessary to comply with
+the notice requirements in Section 3.4).
+
+2.4. Subsequent Licenses
+
+No Contributor makes additional grants as a result of Your choice to
+distribute the Covered Software under a subsequent version of this
+License (see Section 10.2) or under the terms of a Secondary License (if
+permitted under the terms of Section 3.3).
+
+2.5. Representation
+
+Each Contributor represents that the Contributor believes its
+Contributions are its original creation(s) or it has sufficient rights
+to grant the rights to its Contributions conveyed by this License.
+
+2.6. Fair Use
+
+This License is not intended to limit any rights You have under
+applicable copyright doctrines of fair use, fair dealing, or other
+equivalents.
+
+2.7. Conditions
+
+Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
+in Section 2.1.
+
+3. Responsibilities
+-------------------
+
+3.1. Distribution of Source Form
+
+All distribution of Covered Software in Source Code Form, including any
+Modifications that You create or to which You contribute, must be under
+the terms of this License. You must inform recipients that the Source
+Code Form of the Covered Software is governed by the terms of this
+License, and how they can obtain a copy of this License. You may not
+attempt to alter or restrict the recipients' rights in the Source Code
+Form.
+
+3.2. Distribution of Executable Form
+
+If You distribute Covered Software in Executable Form then:
+
+(a) such Covered Software must also be made available in Source Code
+ Form, as described in Section 3.1, and You must inform recipients of
+ the Executable Form how they can obtain a copy of such Source Code
+ Form by reasonable means in a timely manner, at a charge no more
+ than the cost of distribution to the recipient; and
+
+(b) You may distribute such Executable Form under the terms of this
+ License, or sublicense it under different terms, provided that the
+ license for the Executable Form does not attempt to limit or alter
+ the recipients' rights in the Source Code Form under this License.
+
+3.3. Distribution of a Larger Work
+
+You may create and distribute a Larger Work under terms of Your choice,
+provided that You also comply with the requirements of this License for
+the Covered Software. If the Larger Work is a combination of Covered
+Software with a work governed by one or more Secondary Licenses, and the
+Covered Software is not Incompatible With Secondary Licenses, this
+License permits You to additionally distribute such Covered Software
+under the terms of such Secondary License(s), so that the recipient of
+the Larger Work may, at their option, further distribute the Covered
+Software under the terms of either this License or such Secondary
+License(s).
+
+3.4. Notices
+
+You may not remove or alter the substance of any license notices
+(including copyright notices, patent notices, disclaimers of warranty,
+or limitations of liability) contained within the Source Code Form of
+the Covered Software, except that You may alter any license notices to
+the extent required to remedy known factual inaccuracies.
+
+3.5. Application of Additional Terms
+
+You may choose to offer, and to charge a fee for, warranty, support,
+indemnity or liability obligations to one or more recipients of Covered
+Software. However, You may do so only on Your own behalf, and not on
+behalf of any Contributor. You must make it absolutely clear that any
+such warranty, support, indemnity, or liability obligation is offered by
+You alone, and You hereby agree to indemnify every Contributor for any
+liability incurred by such Contributor as a result of warranty, support,
+indemnity or liability terms You offer. You may include additional
+disclaimers of warranty and limitations of liability specific to any
+jurisdiction.
+
+4. Inability to Comply Due to Statute or Regulation
+---------------------------------------------------
+
+If it is impossible for You to comply with any of the terms of this
+License with respect to some or all of the Covered Software due to
+statute, judicial order, or regulation then You must: (a) comply with
+the terms of this License to the maximum extent possible; and (b)
+describe the limitations and the code they affect. Such description must
+be placed in a text file included with all distributions of the Covered
+Software under this License. Except to the extent prohibited by statute
+or regulation, such description must be sufficiently detailed for a
+recipient of ordinary skill to be able to understand it.
+
+5. Termination
+--------------
+
+5.1. The rights granted under this License will terminate automatically
+if You fail to comply with any of its terms. However, if You become
+compliant, then the rights granted under this License from a particular
+Contributor are reinstated (a) provisionally, unless and until such
+Contributor explicitly and finally terminates Your grants, and (b) on an
+ongoing basis, if such Contributor fails to notify You of the
+non-compliance by some reasonable means prior to 60 days after You have
+come back into compliance. Moreover, Your grants from a particular
+Contributor are reinstated on an ongoing basis if such Contributor
+notifies You of the non-compliance by some reasonable means, this is the
+first time You have received notice of non-compliance with this License
+from such Contributor, and You become compliant prior to 30 days after
+Your receipt of the notice.
+
+5.2. If You initiate litigation against any entity by asserting a patent
+infringement claim (excluding declaratory judgment actions,
+counter-claims, and cross-claims) alleging that a Contributor Version
+directly or indirectly infringes any patent, then the rights granted to
+You by any and all Contributors for the Covered Software under Section
+2.1 of this License shall terminate.
+
+5.3. In the event of termination under Sections 5.1 or 5.2 above, all
+end user license agreements (excluding distributors and resellers) which
+have been validly granted by You or Your distributors under this License
+prior to termination shall survive termination.
+
+************************************************************************
+* *
+* 6. Disclaimer of Warranty *
+* ------------------------- *
+* *
+* Covered Software is provided under this License on an "as is" *
+* basis, without warranty of any kind, either expressed, implied, or *
+* statutory, including, without limitation, warranties that the *
+* Covered Software is free of defects, merchantable, fit for a *
+* particular purpose or non-infringing. The entire risk as to the *
+* quality and performance of the Covered Software is with You. *
+* Should any Covered Software prove defective in any respect, You *
+* (not any Contributor) assume the cost of any necessary servicing, *
+* repair, or correction. This disclaimer of warranty constitutes an *
+* essential part of this License. No use of any Covered Software is *
+* authorized under this License except under this disclaimer. *
+* *
+************************************************************************
+
+************************************************************************
+* *
+* 7. Limitation of Liability *
+* -------------------------- *
+* *
+* Under no circumstances and under no legal theory, whether tort *
+* (including negligence), contract, or otherwise, shall any *
+* Contributor, or anyone who distributes Covered Software as *
+* permitted above, be liable to You for any direct, indirect, *
+* special, incidental, or consequential damages of any character *
+* including, without limitation, damages for lost profits, loss of *
+* goodwill, work stoppage, computer failure or malfunction, or any *
+* and all other commercial damages or losses, even if such party *
+* shall have been informed of the possibility of such damages. This *
+* limitation of liability shall not apply to liability for death or *
+* personal injury resulting from such party's negligence to the *
+* extent applicable law prohibits such limitation. Some *
+* jurisdictions do not allow the exclusion or limitation of *
+* incidental or consequential damages, so this exclusion and *
+* limitation may not apply to You. *
+* *
+************************************************************************
+
+8. Litigation
+-------------
+
+Any litigation relating to this License may be brought only in the
+courts of a jurisdiction where the defendant maintains its principal
+place of business and such litigation shall be governed by laws of that
+jurisdiction, without reference to its conflict-of-law provisions.
+Nothing in this Section shall prevent a party's ability to bring
+cross-claims or counter-claims.
+
+9. Miscellaneous
+----------------
+
+This License represents the complete agreement concerning the subject
+matter hereof. If any provision of this License is held to be
+unenforceable, such provision shall be reformed only to the extent
+necessary to make it enforceable. Any law or regulation which provides
+that the language of a contract shall be construed against the drafter
+shall not be used to construe this License against a Contributor.
+
+10. Versions of the License
+---------------------------
+
+10.1. New Versions
+
+Mozilla Foundation is the license steward. Except as provided in Section
+10.3, no one other than the license steward has the right to modify or
+publish new versions of this License. Each version will be given a
+distinguishing version number.
+
+10.2. Effect of New Versions
+
+You may distribute the Covered Software under the terms of the version
+of the License under which You originally received the Covered Software,
+or under the terms of any subsequent version published by the license
+steward.
+
+10.3. Modified Versions
+
+If you create software not governed by this License, and you want to
+create a new license for such software, you may create and use a
+modified version of this License if you rename the license and remove
+any references to the name of the license steward (except to note that
+such modified license differs from this License).
+
+10.4. Distributing Source Code Form that is Incompatible With Secondary
+Licenses
+
+If You choose to distribute Source Code Form that is Incompatible With
+Secondary Licenses under the terms of this version of the License, the
+notice described in Exhibit B of this License must be attached.
+
+Exhibit A - Source Code Form License Notice
+-------------------------------------------
+
+ This Source Code Form is subject to the terms of the Mozilla Public
+ License, v. 2.0. If a copy of the MPL was not distributed with this
+ file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+If it is not possible or desirable to put the notice in a particular
+file, then You may include the notice in a location (such as a LICENSE
+file in a relevant directory) where a recipient would be likely to look
+for such a notice.
+
+You may add additional accurate notices of copyright ownership.
+
+Exhibit B - "Incompatible With Secondary Licenses" Notice
+---------------------------------------------------------
+
+ This Source Code Form is "Incompatible With Secondary Licenses", as
+ defined by the Mozilla Public License, v. 2.0.
diff --git a/lib/third_party/nm/nm.dart b/lib/third_party/nm/nm.dart
new file mode 100644
index 00000000..7d74ec95
--- /dev/null
+++ b/lib/third_party/nm/nm.dart
@@ -0,0 +1 @@
+export 'src/network_manager_client.dart';
diff --git a/lib/third_party/nm/src/network_manager_client.dart b/lib/third_party/nm/src/network_manager_client.dart
new file mode 100644
index 00000000..d9b9ff19
--- /dev/null
+++ b/lib/third_party/nm/src/network_manager_client.dart
@@ -0,0 +1,403 @@
+import 'dart:async';
+
+import 'package:dbus/dbus.dart';
+
+/// D-Bus interface names
+const _managerInterfaceName = 'org.freedesktop.NetworkManager';
+
+/// Overall networking states.
+enum NetworkManagerState {
+ unknown,
+ asleep,
+ disconnected,
+ disconnecting,
+ connecting,
+ connectedLocal,
+ connectedSite,
+ connectedGlobal,
+}
+
+NetworkManagerState _decodeState(int value) {
+ switch (value) {
+ case 10:
+ return NetworkManagerState.asleep;
+ case 20:
+ return NetworkManagerState.disconnected;
+ case 30:
+ return NetworkManagerState.disconnecting;
+ case 40:
+ return NetworkManagerState.connecting;
+ case 50:
+ return NetworkManagerState.connectedLocal;
+ case 60:
+ return NetworkManagerState.connectedSite;
+ case 70:
+ return NetworkManagerState.connectedGlobal;
+ default:
+ return NetworkManagerState.unknown;
+ }
+}
+
+/// Internet connectivity states.
+enum NetworkManagerConnectivityState { unknown, none, portal, limited, full }
+
+NetworkManagerConnectivityState _decodeConnectivityState(int value) {
+ switch (value) {
+ case 1:
+ return NetworkManagerConnectivityState.none;
+ case 2:
+ return NetworkManagerConnectivityState.portal;
+ case 3:
+ return NetworkManagerConnectivityState.limited;
+ case 4:
+ return NetworkManagerConnectivityState.full;
+ default:
+ return NetworkManagerConnectivityState.unknown;
+ }
+}
+
+class _NetworkManagerInterface {
+ final Map properties;
+ final propertiesChangedStreamController =
+ StreamController>.broadcast();
+
+ /// Stream of property names as their values change.
+ Stream> get propertiesChanged =>
+ propertiesChangedStreamController.stream;
+
+ _NetworkManagerInterface(this.properties);
+
+ void updateProperties(Map changedProperties) {
+ properties.addAll(changedProperties);
+ propertiesChangedStreamController.add(changedProperties.keys.toList());
+ }
+}
+
+class _NetworkManagerObject extends DBusRemoteObject {
+ final interfaces = {};
+
+ void updateInterfaces(
+ Map> interfacesAndProperties) {
+ interfacesAndProperties.forEach((interfaceName, properties) {
+ interfaces[interfaceName] = _NetworkManagerInterface(properties);
+ });
+ }
+
+ /// Returns true if removing [interfaceNames] would remove all interfaces on this object.
+ bool wouldRemoveAllInterfaces(List interfaceNames) {
+ for (var interface in interfaces.keys) {
+ if (!interfaceNames.contains(interface)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ void removeInterfaces(List interfaceNames) {
+ for (var interfaceName in interfaceNames) {
+ interfaces.remove(interfaceName);
+ }
+ }
+
+ void updateProperties(
+ String interfaceName, Map changedProperties) {
+ var interface = interfaces[interfaceName];
+ if (interface != null) {
+ interface.updateProperties(changedProperties);
+ }
+ }
+
+ /// Gets a cached property.
+ DBusValue? getCachedProperty(String interfaceName, String name) {
+ var interface = interfaces[interfaceName];
+ if (interface == null) {
+ return null;
+ }
+ return interface.properties[name];
+ }
+
+ /// Gets a cached boolean property, or returns null if not present or not the correct type.
+ bool? getBooleanProperty(String interface, String name) {
+ var value = getCachedProperty(interface, name);
+ if (value == null) {
+ return null;
+ }
+ if (value.signature != DBusSignature('b')) {
+ return null;
+ }
+ return (value as DBusBoolean).value;
+ }
+
+ /// Gets a cached unsigned 8 bit integer property, or returns null if not present or not the correct type.
+ int? getByteProperty(String interface, String name) {
+ var value = getCachedProperty(interface, name);
+ if (value == null) {
+ return null;
+ }
+ if (value.signature != DBusSignature('y')) {
+ return null;
+ }
+ return (value as DBusByte).value;
+ }
+
+ /// Gets a cached signed 32 bit integer property, or returns null if not present or not the correct type.
+ int? getInt32Property(String interface, String name) {
+ var value = getCachedProperty(interface, name);
+ if (value == null) {
+ return null;
+ }
+ if (value.signature != DBusSignature('i')) {
+ return null;
+ }
+ return (value as DBusInt32).value;
+ }
+
+ /// Gets a cached unsigned 32 bit integer property, or returns null if not present or not the correct type.
+ int? getUint32Property(String interface, String name) {
+ var value = getCachedProperty(interface, name);
+ if (value == null) {
+ return null;
+ }
+ if (value.signature != DBusSignature('u')) {
+ return null;
+ }
+ return (value as DBusUint32).value;
+ }
+
+ /// Gets a cached signed 64 bit integer property, or returns null if not present or not the correct type.
+ int? getInt64Property(String interface, String name) {
+ var value = getCachedProperty(interface, name);
+ if (value == null) {
+ return null;
+ }
+ if (value.signature != DBusSignature('x')) {
+ return null;
+ }
+ return (value as DBusInt64).value;
+ }
+
+ /// Gets a cached unsigned 64 bit integer property, or returns null if not present or not the correct type.
+ int? getUint64Property(String interface, String name) {
+ var value = getCachedProperty(interface, name);
+ if (value == null) {
+ return null;
+ }
+ if (value.signature != DBusSignature('t')) {
+ return null;
+ }
+ return (value as DBusUint64).value;
+ }
+
+ /// Gets a cached string property, or returns null if not present or not the correct type.
+ String? getStringProperty(String interface, String name) {
+ var value = getCachedProperty(interface, name);
+ if (value == null) {
+ return null;
+ }
+ if (value.signature != DBusSignature('s')) {
+ return null;
+ }
+ return (value as DBusString).value;
+ }
+
+ /// Gets a cached string array property, or returns null if not present or not the correct type.
+ List? getStringArrayProperty(String interface, String name) {
+ var value = getCachedProperty(interface, name);
+ if (value == null) {
+ return null;
+ }
+ if (value.signature != DBusSignature('as')) {
+ return null;
+ }
+ return (value as DBusArray)
+ .children
+ .map((e) => (e as DBusString).value)
+ .toList();
+ }
+
+ /// Gets a cached object path property, or returns null if not present or not the correct type.
+ DBusObjectPath? getObjectPathProperty(String interface, String name) {
+ var value = getCachedProperty(interface, name);
+ if (value == null) {
+ return null;
+ }
+ if (value.signature != DBusSignature('o')) {
+ return null;
+ }
+ return (value as DBusObjectPath);
+ }
+
+ /// Gets a cached object path array property, or returns null if not present or not the correct type.
+ List? getObjectPathArrayProperty(
+ String interface, String name) {
+ var value = getCachedProperty(interface, name);
+ if (value == null) {
+ return null;
+ }
+ if (value.signature != DBusSignature('ao')) {
+ return null;
+ }
+ return (value as DBusArray)
+ .children
+ .map((e) => (e as DBusObjectPath))
+ .toList();
+ }
+
+ /// Gets a cached list of data property, or returns null if not present or not the correct type.
+ List