DEPRECATION: Make NetInfo API cross platform and expose whether connection is 2g/3g/4g

Summary:
This change intends to fix 2 issues with the NetInfo API:
  - The NetInfo API is currently platform-specific. It returns completely different values on iOS and Android.
  - The NetInfo API currently doesn't expose a way to determine whether the connection is 2g, 3g, or 4g.

The NetInfo API currently just exposes a string-based enum representing the connectivity type. The string values are different between iOS and Andorid. Because of this design, it's not obvious how to achieve the goals of this change without making a breaking change. Consequently, this change deprecates the old NetInfo APIs and introduces new ones. Specifically, these are the API changes:
  - The `fetch` method is deprecated in favor of `getConnection`
  - The `change` event is deprecated in favor of the `connectionchange` event.
  - `getConnection`/`connectionchange` use a new set of enum values compared to `fetch`/`change`. See the documentation for the new values.
    - On iOS, `cell` is now known as `cellular`. It's worth pointing out this one in particular because the old and new names are so similar. The rest of the iOS values have remained the same.
    - Some of the Android enum values have been removed without a replacement (e.g. `DUMMY`, `MOBILE_DUN`, `MOBILE_HIPRI`, `MOBILE_MMS`, `MOBILE_SUPL`, `VPN`). If desirable, we could find a way to expose these in the new API. For example, we could have a `platformValue` key that exposes the platform's enum values directly (like the old `fetch` API did).

`getConnection` and `connectionchange` each expose an object which has 2 keys conveying a `ConnectionType` (e.g. wifi, cellular) and an `EffectiveConnectionType` (e.g. 2g, 3g). These enums and their values are taken directly from the W3C's Network Information API spec (https://wicg.github.io/netinfo/). Copying the W3C's API will make it easy to expose a `navigation.connection` polyfill, if we want, in the future. Additionally, because the new APIs expose an object instead of a string, it's easier to extend the APIs in the future by adding keys to the object without causing a breaking change.

Note that the W3C's spec doesn't have an "unknown" value for `EffectiveConnectionType`. I chose to introduce this non-standard value because it's possible for the current implementation to not have an `effectiveConnectionType` and I figured it was worth representing this possibility explicitly with "unknown" instead of implicitly with `null`.

**Test Plan (required)**

Verified that the methods (`fetch` and `getConnection`) and the events (`change` and `connectionchange`) return the correct data on iOS and Android when connected to a wifi network and a 4G cellular network. Verified that switching networks causes the event to fire with the correct information. Verified that the old APIs (`fetch' and 'change') emit a deprecation warning when used. My team is using a similar patch in our app.

Adam Comella
Microsoft Corp.
Closes https://github.com/facebook/react-native/pull/14618

Differential Revision: D5459593

Pulled By: shergin

fbshipit-source-id: f1e6c5d572bb3e2669fbd4ba7d0fbb106525280e
This commit is contained in:
Adam Comella 2017-07-24 15:42:27 -07:00 коммит произвёл Facebook Github Bot
Родитель 9d54a10cf8
Коммит fc38fe1736
3 изменённых файлов: 269 добавлений и 59 удалений

Просмотреть файл

@ -22,6 +22,7 @@ const NetInfoEventEmitter = new NativeEventEmitter(RCTNetInfo);
const DEVICE_CONNECTIVITY_EVENT = 'networkStatusDidChange'; const DEVICE_CONNECTIVITY_EVENT = 'networkStatusDidChange';
type ChangeEventName = $Enum<{ type ChangeEventName = $Enum<{
connectionChange: string,
change: string, change: string,
}>; }>;
@ -79,56 +80,51 @@ const _isConnectedSubscriptions = new Map();
* NetInfo exposes info about online/offline status * NetInfo exposes info about online/offline status
* *
* ``` * ```
* NetInfo.fetch().then((reach) => { * NetInfo.getConnectionInfo().then((connectionInfo) => {
* console.log('Initial: ' + reach); * console.log('Initial, type: ' + connectionInfo.type + ', effectiveType: ' + connectionInfo.effectiveType);
* }); * });
* function handleFirstConnectivityChange(reach) { * function handleFirstConnectivityChange(connectionInfo) {
* console.log('First change: ' + reach); * console.log('First change, type: ' + connectionInfo.type + ', effectiveType: ' + connectionInfo.effectiveType);
* NetInfo.removeEventListener( * NetInfo.removeEventListener(
* 'change', * 'connectionChange',
* handleFirstConnectivityChange * handleFirstConnectivityChange
* ); * );
* } * }
* NetInfo.addEventListener( * NetInfo.addEventListener(
* 'change', * 'connectionChange',
* handleFirstConnectivityChange * handleFirstConnectivityChange
* ); * );
* ``` * ```
* *
* ### IOS * ### ConnectionType enum
* *
* Asynchronously determine if the device is online and on a cellular network. * `ConnectionType` describes the type of connection the device is using to communicate with the network.
* *
* Cross platform values for `ConnectionType`:
* - `none` - device is offline * - `none` - device is offline
* - `wifi` - device is online and connected via wifi, or is the iOS simulator * - `wifi` - device is online and connected via wifi, or is the iOS simulator
* - `cell` - device is connected via Edge, 3G, WiMax, or LTE * - `cellular` - device is connected via Edge, 3G, WiMax, or LTE
* - `unknown` - error case and the network status is unknown * - `unknown` - error case and the network status is unknown
* *
* Android-only values for `ConnectionType`:
* - `bluetooth` - device is connected via Bluetooth
* - `ethernet` - device is connected via Ethernet
* - `wimax` - device is connected via WiMAX
*
* ### EffectiveConnectionType enum
*
* Cross platform values for `EffectiveConnectionType`:
* - `2g`
* - `3g`
* - `4g`
* - `unknown`
*
* ### Android * ### Android
* *
* To request network info, you need to add the following line to your * To request network info, you need to add the following line to your
* app's `AndroidManifest.xml`: * app's `AndroidManifest.xml`:
* *
* `<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />` * `<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />`
* 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.
* *
* ### isConnectionExpensive * ### isConnectionExpensive
* *
@ -167,22 +163,77 @@ const _isConnectedSubscriptions = new Map();
* handleFirstConnectivityChange * handleFirstConnectivityChange
* ); * );
* ``` * ```
*
* ### Connectivity Types (deprecated)
*
* The following connectivity types are deprecated. They're used by the deprecated APIs `fetch` and the `change` event.
*
* iOS connectivity types (deprecated):
* - `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 connectivity types (deprecated).
* - `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 of the connectivity types are hidden by the Android API, but can be used if necessary.
*/ */
const NetInfo = { const NetInfo = {
/** /**
* Invokes the listener whenever network status changes. * Adds an event handler. Supported events:
* The listener receives one of the connectivity types listed above. *
* - `connectionChange`: Fires when the network status changes. The argument to the event
* handler is an object with keys:
* - `type`: A `ConnectionType` (listed above)
* - `effectiveType`: An `EffectiveConnectionType` (listed above)
* - `change`: This event is deprecated. Listen to `connectionChange` instead. Fires when
* the network status changes. The argument to the event handler is one of the deprecated
* connectivity types listed above.
*/ */
addEventListener( addEventListener(
eventName: ChangeEventName, eventName: ChangeEventName,
handler: Function handler: Function
): {remove: () => void} { ): {remove: () => void} {
const listener = NetInfoEventEmitter.addListener( let listener;
DEVICE_CONNECTIVITY_EVENT, if (eventName === 'connectionChange') {
(appStateData) => { listener = NetInfoEventEmitter.addListener(
handler(appStateData.network_info); DEVICE_CONNECTIVITY_EVENT,
} (appStateData) => {
); handler({
type: appStateData.connectionType,
effectiveType: appStateData.effectiveConnectionType
});
}
);
} else if (eventName === 'change') {
console.warn('NetInfo\'s "change" event is deprecated. Listen to the "connectionChange" event instead.');
listener = NetInfoEventEmitter.addListener(
DEVICE_CONNECTIVITY_EVENT,
(appStateData) => {
handler(appStateData.network_info);
}
);
} else {
console.warn('Trying to subscribe to unknown event: "' + eventName + '"');
return {
remove: () => {}
};
}
_subscriptions.set(handler, listener); _subscriptions.set(handler, listener);
return { return {
remove: () => NetInfo.removeEventListener(eventName, handler) remove: () => NetInfo.removeEventListener(eventName, handler)
@ -205,13 +256,28 @@ const NetInfo = {
}, },
/** /**
* Returns a promise that resolves with one of the connectivity types listed * This function is deprecated. Use `getConnectionInfo` instead. Returns a promise that
* above. * resolves with one of the deprecated connectivity types listed above.
*/ */
fetch(): Promise<any> { fetch(): Promise<any> {
console.warn('NetInfo.fetch() is deprecated. Use NetInfo.getConnectionInfo() instead.');
return RCTNetInfo.getCurrentConnectivity().then(resp => resp.network_info); return RCTNetInfo.getCurrentConnectivity().then(resp => resp.network_info);
}, },
/**
* Returns a promise that resolves to an object with `type` and `effectiveType` keys
* whose values are a `ConnectionType` and an `EffectiveConnectionType`, (described above),
* respectively.
*/
getConnectionInfo(): Promise<any> {
return RCTNetInfo.getCurrentConnectivity().then(resp => {
return {
type: resp.connectionType,
effectiveType: resp.effectiveConnectionType,
};
});
},
/** /**
* An object with the same methods as above but the listener receives a * An object with the same methods as above but the listener receives a
* boolean which represents the internet connectivity. * boolean which represents the internet connectivity.

Просмотреть файл

@ -9,10 +9,28 @@
#import "RCTNetInfo.h" #import "RCTNetInfo.h"
#if !TARGET_OS_TV
#import <CoreTelephony/CTTelephonyNetworkInfo.h>
#endif
#import <React/RCTAssert.h> #import <React/RCTAssert.h>
#import <React/RCTBridge.h> #import <React/RCTBridge.h>
#import <React/RCTEventDispatcher.h> #import <React/RCTEventDispatcher.h>
// Based on the ConnectionType enum described in the W3C Network Information API spec
// (https://wicg.github.io/netinfo/).
static NSString *const RCTConnectionTypeUnknown = @"unknown";
static NSString *const RCTConnectionTypeNone = @"none";
static NSString *const RCTConnectionTypeWifi = @"wifi";
static NSString *const RCTConnectionTypeCellular = @"cellular";
// Based on the EffectiveConnectionType enum described in the W3C Network Information API spec
// (https://wicg.github.io/netinfo/).
static NSString *const RCTEffectiveConnectionTypeUnknown = @"unknown";
static NSString *const RCTEffectiveConnectionType2g = @"2g";
static NSString *const RCTEffectiveConnectionType3g = @"3g";
static NSString *const RCTEffectiveConnectionType4g = @"4g";
// The RCTReachabilityState* values are deprecated.
static NSString *const RCTReachabilityStateUnknown = @"unknown"; static NSString *const RCTReachabilityStateUnknown = @"unknown";
static NSString *const RCTReachabilityStateNone = @"none"; static NSString *const RCTReachabilityStateNone = @"none";
static NSString *const RCTReachabilityStateWifi = @"wifi"; static NSString *const RCTReachabilityStateWifi = @"wifi";
@ -21,7 +39,9 @@ static NSString *const RCTReachabilityStateCell = @"cell";
@implementation RCTNetInfo @implementation RCTNetInfo
{ {
SCNetworkReachabilityRef _reachability; SCNetworkReachabilityRef _reachability;
NSString *_status; NSString *_connectionType;
NSString *_effectiveConnectionType;
NSString *_statusDeprecated;
NSString *_host; NSString *_host;
} }
@ -30,27 +50,57 @@ RCT_EXPORT_MODULE()
static void RCTReachabilityCallback(__unused SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void *info) static void RCTReachabilityCallback(__unused SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void *info)
{ {
RCTNetInfo *self = (__bridge id)info; RCTNetInfo *self = (__bridge id)info;
NSString *connectionType = RCTConnectionTypeUnknown;
NSString *effectiveConnectionType = RCTEffectiveConnectionTypeUnknown;
NSString *status = RCTReachabilityStateUnknown; NSString *status = RCTReachabilityStateUnknown;
if ((flags & kSCNetworkReachabilityFlagsReachable) == 0 || if ((flags & kSCNetworkReachabilityFlagsReachable) == 0 ||
(flags & kSCNetworkReachabilityFlagsConnectionRequired) != 0) { (flags & kSCNetworkReachabilityFlagsConnectionRequired) != 0) {
connectionType = RCTConnectionTypeNone;
status = RCTReachabilityStateNone; status = RCTReachabilityStateNone;
} }
#if TARGET_OS_IPHONE #if !TARGET_OS_TV
else if ((flags & kSCNetworkReachabilityFlagsIsWWAN) != 0) { else if ((flags & kSCNetworkReachabilityFlagsIsWWAN) != 0) {
connectionType = RCTConnectionTypeCellular;
status = RCTReachabilityStateCell; status = RCTReachabilityStateCell;
CTTelephonyNetworkInfo *netinfo = [[CTTelephonyNetworkInfo alloc] init];
if (netinfo) {
if ([netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyGPRS] ||
[netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyEdge] ||
[netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyCDMA1x]) {
effectiveConnectionType = RCTEffectiveConnectionType2g;
} else if ([netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyWCDMA] ||
[netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyHSDPA] ||
[netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyHSUPA] ||
[netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyCDMAEVDORev0] ||
[netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyCDMAEVDORevA] ||
[netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyCDMAEVDORevB] ||
[netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyeHRPD]) {
effectiveConnectionType = RCTEffectiveConnectionType3g;
} else if ([netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyLTE]) {
effectiveConnectionType = RCTEffectiveConnectionType4g;
}
}
} }
#endif #endif
else { else {
connectionType = RCTConnectionTypeWifi;
status = RCTReachabilityStateWifi; status = RCTReachabilityStateWifi;
} }
if (![status isEqualToString:self->_status]) { if (![connectionType isEqualToString:self->_connectionType] ||
self->_status = status; ![effectiveConnectionType isEqualToString:self->_effectiveConnectionType] ||
[self sendEventWithName:@"networkStatusDidChange" body:@{@"network_info": status}]; ![status isEqualToString:self->_statusDeprecated]) {
self->_connectionType = connectionType;
self->_effectiveConnectionType = effectiveConnectionType;
self->_statusDeprecated = status;
[self sendEventWithName:@"networkStatusDidChange" body:@{@"connectionType": connectionType,
@"effectiveConnectionType": effectiveConnectionType,
@"network_info": status}];
} }
} }
@ -74,7 +124,9 @@ static void RCTReachabilityCallback(__unused SCNetworkReachabilityRef target, SC
- (void)startObserving - (void)startObserving
{ {
_status = RCTReachabilityStateUnknown; _connectionType = RCTConnectionTypeUnknown;
_effectiveConnectionType = RCTEffectiveConnectionTypeUnknown;
_statusDeprecated = RCTReachabilityStateUnknown;
_reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, _host.UTF8String ?: "apple.com"); _reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, _host.UTF8String ?: "apple.com");
SCNetworkReachabilityContext context = { 0, ( __bridge void *)self, NULL, NULL, NULL }; SCNetworkReachabilityContext context = { 0, ( __bridge void *)self, NULL, NULL, NULL };
SCNetworkReachabilitySetCallback(_reachability, RCTReachabilityCallback, &context); SCNetworkReachabilitySetCallback(_reachability, RCTReachabilityCallback, &context);
@ -94,7 +146,9 @@ static void RCTReachabilityCallback(__unused SCNetworkReachabilityRef target, SC
RCT_EXPORT_METHOD(getCurrentConnectivity:(RCTPromiseResolveBlock)resolve RCT_EXPORT_METHOD(getCurrentConnectivity:(RCTPromiseResolveBlock)resolve
reject:(__unused RCTPromiseRejectBlock)reject) reject:(__unused RCTPromiseRejectBlock)reject)
{ {
resolve(@{@"network_info": _status ?: RCTReachabilityStateUnknown}); resolve(@{@"connectionType": _connectionType ?: RCTConnectionTypeUnknown,
@"effectiveConnectionType": _effectiveConnectionType ?: RCTEffectiveConnectionTypeUnknown,
@"network_info": _statusDeprecated ?: RCTReachabilityStateUnknown});
} }
@end @end

Просмотреть файл

@ -16,6 +16,7 @@ import android.content.IntentFilter;
import android.net.ConnectivityManager; import android.net.ConnectivityManager;
import android.net.NetworkInfo; import android.net.NetworkInfo;
import android.support.v4.net.ConnectivityManagerCompat; import android.support.v4.net.ConnectivityManagerCompat;
import android.telephony.TelephonyManager;
import com.facebook.react.bridge.LifecycleEventListener; import com.facebook.react.bridge.LifecycleEventListener;
import com.facebook.react.bridge.Promise; import com.facebook.react.bridge.Promise;
@ -35,8 +36,26 @@ import static com.facebook.react.modules.core.DeviceEventManagerModule.RCTDevice
public class NetInfoModule extends ReactContextBaseJavaModule public class NetInfoModule extends ReactContextBaseJavaModule
implements LifecycleEventListener { implements LifecycleEventListener {
private static final String CONNECTION_TYPE_NONE = "NONE"; // Based on the ConnectionType enum described in the W3C Network Information API spec
private static final String CONNECTION_TYPE_UNKNOWN = "UNKNOWN"; // (https://wicg.github.io/netinfo/).
private static final String CONNECTION_TYPE_BLUETOOTH = "bluetooth";
private static final String CONNECTION_TYPE_CELLULAR = "cellular";
private static final String CONNECTION_TYPE_ETHERNET = "ethernet";
private static final String CONNECTION_TYPE_NONE = "none";
private static final String CONNECTION_TYPE_UNKNOWN = "unknown";
private static final String CONNECTION_TYPE_WIFI = "wifi";
private static final String CONNECTION_TYPE_WIMAX = "wimax";
// Based on the EffectiveConnectionType enum described in the W3C Network Information API spec
// (https://wicg.github.io/netinfo/).
private static final String EFFECTIVE_CONNECTION_TYPE_UNKNOWN = "unknown";
private static final String EFFECTIVE_CONNECTION_TYPE_2G = "2g";
private static final String EFFECTIVE_CONNECTION_TYPE_3G = "3g";
private static final String EFFECTIVE_CONNECTION_TYPE_4G = "4g";
private static final String CONNECTION_TYPE_NONE_DEPRECATED = "NONE";
private static final String CONNECTION_TYPE_UNKNOWN_DEPRECATED = "UNKNOWN";
private static final String MISSING_PERMISSION_MESSAGE = private static final String MISSING_PERMISSION_MESSAGE =
"To use NetInfo on Android, add the following to your AndroidManifest.xml:\n" + "To use NetInfo on Android, add the following to your AndroidManifest.xml:\n" +
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" />"; "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" />";
@ -47,7 +66,9 @@ public class NetInfoModule extends ReactContextBaseJavaModule
private final ConnectivityBroadcastReceiver mConnectivityBroadcastReceiver; private final ConnectivityBroadcastReceiver mConnectivityBroadcastReceiver;
private boolean mNoNetworkPermission = false; private boolean mNoNetworkPermission = false;
private String mConnectivity = CONNECTION_TYPE_UNKNOWN; private String mConnectivityDeprecated = CONNECTION_TYPE_UNKNOWN_DEPRECATED;
private String mConnectionType = CONNECTION_TYPE_UNKNOWN;
private String mEffectiveConnectionType = EFFECTIVE_CONNECTION_TYPE_UNKNOWN;
public NetInfoModule(ReactApplicationContext reactContext) { public NetInfoModule(ReactApplicationContext reactContext) {
super(reactContext); super(reactContext);
@ -113,11 +134,52 @@ public class NetInfoModule extends ReactContextBaseJavaModule
} }
private void updateAndSendConnectionType() { private void updateAndSendConnectionType() {
String connectionType = CONNECTION_TYPE_UNKNOWN;
String effectiveConnectionType = EFFECTIVE_CONNECTION_TYPE_UNKNOWN;
try {
NetworkInfo networkInfo = mConnectivityManager.getActiveNetworkInfo();
if (networkInfo == null || !networkInfo.isConnected()) {
connectionType = CONNECTION_TYPE_NONE;
} else {
int networkType = networkInfo.getType();
switch (networkType) {
case ConnectivityManager.TYPE_BLUETOOTH:
connectionType = CONNECTION_TYPE_BLUETOOTH;
break;
case ConnectivityManager.TYPE_ETHERNET:
connectionType = CONNECTION_TYPE_ETHERNET;
break;
case ConnectivityManager.TYPE_MOBILE:
case ConnectivityManager.TYPE_MOBILE_DUN:
connectionType = CONNECTION_TYPE_CELLULAR;
effectiveConnectionType = getEffectiveConnectionType(networkInfo);
break;
case ConnectivityManager.TYPE_WIFI:
connectionType = CONNECTION_TYPE_WIFI;
break;
case ConnectivityManager.TYPE_WIMAX:
connectionType = CONNECTION_TYPE_WIMAX;
break;
default:
connectionType = CONNECTION_TYPE_UNKNOWN;
break;
}
}
} catch (SecurityException e) {
mNoNetworkPermission = true;
connectionType = CONNECTION_TYPE_UNKNOWN;
}
String currentConnectivity = getCurrentConnectionType(); String currentConnectivity = getCurrentConnectionType();
// It is possible to get multiple broadcasts for the same connectivity change, so we only // It is possible to get multiple broadcasts for the same connectivity change, so we only
// update and send an event when the connectivity has indeed changed. // update and send an event when the connectivity has indeed changed.
if (!currentConnectivity.equalsIgnoreCase(mConnectivity)) { if (!connectionType.equalsIgnoreCase(mConnectionType) ||
mConnectivity = currentConnectivity; !effectiveConnectionType.equalsIgnoreCase(mEffectiveConnectionType) ||
!currentConnectivity.equalsIgnoreCase(mConnectivityDeprecated)) {
mConnectionType = connectionType;
mEffectiveConnectionType = effectiveConnectionType;
mConnectivityDeprecated = currentConnectivity;
sendConnectivityChangedEvent(); sendConnectivityChangedEvent();
} }
} }
@ -126,15 +188,41 @@ public class NetInfoModule extends ReactContextBaseJavaModule
try { try {
NetworkInfo networkInfo = mConnectivityManager.getActiveNetworkInfo(); NetworkInfo networkInfo = mConnectivityManager.getActiveNetworkInfo();
if (networkInfo == null || !networkInfo.isConnected()) { if (networkInfo == null || !networkInfo.isConnected()) {
return CONNECTION_TYPE_NONE; return CONNECTION_TYPE_NONE_DEPRECATED;
} else if (ConnectivityManager.isNetworkTypeValid(networkInfo.getType())) { } else if (ConnectivityManager.isNetworkTypeValid(networkInfo.getType())) {
return networkInfo.getTypeName().toUpperCase(); return networkInfo.getTypeName().toUpperCase();
} else { } else {
return CONNECTION_TYPE_UNKNOWN; return CONNECTION_TYPE_UNKNOWN_DEPRECATED;
} }
} catch (SecurityException e) { } catch (SecurityException e) {
mNoNetworkPermission = true; mNoNetworkPermission = true;
return CONNECTION_TYPE_UNKNOWN; return CONNECTION_TYPE_UNKNOWN_DEPRECATED;
}
}
private String getEffectiveConnectionType(NetworkInfo networkInfo) {
switch (networkInfo.getSubtype()) {
case TelephonyManager.NETWORK_TYPE_1xRTT:
case TelephonyManager.NETWORK_TYPE_CDMA:
case TelephonyManager.NETWORK_TYPE_EDGE:
case TelephonyManager.NETWORK_TYPE_GPRS:
case TelephonyManager.NETWORK_TYPE_IDEN:
return EFFECTIVE_CONNECTION_TYPE_2G;
case TelephonyManager.NETWORK_TYPE_EHRPD:
case TelephonyManager.NETWORK_TYPE_EVDO_0:
case TelephonyManager.NETWORK_TYPE_EVDO_A:
case TelephonyManager.NETWORK_TYPE_EVDO_B:
case TelephonyManager.NETWORK_TYPE_HSDPA:
case TelephonyManager.NETWORK_TYPE_HSPA:
case TelephonyManager.NETWORK_TYPE_HSUPA:
case TelephonyManager.NETWORK_TYPE_UMTS:
return EFFECTIVE_CONNECTION_TYPE_3G;
case TelephonyManager.NETWORK_TYPE_HSPAP:
case TelephonyManager.NETWORK_TYPE_LTE:
return EFFECTIVE_CONNECTION_TYPE_4G;
case TelephonyManager.NETWORK_TYPE_UNKNOWN:
default:
return EFFECTIVE_CONNECTION_TYPE_UNKNOWN;
} }
} }
@ -145,7 +233,9 @@ public class NetInfoModule extends ReactContextBaseJavaModule
private WritableMap createConnectivityEventMap() { private WritableMap createConnectivityEventMap() {
WritableMap event = new WritableNativeMap(); WritableMap event = new WritableNativeMap();
event.putString("network_info", mConnectivity); event.putString("network_info", mConnectivityDeprecated);
event.putString("connectionType", mConnectionType);
event.putString("effectiveConnectionType", mEffectiveConnectionType);
return event; return event;
} }