[connectivity_platform_interface] Move here as much code as possible from the core package (#2526)

* Bring ConnectivityResult and LocationAuthorizationStatus enums from
core package.
* Use the above Enums as return values for ConnectivityPlatformInterface
methods.
* Modify the MethodChannel implementation so it returns the right types.
* Bring utility methods, asserts and other logic that is only needed on
the MethodChannel implementation from the core package, so it's simpler.
* Bring MethodChannel unit tests from core package.

Co-authored-by: David Iglesias <ditman@gmail.com>
diff --git a/packages/connectivity/connectivity_platform_interface/CHANGELOG.md b/packages/connectivity/connectivity_platform_interface/CHANGELOG.md
index 8e4cc49..eb60d86 100644
--- a/packages/connectivity/connectivity_platform_interface/CHANGELOG.md
+++ b/packages/connectivity/connectivity_platform_interface/CHANGELOG.md
@@ -1,3 +1,11 @@
+## 1.0.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.
+
 ## 1.0.1
 
 * Fix README.md link.
diff --git a/packages/connectivity/connectivity_platform_interface/lib/connectivity_platform_interface.dart b/packages/connectivity/connectivity_platform_interface/lib/connectivity_platform_interface.dart
index 30884bb..cfd9cf6 100644
--- a/packages/connectivity/connectivity_platform_interface/lib/connectivity_platform_interface.dart
+++ b/packages/connectivity/connectivity_platform_interface/lib/connectivity_platform_interface.dart
@@ -6,7 +6,10 @@
 
 import 'package:plugin_platform_interface/plugin_platform_interface.dart';
 
-import 'method_channel_connectivity.dart';
+import 'src/enums.dart';
+import 'src/method_channel_connectivity.dart';
+
+export 'src/enums.dart';
 
 /// The interface that implementations of connectivity must implement.
 ///
@@ -36,10 +39,16 @@
   }
 
   /// Checks the connection status of the device.
-  Future<String> checkConnectivity() {
+  Future<ConnectivityResult> checkConnectivity() {
     throw UnimplementedError('checkConnectivity() has not been implemented.');
   }
 
+  /// Returns a Stream of ConnectivityResults changes.
+  Stream<ConnectivityResult> get onConnectivityChanged {
+    throw UnimplementedError(
+        'get onConnectivityChanged has not been implemented.');
+  }
+
   /// Obtains the wifi name (SSID) of the connected network
   Future<String> getWifiName() {
     throw UnimplementedError('getWifiName() has not been implemented.');
@@ -56,14 +65,14 @@
   }
 
   /// Request to authorize the location service (Only on iOS).
-  Future<String> requestLocationServiceAuthorization(
+  Future<LocationAuthorizationStatus> requestLocationServiceAuthorization(
       {bool requestAlwaysLocationUsage = false}) {
     throw UnimplementedError(
         'requestLocationServiceAuthorization() has not been implemented.');
   }
 
   /// Get the current location service authorization (Only on iOS).
-  Future<String> getLocationServiceAuthorization() {
+  Future<LocationAuthorizationStatus> getLocationServiceAuthorization() {
     throw UnimplementedError(
         'getLocationServiceAuthorization() has not been implemented.');
   }
diff --git a/packages/connectivity/connectivity_platform_interface/lib/method_channel_connectivity.dart b/packages/connectivity/connectivity_platform_interface/lib/method_channel_connectivity.dart
deleted file mode 100644
index 0d35b02..0000000
--- a/packages/connectivity/connectivity_platform_interface/lib/method_channel_connectivity.dart
+++ /dev/null
@@ -1,55 +0,0 @@
-// 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:flutter/services.dart';
-import 'package:meta/meta.dart';
-
-import 'connectivity_platform_interface.dart';
-
-/// The method channel used to interact with the native platform.
-@visibleForTesting
-const MethodChannel method_channel =
-    MethodChannel('plugins.flutter.io/connectivity');
-
-/// An implementation of [ConnectivityPlatform] that uses method channels.
-class MethodChannelConnectivity extends ConnectivityPlatform {
-  @override
-  Future<String> checkConnectivity() async {
-    final String result = await method_channel.invokeMethod<String>('check');
-    return result;
-  }
-
-  @override
-  Future<String> getWifiName() async {
-    return method_channel.invokeMethod<String>('wifiName');
-  }
-
-  @override
-  Future<String> getWifiBSSID() async {
-    return await method_channel.invokeMethod<String>('wifiBSSID');
-  }
-
-  @override
-  Future<String> getWifiIP() async {
-    return await method_channel.invokeMethod<String>('wifiIPAddress');
-  }
-
-  @override
-  Future<String> requestLocationServiceAuthorization(
-      {bool requestAlwaysLocationUsage = false}) async {
-    final String result = await method_channel.invokeMethod<String>(
-        'requestLocationServiceAuthorization',
-        <bool>[requestAlwaysLocationUsage]);
-    return result;
-  }
-
-  @override
-  Future<String> getLocationServiceAuthorization() async {
-    final String result = await method_channel
-        .invokeMethod<String>('getLocationServiceAuthorization');
-    return result;
-  }
-}
diff --git a/packages/connectivity/connectivity_platform_interface/lib/src/enums.dart b/packages/connectivity/connectivity_platform_interface/lib/src/enums.dart
new file mode 100644
index 0000000..9d8cef9
--- /dev/null
+++ b/packages/connectivity/connectivity_platform_interface/lib/src/enums.dart
@@ -0,0 +1,32 @@
+/// Connection status check result.
+enum ConnectivityResult {
+  /// WiFi: Device connected via Wi-Fi
+  wifi,
+
+  /// Mobile: Device connected to cellular network
+  mobile,
+
+  /// None: Device not connected to any network
+  none
+}
+
+/// The status of the location service authorization.
+enum LocationAuthorizationStatus {
+  /// The authorization of the location service is not determined.
+  notDetermined,
+
+  /// This app is not authorized to use location.
+  restricted,
+
+  /// User explicitly denied the location service.
+  denied,
+
+  /// User authorized the app to access the location at any time.
+  authorizedAlways,
+
+  /// User authorized the app to access the location when the app is visible to them.
+  authorizedWhenInUse,
+
+  /// Status unknown.
+  unknown
+}
diff --git a/packages/connectivity/connectivity_platform_interface/lib/src/method_channel_connectivity.dart b/packages/connectivity/connectivity_platform_interface/lib/src/method_channel_connectivity.dart
new file mode 100644
index 0000000..7a64115
--- /dev/null
+++ b/packages/connectivity/connectivity_platform_interface/lib/src/method_channel_connectivity.dart
@@ -0,0 +1,88 @@
+// 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 'dart:io' show Platform;
+
+import 'package:connectivity_platform_interface/connectivity_platform_interface.dart';
+import 'package:flutter/services.dart';
+import 'package:meta/meta.dart';
+
+import '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 =
+      MethodChannel('plugins.flutter.io/connectivity');
+
+  /// The event channel used to receive ConnectivityResult changes from the native platform.
+  @visibleForTesting
+  EventChannel eventChannel =
+      EventChannel('plugins.flutter.io/connectivity_status');
+
+  Stream<ConnectivityResult> _onConnectivityChanged;
+
+  /// Fires whenever the connectivity state changes.
+  Stream<ConnectivityResult> get onConnectivityChanged {
+    if (_onConnectivityChanged == null) {
+      _onConnectivityChanged = eventChannel
+          .receiveBroadcastStream()
+          .map((dynamic result) => result.toString())
+          .map(parseConnectivityResult);
+    }
+    return _onConnectivityChanged;
+  }
+
+  @override
+  Future<ConnectivityResult> checkConnectivity() {
+    return methodChannel
+        .invokeMethod<String>('check')
+        .then(parseConnectivityResult);
+  }
+
+  @override
+  Future<String> getWifiName() async {
+    String wifiName = await methodChannel.invokeMethod<String>('wifiName');
+    // as Android might return <unknown ssid>, uniforming result
+    // our iOS implementation will return null
+    if (wifiName == '<unknown ssid>') {
+      wifiName = null;
+    }
+    return wifiName;
+  }
+
+  @override
+  Future<String> getWifiBSSID() {
+    return methodChannel.invokeMethod<String>('wifiBSSID');
+  }
+
+  @override
+  Future<String> getWifiIP() {
+    return methodChannel.invokeMethod<String>('wifiIPAddress');
+  }
+
+  @override
+  Future<LocationAuthorizationStatus> requestLocationServiceAuthorization({
+    bool requestAlwaysLocationUsage = false,
+  }) {
+    // `assert(Platform.isIOS)` will prevent us from doing dart side unit testing.
+    // TODO: These should noop for non-Android, instead of throwing, so people don't need to rely on dart:io for this.
+    assert(!Platform.isAndroid);
+    return methodChannel.invokeMethod<String>(
+        'requestLocationServiceAuthorization', <bool>[
+      requestAlwaysLocationUsage
+    ]).then(parseLocationAuthorizationStatus);
+  }
+
+  @override
+  Future<LocationAuthorizationStatus> getLocationServiceAuthorization() {
+    // `assert(Platform.isIOS)` will prevent us from doing dart side unit testing.
+    assert(!Platform.isAndroid);
+    return methodChannel
+        .invokeMethod<String>('getLocationServiceAuthorization')
+        .then(parseLocationAuthorizationStatus);
+  }
+}
diff --git a/packages/connectivity/connectivity_platform_interface/lib/src/utils.dart b/packages/connectivity/connectivity_platform_interface/lib/src/utils.dart
new file mode 100644
index 0000000..2ae22e1
--- /dev/null
+++ b/packages/connectivity/connectivity_platform_interface/lib/src/utils.dart
@@ -0,0 +1,32 @@
+import 'package:connectivity_platform_interface/connectivity_platform_interface.dart';
+
+/// Convert a String to a ConnectivityResult value.
+ConnectivityResult parseConnectivityResult(String state) {
+  switch (state) {
+    case 'wifi':
+      return ConnectivityResult.wifi;
+    case 'mobile':
+      return ConnectivityResult.mobile;
+    case 'none':
+    default:
+      return ConnectivityResult.none;
+  }
+}
+
+/// Convert a String to a LocationAuthorizationStatus value.
+LocationAuthorizationStatus parseLocationAuthorizationStatus(String result) {
+  switch (result) {
+    case 'notDetermined':
+      return LocationAuthorizationStatus.notDetermined;
+    case 'restricted':
+      return LocationAuthorizationStatus.restricted;
+    case 'denied':
+      return LocationAuthorizationStatus.denied;
+    case 'authorizedAlways':
+      return LocationAuthorizationStatus.authorizedAlways;
+    case 'authorizedWhenInUse':
+      return LocationAuthorizationStatus.authorizedWhenInUse;
+    default:
+      return LocationAuthorizationStatus.unknown;
+  }
+}
diff --git a/packages/connectivity/connectivity_platform_interface/pubspec.yaml b/packages/connectivity/connectivity_platform_interface/pubspec.yaml
index cccf14e..14fa04e 100644
--- a/packages/connectivity/connectivity_platform_interface/pubspec.yaml
+++ b/packages/connectivity/connectivity_platform_interface/pubspec.yaml
@@ -3,7 +3,7 @@
 homepage: https://github.com/flutter/plugins/tree/master/packages/connectivity/connectivity_platform_interface
 # NOTE: We strongly prefer non-breaking changes, even at the expense of a
 # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes
-version: 1.0.1
+version: 1.0.2
 
 dependencies:
   flutter:
@@ -14,7 +14,6 @@
 dev_dependencies:
   flutter_test:
     sdk: flutter
-  mockito: ^4.1.1
 
 environment:
   sdk: ">=2.0.0-dev.28.0 <3.0.0"
diff --git a/packages/connectivity/connectivity_platform_interface/test/method_channel_connectivity_test.dart b/packages/connectivity/connectivity_platform_interface/test/method_channel_connectivity_test.dart
index d8d9d5b..3d9c405 100644
--- a/packages/connectivity/connectivity_platform_interface/test/method_channel_connectivity_test.dart
+++ b/packages/connectivity/connectivity_platform_interface/test/method_channel_connectivity_test.dart
@@ -2,129 +2,152 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'package:mockito/mockito.dart';
+import 'package:connectivity_platform_interface/connectivity_platform_interface.dart';
 import 'package:flutter/services.dart';
 import 'package:flutter_test/flutter_test.dart';
-import 'package:plugin_platform_interface/plugin_platform_interface.dart';
-
-import 'package:connectivity_platform_interface/method_channel_connectivity.dart';
-import 'package:connectivity_platform_interface/connectivity_platform_interface.dart';
+import 'package:connectivity_platform_interface/src/method_channel_connectivity.dart';
 
 void main() {
   TestWidgetsFlutterBinding.ensureInitialized();
 
-  group('$ConnectivityPlatform', () {
-    test('$MethodChannelConnectivity() is the default instance', () {
-      expect(ConnectivityPlatform.instance,
-          isInstanceOf<MethodChannelConnectivity>());
-    });
-
-    test('Cannot be implemented with `implements`', () {
-      expect(() {
-        ConnectivityPlatform.instance = ImplementsConnectivityPlatform();
-      }, throwsA(isInstanceOf<AssertionError>()));
-    });
-
-    test('Can be mocked with `implements`', () {
-      final ConnectivityPlatformMock mock = ConnectivityPlatformMock();
-      ConnectivityPlatform.instance = mock;
-    });
-
-    test('Can be extended', () {
-      ConnectivityPlatform.instance = ExtendsConnectivityPlatform();
-    });
-  });
-
   group('$MethodChannelConnectivity', () {
-    const MethodChannel channel =
-        MethodChannel('plugins.flutter.io/connectivity');
     final List<MethodCall> log = <MethodCall>[];
-    channel.setMockMethodCallHandler((MethodCall methodCall) async {
-      log.add(methodCall);
-    });
+    MethodChannelConnectivity methodChannelConnectivity;
 
-    final MethodChannelConnectivity connectivity = MethodChannelConnectivity();
+    setUp(() async {
+      methodChannelConnectivity = MethodChannelConnectivity();
 
-    tearDown(() {
+      methodChannelConnectivity.methodChannel
+          .setMockMethodCallHandler((MethodCall methodCall) async {
+        log.add(methodCall);
+        switch (methodCall.method) {
+          case 'check':
+            return 'wifi';
+          case 'wifiName':
+            return '1337wifi';
+          case 'wifiBSSID':
+            return 'c0:ff:33:c0:d3:55';
+          case 'wifiIPAddress':
+            return '127.0.0.1';
+          case 'requestLocationServiceAuthorization':
+            return 'authorizedAlways';
+          case 'getLocationServiceAuthorization':
+            return 'authorizedAlways';
+          default:
+            return null;
+        }
+      });
       log.clear();
+      MethodChannel(methodChannelConnectivity.eventChannel.name)
+          .setMockMethodCallHandler((MethodCall methodCall) async {
+        switch (methodCall.method) {
+          case 'listen':
+            await ServicesBinding.instance.defaultBinaryMessenger
+                .handlePlatformMessage(
+              methodChannelConnectivity.eventChannel.name,
+              methodChannelConnectivity.eventChannel.codec
+                  .encodeSuccessEnvelope('wifi'),
+              (_) {},
+            );
+            break;
+          case 'cancel':
+          default:
+            return null;
+        }
+      });
     });
 
-    test('checkConnectivity', () async {
-      await connectivity.checkConnectivity();
-      expect(
-        log,
-        <Matcher>[isMethodCall('check', arguments: null)],
-      );
+    test('onConnectivityChanged', () async {
+      final ConnectivityResult result =
+          await methodChannelConnectivity.onConnectivityChanged.first;
+      expect(result, ConnectivityResult.wifi);
     });
 
     test('getWifiName', () async {
-      await connectivity.getWifiName();
-      expect(
-        log,
-        <Matcher>[isMethodCall('wifiName', arguments: null)],
-      );
-    });
-
-    test('getWifiBSSID', () async {
-      await connectivity.getWifiBSSID();
-      expect(
-        log,
-        <Matcher>[isMethodCall('wifiBSSID', arguments: null)],
-      );
-    });
-
-    test('getWifiIP', () async {
-      await connectivity.getWifiIP();
-      expect(
-        log,
-        <Matcher>[isMethodCall('wifiIPAddress', arguments: null)],
-      );
-    });
-
-    test(
-        'requestLocationServiceAuthorization requestLocationServiceAuthorization set to false (default)',
-        () async {
-      await connectivity.requestLocationServiceAuthorization();
+      final String result = await methodChannelConnectivity.getWifiName();
+      expect(result, '1337wifi');
       expect(
         log,
         <Matcher>[
-          isMethodCall('requestLocationServiceAuthorization',
-              arguments: <bool>[false])
+          isMethodCall(
+            'wifiName',
+            arguments: null,
+          ),
         ],
       );
     });
 
-    test(
-        'requestLocationServiceAuthorization requestLocationServiceAuthorization set to true',
-        () async {
-      await connectivity.requestLocationServiceAuthorization(
-          requestAlwaysLocationUsage: true);
+    test('getWifiBSSID', () async {
+      final String result = await methodChannelConnectivity.getWifiBSSID();
+      expect(result, 'c0:ff:33:c0:d3:55');
       expect(
         log,
         <Matcher>[
-          isMethodCall('requestLocationServiceAuthorization',
-              arguments: <bool>[true])
+          isMethodCall(
+            'wifiBSSID',
+            arguments: null,
+          ),
+        ],
+      );
+    });
+
+    test('getWifiIP', () async {
+      final String result = await methodChannelConnectivity.getWifiIP();
+      expect(result, '127.0.0.1');
+      expect(
+        log,
+        <Matcher>[
+          isMethodCall(
+            'wifiIPAddress',
+            arguments: null,
+          ),
+        ],
+      );
+    });
+
+    test('requestLocationServiceAuthorization', () async {
+      final LocationAuthorizationStatus result =
+          await methodChannelConnectivity.requestLocationServiceAuthorization();
+      expect(result, LocationAuthorizationStatus.authorizedAlways);
+      expect(
+        log,
+        <Matcher>[
+          isMethodCall(
+            'requestLocationServiceAuthorization',
+            arguments: <bool>[false],
+          ),
         ],
       );
     });
 
     test('getLocationServiceAuthorization', () async {
-      await connectivity.getLocationServiceAuthorization();
+      final LocationAuthorizationStatus result =
+          await methodChannelConnectivity.getLocationServiceAuthorization();
+      expect(result, LocationAuthorizationStatus.authorizedAlways);
       expect(
         log,
         <Matcher>[
-          isMethodCall('getLocationServiceAuthorization', arguments: null)
+          isMethodCall(
+            'getLocationServiceAuthorization',
+            arguments: null,
+          ),
+        ],
+      );
+    });
+
+    test('checkConnectivity', () async {
+      final ConnectivityResult result =
+          await methodChannelConnectivity.checkConnectivity();
+      expect(result, ConnectivityResult.wifi);
+      expect(
+        log,
+        <Matcher>[
+          isMethodCall(
+            'check',
+            arguments: null,
+          ),
         ],
       );
     });
   });
 }
-
-class ConnectivityPlatformMock extends Mock
-    with MockPlatformInterfaceMixin
-    implements ConnectivityPlatform {}
-
-class ImplementsConnectivityPlatform extends Mock
-    implements ConnectivityPlatform {}
-
-class ExtendsConnectivityPlatform extends ConnectivityPlatform {}