diff --git a/packages/connectivity/experimental_connectivity_web/.gitignore b/packages/connectivity/experimental_connectivity_web/.gitignore new file mode 100644 index 000000000000..d7dee828a6b9 --- /dev/null +++ b/packages/connectivity/experimental_connectivity_web/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +.dart_tool/ + +.packages +.pub/ + +build/ +lib/generated_plugin_registrant.dart diff --git a/packages/connectivity/experimental_connectivity_web/.metadata b/packages/connectivity/experimental_connectivity_web/.metadata new file mode 100644 index 000000000000..23eb55ba6da2 --- /dev/null +++ b/packages/connectivity/experimental_connectivity_web/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 52ee8a6c6565cd421dfa32042941eb40691f4746 + channel: master + +project_type: plugin diff --git a/packages/connectivity/experimental_connectivity_web/CHANGELOG.md b/packages/connectivity/experimental_connectivity_web/CHANGELOG.md new file mode 100644 index 000000000000..6073234226b2 --- /dev/null +++ b/packages/connectivity/experimental_connectivity_web/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.1.0 + +* Initial release. diff --git a/packages/connectivity/experimental_connectivity_web/LICENSE b/packages/connectivity/experimental_connectivity_web/LICENSE new file mode 100644 index 000000000000..4da9688730d1 --- /dev/null +++ b/packages/connectivity/experimental_connectivity_web/LICENSE @@ -0,0 +1,26 @@ +Copyright 2016, the Flutter project 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/packages/connectivity/experimental_connectivity_web/README.md b/packages/connectivity/experimental_connectivity_web/README.md new file mode 100644 index 000000000000..01674138623a --- /dev/null +++ b/packages/connectivity/experimental_connectivity_web/README.md @@ -0,0 +1,57 @@ +# experimental_connectivity_web + +A web implementation of [connectivity](https://pub.dev/connectivity/connectivity). Currently this package uses an experimental API, so not all browsers that Flutter web supports are supported. + +## Usage + +### Import the package + +This package is a non-endorsed implementation of `connectivity` for the web platform, so you need to modify your `pubspec.yaml` to use it: + +```yaml +... +dependencies: + ... + connectivity: ^0.4.9 + experimental_connectivity_web: ^0.1.0 + ... +... +``` + +## Example + +Find the example wiring in the [Google sign-in example application](https://github.com/flutter/plugins/blob/master/packages/connectivity/connectivity/example/lib/main.dart). + +## Limitations on the web platform + +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 (February 2020) is still "experimental". + +![Data on support for the netinfo feature across the major browsers from caniuse.com](https://caniuse.bitsofco.de/image/netinfo.png) + +On desktop browsers, the API only returns a very broad set of connectivity statuses (One of `'slow-2g', '2g', '3g', or '4g'`), and may *not* provide an Stream of changes. Firefox still hasn't enabled this feature by default. + +Other than the approximate "downlink" speed, and due to security and privacy concerns, this Web API will not 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, in any web platform (mobile or desktop). + +### `null` connectivity results + +Because of the limitations above, unsupported browsers will return `null` connectivity results, both on the `checkConnectivity` call, and the `onConnectivityChanged` stream. You should adapt your app code to check for nulls being returned from calls to the plugin. + +## Contributions and Testing + +Tests are a crucial to contributions to this package. All new contributions should be reasonably tested. + +In order to run tests in this package, do: + +``` +cd test +flutter run -d chrome +``` + +Contributions to this package are welcome. Read the [Contributing to Flutter Plugins](https://github.com/flutter/plugins/blob/master/CONTRIBUTING.md) guide to get started. + +## Issues and feedback + +Please file an [issue](https://github.com/ditman/plugins/issues/new) +to send feedback or report a bug. + +**Thank you!** diff --git a/packages/connectivity/experimental_connectivity_web/android/.gitignore b/packages/connectivity/experimental_connectivity_web/android/.gitignore new file mode 100644 index 000000000000..c6cbe562a427 --- /dev/null +++ b/packages/connectivity/experimental_connectivity_web/android/.gitignore @@ -0,0 +1,8 @@ +*.iml +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures diff --git a/packages/connectivity/experimental_connectivity_web/android/build.gradle b/packages/connectivity/experimental_connectivity_web/android/build.gradle new file mode 100644 index 000000000000..09af6eb5f0ba --- /dev/null +++ b/packages/connectivity/experimental_connectivity_web/android/build.gradle @@ -0,0 +1,43 @@ +group 'com.example.connectivity_web' +version '1.0-SNAPSHOT' + +buildscript { + ext.kotlin_version = '1.3.50' + repositories { + google() + jcenter() + } + + dependencies { + classpath 'com.android.tools.build:gradle:3.5.0' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +rootProject.allprojects { + repositories { + google() + jcenter() + } +} + +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' + +android { + compileSdkVersion 28 + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } + defaultConfig { + minSdkVersion 16 + } + lintOptions { + disable 'InvalidPackage' + } +} + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" +} diff --git a/packages/connectivity/experimental_connectivity_web/android/gradle.properties b/packages/connectivity/experimental_connectivity_web/android/gradle.properties new file mode 100644 index 000000000000..38c8d4544ff1 --- /dev/null +++ b/packages/connectivity/experimental_connectivity_web/android/gradle.properties @@ -0,0 +1,4 @@ +org.gradle.jvmargs=-Xmx1536M +android.enableR8=true +android.useAndroidX=true +android.enableJetifier=true diff --git a/packages/connectivity/experimental_connectivity_web/android/gradle/wrapper/gradle-wrapper.properties b/packages/connectivity/experimental_connectivity_web/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000000..01a286e96a21 --- /dev/null +++ b/packages/connectivity/experimental_connectivity_web/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip diff --git a/packages/connectivity/experimental_connectivity_web/android/settings.gradle b/packages/connectivity/experimental_connectivity_web/android/settings.gradle new file mode 100644 index 000000000000..99cc01467562 --- /dev/null +++ b/packages/connectivity/experimental_connectivity_web/android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'connectivity_web' diff --git a/packages/connectivity/experimental_connectivity_web/android/src/main/AndroidManifest.xml b/packages/connectivity/experimental_connectivity_web/android/src/main/AndroidManifest.xml new file mode 100644 index 000000000000..afd9aa90b517 --- /dev/null +++ b/packages/connectivity/experimental_connectivity_web/android/src/main/AndroidManifest.xml @@ -0,0 +1,3 @@ + + diff --git a/packages/connectivity/experimental_connectivity_web/android/src/main/kotlin/com/example/connectivity_web/ConnectivityWebPlugin.kt b/packages/connectivity/experimental_connectivity_web/android/src/main/kotlin/com/example/connectivity_web/ConnectivityWebPlugin.kt new file mode 100644 index 000000000000..73e36d2dcff5 --- /dev/null +++ b/packages/connectivity/experimental_connectivity_web/android/src/main/kotlin/com/example/connectivity_web/ConnectivityWebPlugin.kt @@ -0,0 +1,52 @@ +package com.example.connectivity_web + +import androidx.annotation.NonNull; +import io.flutter.embedding.engine.plugins.FlutterPlugin +import io.flutter.plugin.common.MethodCall +import io.flutter.plugin.common.MethodChannel +import io.flutter.plugin.common.MethodChannel.MethodCallHandler +import io.flutter.plugin.common.MethodChannel.Result +import io.flutter.plugin.common.PluginRegistry.Registrar + +/** ConnectivityWebPlugin */ +public class ConnectivityWebPlugin: FlutterPlugin, MethodCallHandler { + /// The MethodChannel that will the communication between Flutter and native Android + /// + /// This local reference serves to register the plugin with the Flutter Engine and unregister it + /// when the Flutter Engine is detached from the Activity + private lateinit var channel : MethodChannel + + override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { + channel = MethodChannel(flutterPluginBinding.getFlutterEngine().getDartExecutor(), "connectivity_web") + channel.setMethodCallHandler(this); + } + + // This static function is optional and equivalent to onAttachedToEngine. It supports the old + // pre-Flutter-1.12 Android projects. You are encouraged to continue supporting + // plugin registration via this function while apps migrate to use the new Android APIs + // post-flutter-1.12 via https://flutter.dev/go/android-project-migration. + // + // It is encouraged to share logic between onAttachedToEngine and registerWith to keep + // them functionally equivalent. Only one of onAttachedToEngine or registerWith will be called + // depending on the user's project. onAttachedToEngine or registerWith must both be defined + // in the same class. + companion object { + @JvmStatic + fun registerWith(registrar: Registrar) { + val channel = MethodChannel(registrar.messenger(), "connectivity_web") + channel.setMethodCallHandler(ConnectivityWebPlugin()) + } + } + + override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) { + if (call.method == "getPlatformVersion") { + result.success("Android ${android.os.Build.VERSION.RELEASE}") + } else { + result.notImplemented() + } + } + + override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) { + channel.setMethodCallHandler(null) + } +} diff --git a/packages/connectivity/experimental_connectivity_web/ios/connectivity_web.podspec b/packages/connectivity/experimental_connectivity_web/ios/connectivity_web.podspec new file mode 100644 index 000000000000..6f0fb034b705 --- /dev/null +++ b/packages/connectivity/experimental_connectivity_web/ios/connectivity_web.podspec @@ -0,0 +1,23 @@ +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. +# Run `pod lib lint connectivity_web.podspec' to validate before publishing. +# +Pod::Spec.new do |s| + s.name = 'connectivity_web' + s.version = '0.1.0' + s.summary = 'No-op implementation of connectivity_web web plugin to avoid build issues on iOS' + s.description = <<-DESC +temp fake connectivity_web plugin + DESC + s.homepage = 'https://github.com/flutter/plugins/tree/master/packages/connectivity/connectivity_web' + s.license = { :file => '../LICENSE' } + s.author = { 'Flutter Team' => 'flutter-dev@googlegroups.com' } + s.source = { :path => '.' } + s.source_files = 'Classes/**/*' + s.dependency 'Flutter' + s.platform = :ios, '8.0' + + # Flutter.framework does not contain a i386 slice. Only x86_64 simulators are supported. + s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS[sdk=iphonesimulator*]' => 'x86_64' } + s.swift_version = '5.0' +end diff --git a/packages/connectivity/experimental_connectivity_web/lib/experimental_connectivity_web.dart b/packages/connectivity/experimental_connectivity_web/lib/experimental_connectivity_web.dart new file mode 100644 index 000000000000..e28772944a94 --- /dev/null +++ b/packages/connectivity/experimental_connectivity_web/lib/experimental_connectivity_web.dart @@ -0,0 +1,110 @@ +import 'dart:async'; + +import 'package:connectivity_platform_interface/connectivity_platform_interface.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_web_plugins/flutter_web_plugins.dart'; +import 'package:js/js.dart'; + +import 'src/generated/network_information_types.dart' as dom; +import 'src/utils/connectivity_result.dart'; + +/// The web implementation of the ConnectivityPlatform of the Connectivity plugin. +class ConnectivityPlugin extends ConnectivityPlatform { + /// Factory method that initializes the connectivity plugin platform with an instance + /// of the plugin for the web. + static void registerWith(Registrar registrar) { + ConnectivityPlatform.instance = ConnectivityPlugin(); + } + + final dom.NetworkInformation _networkInformation; + final bool _networkInformationApiSupported; + + /// The constructor of the plugin. + ConnectivityPlugin() : this.withConnection(dom.navigator?.connection); + + /// Creates the plugin, with an override of the NetworkInformation object. + @visibleForTesting + ConnectivityPlugin.withConnection(dom.NetworkInformation connection) + : _networkInformationApiSupported = connection != null, + _networkInformation = connection; + + /// Checks the connection status of the device. + @override + Future checkConnectivity() async { + if (!_networkInformationApiSupported) { + return null; + } + return networkInformationToConnectivityResult(_networkInformation); + } + + Stream get _noopStream async* { + yield null; + } + + StreamController _connectivityResult; + + /// Returns a Stream of ConnectivityResults changes. + @override + Stream get onConnectivityChanged { + if (!_networkInformationApiSupported) { + return _noopStream; + } + if (_connectivityResult == null) { + _connectivityResult = StreamController(); + _networkInformation.onchange = allowInterop((_) { + _connectivityResult + .add(networkInformationToConnectivityResult(_networkInformation)); + }); + } + return _connectivityResult.stream; + } + + /// Obtains the wifi name (SSID) of the connected network + @override + Future getWifiName() { + throw PlatformException( + code: 'UNSUPPORTED_OPERATION', + message: 'getWifiName() is not supported on the web platform.', + ); + } + + /// Obtains the wifi BSSID of the connected network. + @override + Future getWifiBSSID() { + throw PlatformException( + code: 'UNSUPPORTED_OPERATION', + message: 'getWifiBSSID() is not supported on the web platform.', + ); + } + + /// Obtains the IP address of the connected wifi network + @override + Future getWifiIP() { + throw PlatformException( + code: 'UNSUPPORTED_OPERATION', + message: 'getWifiIP() is not supported on the web platform.', + ); + } + + /// Request to authorize the location service (Only on iOS). + @override + Future requestLocationServiceAuthorization( + {bool requestAlwaysLocationUsage = false}) { + throw PlatformException( + code: 'UNSUPPORTED_OPERATION', + message: + 'requestLocationServiceAuthorization() is not supported on the web platform.', + ); + } + + /// Get the current location service authorization (Only on iOS). + @override + Future getLocationServiceAuthorization() { + throw PlatformException( + code: 'UNSUPPORTED_OPERATION', + message: + 'getLocationServiceAuthorization() is not supported on the web platform.', + ); + } +} diff --git a/packages/connectivity/experimental_connectivity_web/lib/src/generated/network_information_types.dart b/packages/connectivity/experimental_connectivity_web/lib/src/generated/network_information_types.dart new file mode 100644 index 000000000000..c4045b3ec1fc --- /dev/null +++ b/packages/connectivity/experimental_connectivity_web/lib/src/generated/network_information_types.dart @@ -0,0 +1,78 @@ +@JS() +library network_information_types; + +import "package:js/js.dart"; +import "dart:html" show EventListener, EventTarget; + +/// W3C Spec Draft http://wicg.github.io/netinfo/ +/// Edition: Draft Community Group Report 20 February 2019 + +/// http://wicg.github.io/netinfo/#navigatornetworkinformation-interface +@anonymous +@JS() +abstract class Navigator implements NavigatorNetworkInformation {} + +@anonymous +@JS() +abstract class WorkerNavigator implements NavigatorNetworkInformation { + external factory WorkerNavigator({NetworkInformation connection}); +} + +/// http://wicg.github.io/netinfo/#navigatornetworkinformation-interface +@anonymous +@JS() +abstract class NavigatorNetworkInformation { + external NetworkInformation get connection; + external factory NavigatorNetworkInformation({NetworkInformation connection}); +} + +/// http://wicg.github.io/netinfo/#connection-types +/*type ConnectionType = + | 'bluetooth' + | 'cellular' + | 'ethernet' + | 'mixed' + | 'none' + | 'other' + | 'unknown' + | 'wifi' + | 'wimax'; +*/ + +/// http://wicg.github.io/netinfo/#effectiveconnectiontype-enum +/*type EffectiveConnectionType = '2g' | '3g' | '4g' | 'slow-2g';*/ + +/// http://wicg.github.io/netinfo/#dom-megabit +/*type Megabit = number;*/ +/// http://wicg.github.io/netinfo/#dom-millisecond +/*type Millisecond = number;*/ + +/// http://wicg.github.io/netinfo/#networkinformation-interface +@anonymous +@JS() +abstract class NetworkInformation implements EventTarget { + /// http://wicg.github.io/netinfo/#type-attribute + external String /*'bluetooth'|'cellular'|'ethernet'|'mixed'|'none'|'other'|'unknown'|'wifi'|'wimax'*/ get type; + + /// http://wicg.github.io/netinfo/#effectivetype-attribute + external String /*'2g'|'3g'|'4g'|'slow-2g'*/ get effectiveType; + + /// http://wicg.github.io/netinfo/#downlinkmax-attribute + external num get downlinkMax; + + /// http://wicg.github.io/netinfo/#downlink-attribute + external num get downlink; + + /// http://wicg.github.io/netinfo/#rtt-attribute + external num get rtt; + + /// http://wicg.github.io/netinfo/#savedata-attribute + external bool get saveData; + + /// http://wicg.github.io/netinfo/#handling-changes-to-the-underlying-connection + external EventListener get onchange; + external set onchange(EventListener v); +} + +@JS() +external Navigator get navigator; diff --git a/packages/connectivity/experimental_connectivity_web/lib/src/utils/connectivity_result.dart b/packages/connectivity/experimental_connectivity_web/lib/src/utils/connectivity_result.dart new file mode 100644 index 000000000000..28943ef5c7e1 --- /dev/null +++ b/packages/connectivity/experimental_connectivity_web/lib/src/utils/connectivity_result.dart @@ -0,0 +1,56 @@ +import 'package:connectivity_platform_interface/connectivity_platform_interface.dart'; + +/// Converts an incoming NetworkInformation object into the correct ConnectivityResult. +// +// We can't be more specific on the signature of this method because the API is odd, +// data can come from a static value in the DOM, or as the 'target' of a DOM Event. +// +// If we type info as `NetworkInformation`, Dart will complain with: +// "Uncaught Error: Expected a value of type 'NetworkInformation', +// but got one of type 'NetworkInformation'" +ConnectivityResult networkInformationToConnectivityResult( + dynamic /* NetworkInformation */ info) { + if (info == null) { + return ConnectivityResult.none; + } + 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': + 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': + case 'cellular': + case 'mixed': + case 'other': + case 'unknown': + return ConnectivityResult.mobile; + default: + return ConnectivityResult.wifi; + } +} diff --git a/packages/connectivity/experimental_connectivity_web/pubspec.yaml b/packages/connectivity/experimental_connectivity_web/pubspec.yaml new file mode 100644 index 000000000000..1da5af4ed050 --- /dev/null +++ b/packages/connectivity/experimental_connectivity_web/pubspec.yaml @@ -0,0 +1,31 @@ +name: experimental_connectivity_web +description: An (experimental) implementation for the web platform of the Flutter `connectivity` plugin. This uses the browser's NetworkInformation Web API through Dart's JS interop, where available. Due to privacy concerns, not all features available to Mobile apps are available to Web apps. +version: 0.1.0 +homepage: https://github.com/ditman/plugins/tree/connectivity-web/packages/connectivity/experimental_connectivity_web + +flutter: + plugin: + platforms: + web: + pluginClass: ConnectivityPlugin + fileName: experimental_connectivity_web.dart + +dependencies: + connectivity_platform_interface: ^1.0.3 + js: ^0.6.1+1 + flutter_web_plugins: + sdk: flutter + flutter: + sdk: flutter + +dev_dependencies: + test: any + flutter_driver: + sdk: flutter + flutter_test: + sdk: flutter + e2e: ^0.2.4+3 + +environment: + sdk: ">=2.6.0 <3.0.0" + flutter: ">=1.12.13+hotfix.4" diff --git a/packages/connectivity/experimental_connectivity_web/test/.gitignore b/packages/connectivity/experimental_connectivity_web/test/.gitignore new file mode 100644 index 000000000000..d7dee828a6b9 --- /dev/null +++ b/packages/connectivity/experimental_connectivity_web/test/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +.dart_tool/ + +.packages +.pub/ + +build/ +lib/generated_plugin_registrant.dart diff --git a/packages/connectivity/experimental_connectivity_web/test/lib/main.dart b/packages/connectivity/experimental_connectivity_web/test/lib/main.dart new file mode 100644 index 000000000000..128845efb347 --- /dev/null +++ b/packages/connectivity/experimental_connectivity_web/test/lib/main.dart @@ -0,0 +1,91 @@ +import 'package:e2e/e2e.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:connectivity_platform_interface/connectivity_platform_interface.dart'; +import 'package:experimental_connectivity_web/experimental_connectivity_web.dart'; + +import 'src/connectivity_mocks.dart'; + +void main() { + E2EWidgetsFlutterBinding.ensureInitialized(); + + group('checkConnectivity', () { + void testCheckConnectivity({ + String type, + String effectiveType, + num downlink = 10, + num rtt = 50, + ConnectivityResult expected, + }) { + MockNetworkInformation connection = MockNetworkInformation( + type: type, + effectiveType: effectiveType, + downlink: downlink, + rtt: rtt); + ConnectivityPlugin plugin = ConnectivityPlugin.withConnection(connection); + expect(plugin.checkConnectivity(), completion(equals(expected))); + } + + group('in Chrome', () { + test('0 downlink and rtt -> none', () { + testCheckConnectivity( + effectiveType: '4g', + downlink: 0, + rtt: 0, + expected: ConnectivityResult.none); + }); + test('slow-2g -> mobile', () { + testCheckConnectivity( + effectiveType: 'slow-2g', expected: ConnectivityResult.mobile); + }); + test('2g -> mobile', () { + testCheckConnectivity( + effectiveType: '2g', expected: ConnectivityResult.mobile); + }); + test('3g -> mobile', () { + testCheckConnectivity( + effectiveType: '3g', expected: ConnectivityResult.mobile); + }); + test('4g -> wifi', () { + testCheckConnectivity( + effectiveType: '4g', expected: ConnectivityResult.wifi); + }); + }); + + group('unsupported browsers', () { + test('null connection -> null', () { + ConnectivityPlugin plugin = ConnectivityPlugin.withConnection(null); + expect(plugin.checkConnectivity(), completion(null)); + }); + }); + }); + + group('get onConnectivityChanged', () { + group('in Chrome', () { + test('puts change events in a Stream', () async { + MockNetworkInformation connection = + MockNetworkInformation(effectiveType: '4g', downlink: 10, rtt: 50); + ConnectivityPlugin plugin = + ConnectivityPlugin.withConnection(connection); + + Stream results = plugin.onConnectivityChanged; + + // Fake a disconnect-reconnect + connection.mockChangeValue(downlink: 0, rtt: 0); + connection.mockChangeValue(downlink: 10, rtt: 50); + + // The stream of results is infinite, so we need to .take(2) for this test to complete. + expect( + results.take(2).toList(), + completion( + equals([ConnectivityResult.none, ConnectivityResult.wifi]))); + }); + }); + + group('unsupported browsers', () { + test('null connection -> null', () { + ConnectivityPlugin plugin = ConnectivityPlugin.withConnection(null); + expect(plugin.onConnectivityChanged.last, completion(null)); + }); + }); + }); +} diff --git a/packages/connectivity/experimental_connectivity_web/test/lib/src/connectivity_mocks.dart b/packages/connectivity/experimental_connectivity_web/test/lib/src/connectivity_mocks.dart new file mode 100644 index 000000000000..028bb5c8afc3 --- /dev/null +++ b/packages/connectivity/experimental_connectivity_web/test/lib/src/connectivity_mocks.dart @@ -0,0 +1,60 @@ +import 'dart:html'; + +import 'package:experimental_connectivity_web/src/generated/network_information_types.dart' + as dom; + +/// A Mock implementation of the NetworkInformation API that allows +/// for external modification of its values. +class MockNetworkInformation extends dom.NetworkInformation { + @override + String type; + + @override + String effectiveType; + + @override + num downlink; + + @override + num rtt; + + @override + EventListener onchange; + + /// Constructor of mocked instances... + MockNetworkInformation({ + this.type, + this.effectiveType, + this.downlink, + this.rtt, + }); + + /// Changes the desired values, and triggers the change event listener. + void mockChangeValue({ + String type, + String effectiveType, + num downlink, + num rtt, + }) { + this.type = type ?? this.type; + this.effectiveType = effectiveType ?? this.effectiveType; + this.downlink = downlink ?? this.downlink; + this.rtt = rtt ?? this.rtt; + + onchange(Event('change')); + } + + @override + void addEventListener(String type, listener, [bool useCapture]) {} + + @override + bool dispatchEvent(Event event) { + return true; + } + + @override + Events get on => null; + + @override + void removeEventListener(String type, listener, [bool useCapture]) {} +} diff --git a/packages/connectivity/experimental_connectivity_web/test/pubspec.yaml b/packages/connectivity/experimental_connectivity_web/test/pubspec.yaml new file mode 100644 index 000000000000..35bc9b5e083c --- /dev/null +++ b/packages/connectivity/experimental_connectivity_web/test/pubspec.yaml @@ -0,0 +1,24 @@ +name: connectivity_web_example +description: Example web app for the connectivity plugin +version: 0.1.0 +homepage: https://github.com/flutter/plugins/tree/master/packages/connectivity/connectivity_web + +dependencies: + experimental_connectivity_web: + path: ../ + js: ^0.6.1+1 + flutter_web_plugins: + sdk: flutter + flutter: + sdk: flutter + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_driver: + sdk: flutter + e2e: ^0.2.4+3 + +environment: + sdk: ">=2.6.0 <3.0.0" + flutter: ">=1.12.13+hotfix.4" diff --git a/packages/connectivity/experimental_connectivity_web/test/web/index.html b/packages/connectivity/experimental_connectivity_web/test/web/index.html new file mode 100644 index 000000000000..6eff9a740d43 --- /dev/null +++ b/packages/connectivity/experimental_connectivity_web/test/web/index.html @@ -0,0 +1,10 @@ + + + + + example + + + + + diff --git a/packages/connectivity/experimental_connectivity_web/ts/.gitignore b/packages/connectivity/experimental_connectivity_web/ts/.gitignore new file mode 100644 index 000000000000..de4d1f007dd1 --- /dev/null +++ b/packages/connectivity/experimental_connectivity_web/ts/.gitignore @@ -0,0 +1,2 @@ +dist +node_modules diff --git a/packages/connectivity/experimental_connectivity_web/ts/README.md b/packages/connectivity/experimental_connectivity_web/ts/README.md new file mode 100644 index 000000000000..3372ad2f3790 --- /dev/null +++ b/packages/connectivity/experimental_connectivity_web/ts/README.md @@ -0,0 +1,25 @@ +# JS Facade generator + +This npm script takes the `network-information-types` npm package, and runs it through Dart's `dart_js_facade_gen` to auto-generate (most) of the JS facades used by this plugin. + +The process is not completely automated yet, but it should be pretty close. + +To generate the facades, and after [installing `npm`](https://www.npmjs.com/get-npm), do: + +``` +npm install +npm run build +``` + +The above will fetch the required dependencies, and generate a `dist/network_information_types.dart` file that you can use with the plugin. + +``` +cp dist/*.dart ../lib/src/generated +``` + +This script should come handy once the Network Information Web API changes, or becomes stable, so the JS-interop part of this plugin can be regenerated more easily. + +Read more: + +* [Dart JS Interop](https://dart.dev/web/js-interop) +* [dart_js_facade_gen](https://www.npmjs.com/package/dart_js_facade_gen) \ No newline at end of file diff --git a/packages/connectivity/experimental_connectivity_web/ts/package-lock.json b/packages/connectivity/experimental_connectivity_web/ts/package-lock.json new file mode 100644 index 000000000000..45293a400492 --- /dev/null +++ b/packages/connectivity/experimental_connectivity_web/ts/package-lock.json @@ -0,0 +1,117 @@ +{ + "name": "network-information-types-to-dart-generator", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@types/chai": { + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.9.tgz", + "integrity": "sha512-NeXgZj+MFL4izGqA4sapdYzkzQG+MtGra9vhQ58dnmDY++VgJaRUws+aLVV5zRJCYJl/8s9IjMmhiUw1WsKSmw==" + }, + "@types/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-UoOfVEzAUpeSPmjm7h1uk5MH6KZma2z2O7a75onTGjnNvAvMVrPzPL/vBbT65iIGHWj6rokwfmYcmxmlSf2uwg==", + "requires": { + "@types/node": "*" + } + }, + "@types/minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-aaI6OtKcrwCX8G7aWbNh7i8GOfY=" + }, + "@types/mocha": { + "version": "5.2.7", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz", + "integrity": "sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==" + }, + "@types/node": { + "version": "12.12.28", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.28.tgz", + "integrity": "sha512-g73GJYJDXgf0jqg+P9S8h2acWbDXNkoCX8DLtJVu7Fkn788pzQ/oJsrdJz/2JejRf/SjfZaAhsw+3nd1D5EWGg==" + }, + "@types/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@types/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LrnsgZIfJaysFkv9rRJp4/uAyqw87oVed3s1hhF83nwbo9c7MG9g5DqR0seHP+lkX4ldmMrVolPjQSe2ZfD0yA==", + "requires": { + "source-map": "*" + } + }, + "@types/source-map-support": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@types/source-map-support/-/source-map-support-0.5.1.tgz", + "integrity": "sha512-VDqnZe9D2zR19qbeRvwYyHSp7AtUtCkTaRVFQ8wzwH9TXw9kKKq/vBhfEnFEXVupO2M0lBMA9mr/XyQ6gEkUOA==", + "requires": { + "@types/node": "*" + } + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + }, + "dart-style": { + "version": "1.3.2-dev", + "resolved": "https://registry.npmjs.org/dart-style/-/dart-style-1.3.2-dev.tgz", + "integrity": "sha512-NFI4UQYvG32t/cEkQAdkXT2ZT72tjF61tMWoALmnGwj03d2Co94zwGfbnFfdQUQvrhUNx8Wz2jKSVxGrmFaVJQ==" + }, + "dart_js_facade_gen": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/dart_js_facade_gen/-/dart_js_facade_gen-0.0.7.tgz", + "integrity": "sha512-AZiWsccbzhgJWmBjbFTPuvBhwGXk7AN8nOP91/I8PqUfSvVALiWshDc66TvywNkdNogAE5X8zlxjodw1C3iHpA==", + "requires": { + "@types/chai": "^4.2.3", + "@types/fs-extra": "^8.0.0", + "@types/minimist": "^1.2.0", + "@types/mocha": "^5.2.7", + "@types/node": "^12.7.8", + "@types/source-map": "^0.5.7", + "@types/source-map-support": "^0.5.0", + "dart-style": "^1.3.2-dev", + "minimist": "^1.2.0", + "source-map": "^0.7.3", + "source-map-support": "^0.5.13", + "typescript": "^3.6.3" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + }, + "network-information-types": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/network-information-types/-/network-information-types-0.1.0.tgz", + "integrity": "sha512-cRUCYZoRHTMjYcgk5MbwqM0h0Za34panRxAJKY8n+mQ+NLMuRIw7aKzmaZqkC/cte7bnRcdfTwFA27GgN62EtQ==" + }, + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==" + }, + "source-map-support": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz", + "integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==", + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "typescript": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.2.tgz", + "integrity": "sha512-EgOVgL/4xfVrCMbhYKUQTdF37SQn4Iw73H5BgCrF1Abdun7Kwy/QZsE/ssAy0y4LxBbvua3PIbFsbRczWWnDdQ==" + } + } +} diff --git a/packages/connectivity/experimental_connectivity_web/ts/package.json b/packages/connectivity/experimental_connectivity_web/ts/package.json new file mode 100644 index 000000000000..665c89d6afbb --- /dev/null +++ b/packages/connectivity/experimental_connectivity_web/ts/package.json @@ -0,0 +1,17 @@ +{ + "name": "network-information-types-to-dart-generator", + "version": "1.0.0", + "description": "Use dart_js_facade_gen to generate the facade for the network-information-types package.", + "main": "index.js", + "private": true, + "scripts": { + "build": "./scripts/run_facade_gen.sh", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "MIT", + "dependencies": { + "network-information-types": "0.1.0", + "dart_js_facade_gen": "^0.0.7" + } +} diff --git a/packages/connectivity/experimental_connectivity_web/ts/scripts/run_facade_gen.sh b/packages/connectivity/experimental_connectivity_web/ts/scripts/run_facade_gen.sh new file mode 100755 index 000000000000..c74b8ba171b2 --- /dev/null +++ b/packages/connectivity/experimental_connectivity_web/ts/scripts/run_facade_gen.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +INDEX_PATH=node_modules/network-information-types/dist-types/index.d.ts +WORK_PATH=network_information_types.d.ts +DIST_PATH=dist + +# Create dist if it doesn't exist already +mkdir -p $DIST_PATH + +# Copy the input file(s) into our work path +cp $INDEX_PATH $WORK_PATH + +# Run dart_js_facade_gen +dart_js_facade_gen $WORK_PATH --trust-js-types --generate-html --destination . + +# Move output to the right place, and clean after yourself +mv *.dart $DIST_PATH +rm $WORK_PATH