| // 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 'dart:io'; |
| |
| import 'package:flutter/services.dart'; |
| import 'package:meta/meta.dart'; |
| |
| /// 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 |
| } |
| |
| /// 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 |
| // misusage of creating a second instance from a programmer. |
| factory Connectivity() { |
| if (_singleton == null) { |
| _singleton = Connectivity._(); |
| } |
| return _singleton; |
| } |
| |
| Connectivity._(); |
| |
| static Connectivity _singleton; |
| |
| Stream<ConnectivityResult> _onConnectivityChanged; |
| |
| /// Exposed for testing purposes and should not be used by users of the plugin. |
| @visibleForTesting |
| static const MethodChannel methodChannel = MethodChannel( |
| 'plugins.flutter.io/connectivity', |
| ); |
| |
| /// Exposed for testing purposes and should not be used by users of the plugin. |
| @visibleForTesting |
| static const EventChannel eventChannel = EventChannel( |
| 'plugins.flutter.io/connectivity_status', |
| ); |
| |
| /// Fires whenever the connectivity state changes. |
| Stream<ConnectivityResult> get onConnectivityChanged { |
| if (_onConnectivityChanged == null) { |
| _onConnectivityChanged = eventChannel |
| .receiveBroadcastStream() |
| .map((dynamic event) => _parseConnectivityResult(event)); |
| } |
| return _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<ConnectivityResult> checkConnectivity() async { |
| final String result = await methodChannel.invokeMethod<String>('check'); |
| return _parseConnectivityResult(result); |
| } |
| |
| /// Obtains the wifi name (SSID) of the connected network |
| /// |
| /// Please note that it DOESN'T WORK on emulators (returns null). |
| /// |
| /// From android 8.0 onwards the GPS must be ON (high accuracy) |
| /// in order to be able to obtain the SSID. |
| 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; |
| } |
| |
| /// Obtains the wifi BSSID of the connected network. |
| /// |
| /// Please note that it DOESN'T WORK on emulators (returns null). |
| /// |
| /// From Android 8.0 onwards the GPS must be ON (high accuracy) |
| /// in order to be able to obtain the BSSID. |
| Future<String> getWifiBSSID() async { |
| return await methodChannel.invokeMethod<String>('wifiBSSID'); |
| } |
| |
| /// Obtains the IP address of the connected wifi network |
| Future<String> getWifiIP() async { |
| return await methodChannel.invokeMethod<String>('wifiIPAddress'); |
| } |
| |
| /// Request to authorize the location service (Only on iOS). |
| /// |
| /// This method will throw a [PlatformException] on Android. |
| /// |
| /// Returns a [LocationAuthorizationStatus] after user authorized or denied the location on this request. |
| /// |
| /// If the location information needs to be accessible all the time, set `requestAlwaysLocationUsage` to true. If user has |
| /// already granted a [LocationAuthorizationStatus.authorizedWhenInUse] prior to requesting an "always" access, it will return [LocationAuthorizationStatus.denied]. |
| /// |
| /// If the location service authorization is not determined prior to making this call, a platform standard UI of requesting a location service will pop up. |
| /// This UI will only show once unless the user re-install the app to their phone which resets the location service authorization to not determined. |
| /// |
| /// This method is a helper to get the location authorization that is necessary for certain functionality of this plugin. |
| /// It can be replaced with other permission handling code/plugin if preferred. |
| /// To request location authorization, make sure to add the following keys to your _Info.plist_ file, located in `<project root>/ios/Runner/Info.plist`: |
| /// * `NSLocationAlwaysAndWhenInUseUsageDescription` - describe why the app needs access to the user’s location information |
| /// all the time (foreground and background). This is called _Privacy - Location Always and When In Use Usage Description_ in the visual editor. |
| /// * `NSLocationWhenInUseUsageDescription` - describe why the app needs access to the user’s location information when the app is |
| /// running in the foreground. This is called _Privacy - Location When In Use Usage Description_ in the visual editor. |
| /// |
| /// Starting from iOS 13, `getWifiBSSID` and `getWifiIP` will only work properly if: |
| /// |
| /// * The app uses Core Location, and has the user’s authorization to use location information. |
| /// * The app uses the NEHotspotConfiguration API to configure the current Wi-Fi network. |
| /// * The app has active VPN configurations installed. |
| /// |
| /// If the app falls into the first category, call this method before calling `getWifiBSSID` or `getWifiIP`. |
| /// For example, |
| /// ```dart |
| /// if (Platform.isIOS) { |
| /// LocationAuthorizationStatus status = await _connectivity.getLocationServiceAuthorization(); |
| /// if (status == LocationAuthorizationStatus.notDetermined) { |
| /// status = await _connectivity.requestLocationServiceAuthorization(); |
| /// } |
| /// if (status == LocationAuthorizationStatus.authorizedAlways || status == LocationAuthorizationStatus.authorizedWhenInUse) { |
| /// wifiBSSID = await _connectivity.getWifiName(); |
| /// } else { |
| /// print('location service is not authorized, the data might not be correct'); |
| /// wifiBSSID = await _connectivity.getWifiName(); |
| /// } |
| /// } else { |
| /// wifiBSSID = await _connectivity.getWifiName(); |
| /// } |
| /// ``` |
| /// |
| /// Ideally, a location service authorization should only be requested if the current authorization status is not determined. |
| /// |
| /// See also [getLocationServiceAuthorization] to obtain current location service status. |
| Future<LocationAuthorizationStatus> requestLocationServiceAuthorization( |
| {bool requestAlwaysLocationUsage = false}) async { |
| //Just `assert(Platform.isIOS)` will prevent us from doing dart side unit testing. |
| assert(!Platform.isAndroid); |
| final String result = await methodChannel.invokeMethod<String>( |
| 'requestLocationServiceAuthorization', |
| <bool>[requestAlwaysLocationUsage]); |
| return _convertLocationStatusString(result); |
| } |
| |
| /// Get the current location service authorization (Only on iOS). |
| /// |
| /// This method will throw a [PlatformException] on Android. |
| /// |
| /// Returns a [LocationAuthorizationStatus]. |
| /// If the returned value is [LocationAuthorizationStatus.notDetermined], a subsequent [requestLocationServiceAuthorization] call |
| /// can request the authorization. |
| /// If the returned value is not [LocationAuthorizationStatus.notDetermined], a subsequent [requestLocationServiceAuthorization] |
| /// will not initiate another request. It will instead return the "determined" status. |
| /// |
| /// This method is a helper to get the location authorization that is necessary for certain functionality of this plugin. |
| /// It can be replaced with other permission handling code/plugin if preferred. |
| /// |
| /// Starting from iOS 13, `getWifiBSSID` and `getWifiIP` will only work properly if: |
| /// |
| /// * The app uses Core Location, and has the user’s authorization to use location information. |
| /// * The app uses the NEHotspotConfiguration API to configure the current Wi-Fi network. |
| /// * The app has active VPN configurations installed. |
| /// |
| /// If the app falls into the first category, call this method before calling `getWifiBSSID` or `getWifiIP`. |
| /// For example, |
| /// ```dart |
| /// if (Platform.isIOS) { |
| /// LocationAuthorizationStatus status = await _connectivity.getLocationServiceAuthorization(); |
| /// if (status == LocationAuthorizationStatus.authorizedAlways || status == LocationAuthorizationStatus.authorizedWhenInUse) { |
| /// wifiBSSID = await _connectivity.getWifiName(); |
| /// } else { |
| /// print('location service is not authorized, the data might not be correct'); |
| /// wifiBSSID = await _connectivity.getWifiName(); |
| /// } |
| /// } else { |
| /// wifiBSSID = await _connectivity.getWifiName(); |
| /// } |
| /// ``` |
| /// |
| /// See also [requestLocationServiceAuthorization] for requesting a location service authorization. |
| Future<LocationAuthorizationStatus> getLocationServiceAuthorization() async { |
| //Just `assert(Platform.isIOS)` will prevent us from doing dart side unit testing. |
| assert(!Platform.isAndroid); |
| final String result = await methodChannel |
| .invokeMethod<String>('getLocationServiceAuthorization'); |
| return _convertLocationStatusString(result); |
| } |
| |
| LocationAuthorizationStatus _convertLocationStatusString(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; |
| } |
| } |
| } |
| |
| ConnectivityResult _parseConnectivityResult(String state) { |
| switch (state) { |
| case 'wifi': |
| return ConnectivityResult.wifi; |
| case 'mobile': |
| return ConnectivityResult.mobile; |
| case 'none': |
| default: |
| return ConnectivityResult.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 |
| } |