text: /**
   * Copyright (c) 2015-present, Facebook, Inc.
   * All rights reserved.
   *
   * This source code is licensed under the BSD-style license found in the
   * LICENSE file in the root directory of this source tree. An additional grant
   * of patent rights can be found in the PATENTS file in the same directory.
   *
   * @providesModule NetInfo
   * @flow
   */
  'use strict';
  
  var Map = require('Map');
  var NativeModules = require('NativeModules');
  var Platform = require('Platform');
  var RCTDeviceEventEmitter = require('RCTDeviceEventEmitter');
  var RCTNetInfo = NativeModules.NetInfo;
  
  var DEVICE_REACHABILITY_EVENT = 'networkDidChange';
  
  type ChangeEventName = $Enum<{
    change: string;
  }>;
  
  type ReachabilityStateIOS = $Enum<{
    cell: string;
    none: string;
    unknown: string;
    wifi: string;
  }>;
  
  type ConnectivityStateAndroid = $Enum<{
    NONE: string;
    MOBILE: string;
    WIFI: string;
    MOBILE_MMS: string;
    MOBILE_SUPL: string;
    MOBILE_DUN: string;
    MOBILE_HIPRI: string;
    WIMAX: string;
    BLUETOOTH: string;
    DUMMY: string;
    ETHERNET: string;
    MOBILE_FOTA: string;
    MOBILE_IMS: string;
    MOBILE_CBS: string;
    WIFI_P2P: string;
    MOBILE_IA: string;
    MOBILE_EMERGENCY: string;
    PROXY: string;
    VPN: string;
    UNKNOWN: string;
  }>;
  
  /**
   * NetInfo exposes info about online/offline status
   *
   * ```
   * NetInfo.fetch().done((reach) => {
   *   console.log('Initial: ' + reach);
   * });
   * function handleFirstConnectivityChange(reach) {
   *   console.log('First change: ' + reach);
   *   NetInfo.removeEventListener(
   *     'change',
   *     handleFirstConnectivityChange
   *   );
   * }
   * NetInfo.addEventListener(
   *   'change',
   *   handleFirstConnectivityChange
   * );
   * ```
   *
   * ### IOS
   *
   * Asynchronously determine if the device is online and on a cellular network.
   *
   * - `none` - device is offline
   * - `wifi` - device is online and connected via wifi, or is the iOS simulator
   * - `cell` - device is connected via Edge, 3G, WiMax, or LTE
   * - `unknown` - error case and the network status is unknown
   *
   * ### Android
   *
   * Asynchronously determine if the device is connected and details about that connection.
   *
   * Android Connectivity Types
   * - `NONE` - device is offline
   * - `BLUETOOTH` - The Bluetooth data connection.
   * - `DUMMY` -  Dummy data connection.
   * - `ETHERNET` - The Ethernet data connection.
   * - `MOBILE` - The Mobile data connection.
   * - `MOBILE_DUN` - A DUN-specific Mobile data connection.
   * - `MOBILE_HIPRI` - A High Priority Mobile data connection.
   * - `MOBILE_MMS` - An MMS-specific Mobile data connection.
   * - `MOBILE_SUPL` -  A SUPL-specific Mobile data connection.
   * - `VPN` -  A virtual network using one or more native bearers. Requires API Level 21
   * - `WIFI` - The WIFI data connection.
   * - `WIMAX` -  The WiMAX data connection.
   * - `UNKNOWN` - Unknown data connection.
   * The rest ConnectivityStates are hidden by the Android API, but can be used if necessary.
   *
   * ### isConnectionMetered
   *
   * Available on Android. Detect if the current active connection is metered or not. A network is
   * classified as metered when the user is sensitive to heavy data usage on that connection due to
   * monetary costs, data limitations or battery/performance issues.
   *
   * NetInfo.isConnectionMetered((isConnectionMetered) => {
   *   console.log('Connection is ' + (isConnectionMetered ? 'Metered' : 'Not Metered'));
   * });
   * ```
   *
   * ### isConnected
   *
   * Available on all platforms. Asynchronously fetch a boolean to determine
   * internet connectivity.
   *
   * ```
   * NetInfo.isConnected.fetch().done((isConnected) => {
   *   console.log('First, is ' + (isConnected ? 'online' : 'offline'));
   * });
   * function handleFirstConnectivityChange(isConnected) {
   *   console.log('Then, is ' + (isConnected ? 'online' : 'offline'));
   *   NetInfo.isConnected.removeEventListener(
   *     'change',
   *     handleFirstConnectivityChange
   *   );
   * }
   * NetInfo.isConnected.addEventListener(
   *   'change',
   *   handleFirstConnectivityChange
   * );
   * ```
   */
  
  var _subscriptions = new Map();
  
  var NetInfo = {
    addEventListener: function (
      eventName: ChangeEventName,
      handler: Function
    ): void {
      var listener = RCTDeviceEventEmitter.addListener(
        DEVICE_REACHABILITY_EVENT,
        (appStateData) => {
          handler(appStateData.network_info);
        }
      );
      _subscriptions.set(handler, listener);
    },
  
    removeEventListener: function(
      eventName: ChangeEventName,
      handler: Function
    ): void {
      var listener = _subscriptions.get(handler);
      if (!listener) {
        return;
      }
      listener.remove();
      _subscriptions.delete(handler);
    },
  
    fetch: function(): Promise {
      return new Promise((resolve, reject) => {
        RCTNetInfo.getCurrentReachability(
          function(resp) {
            resolve(resp.network_reachability);
          },
          reject
        );
      });
    },
  
    isConnected: {},
  
    isConnectionMetered: {},
  };
  
  if (Platform.OS === 'ios') {
    var _isConnected = function(
      reachability: ReachabilityStateIOS
    ): bool {
      return reachability !== 'none' &&
        reachability !== 'unknown';
    };
  } else if (Platform.OS === 'android') {
    var _isConnected = function(
        connectionType: ConnectivityStateAndroid
      ): bool {
      return connectionType !== 'NONE' && connectionType !== 'UNKNOWN';
    };
  }
  
  var _isConnectedSubscriptions = new Map();
  
  NetInfo.isConnected = {
    addEventListener: function (
      eventName: ChangeEventName,
      handler: Function
    ): void {
      var listener = (connection) => {
        handler(_isConnected(connection));
      };
      _isConnectedSubscriptions.set(handler, listener);
      NetInfo.addEventListener(
        eventName,
        listener
      );
    },
  
    removeEventListener: function(
      eventName: ChangeEventName,
      handler: Function
    ): void {
      var listener = _isConnectedSubscriptions.get(handler);
      NetInfo.removeEventListener(
        eventName,
        listener
      );
      _isConnectedSubscriptions.delete(handler);
    },
  
    fetch: function(): Promise {
      return NetInfo.fetch().then(
        (connection) => _isConnected(connection)
      );
    },
  };
  
  if (Platform.OS === 'android') {
    NetInfo.isConnectionMetered = function(callback): void {
      RCTNetInfo.isConnectionMetered((_isMetered) => {
        callback(_isMetered);
      });
    };
  }
  
  module.exports = NetInfo;
  