@ohos-ports/expo-network 57.0.2-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/CHANGELOG.md +391 -0
  2. package/LICENSE +21 -0
  3. package/README.md +44 -0
  4. package/android/build.gradle +27 -0
  5. package/android/src/main/AndroidManifest.xml +4 -0
  6. package/android/src/main/java/expo/modules/network/NetworkExceptions.kt +9 -0
  7. package/android/src/main/java/expo/modules/network/NetworkModule.kt +281 -0
  8. package/android/src/main/java/expo/modules/network/NetworkUtils.kt +52 -0
  9. package/build/ExpoNetwork.d.ts +3 -0
  10. package/build/ExpoNetwork.d.ts.map +1 -0
  11. package/build/ExpoNetwork.js +3 -0
  12. package/build/ExpoNetwork.js.map +1 -0
  13. package/build/ExpoNetwork.web.d.ts +14 -0
  14. package/build/ExpoNetwork.web.d.ts.map +1 -0
  15. package/build/ExpoNetwork.web.js +47 -0
  16. package/build/ExpoNetwork.web.js.map +1 -0
  17. package/build/Network.d.ts +80 -0
  18. package/build/Network.d.ts.map +1 -0
  19. package/build/Network.js +111 -0
  20. package/build/Network.js.map +1 -0
  21. package/build/Network.types.d.ts +85 -0
  22. package/build/Network.types.d.ts.map +1 -0
  23. package/build/Network.types.js +55 -0
  24. package/build/Network.types.js.map +1 -0
  25. package/expo-module.config.json +15 -0
  26. package/ios/Exceptions.swift +7 -0
  27. package/ios/ExpoNetwork.podspec +28 -0
  28. package/ios/NetworkModule.swift +155 -0
  29. package/ios/NetworkType.swift +30 -0
  30. package/local-maven-repo/host/exp/exponent/expo.modules.network/57.0.2/expo.modules.network-57.0.2-sources.jar +0 -0
  31. package/local-maven-repo/host/exp/exponent/expo.modules.network/57.0.2/expo.modules.network-57.0.2-sources.jar.md5 +1 -0
  32. package/local-maven-repo/host/exp/exponent/expo.modules.network/57.0.2/expo.modules.network-57.0.2-sources.jar.sha1 +1 -0
  33. package/local-maven-repo/host/exp/exponent/expo.modules.network/57.0.2/expo.modules.network-57.0.2-sources.jar.sha256 +1 -0
  34. package/local-maven-repo/host/exp/exponent/expo.modules.network/57.0.2/expo.modules.network-57.0.2-sources.jar.sha512 +1 -0
  35. package/local-maven-repo/host/exp/exponent/expo.modules.network/57.0.2/expo.modules.network-57.0.2.aar +0 -0
  36. package/local-maven-repo/host/exp/exponent/expo.modules.network/57.0.2/expo.modules.network-57.0.2.aar.md5 +1 -0
  37. package/local-maven-repo/host/exp/exponent/expo.modules.network/57.0.2/expo.modules.network-57.0.2.aar.sha1 +1 -0
  38. package/local-maven-repo/host/exp/exponent/expo.modules.network/57.0.2/expo.modules.network-57.0.2.aar.sha256 +1 -0
  39. package/local-maven-repo/host/exp/exponent/expo.modules.network/57.0.2/expo.modules.network-57.0.2.aar.sha512 +1 -0
  40. package/local-maven-repo/host/exp/exponent/expo.modules.network/57.0.2/expo.modules.network-57.0.2.module +101 -0
  41. package/local-maven-repo/host/exp/exponent/expo.modules.network/57.0.2/expo.modules.network-57.0.2.module.md5 +1 -0
  42. package/local-maven-repo/host/exp/exponent/expo.modules.network/57.0.2/expo.modules.network-57.0.2.module.sha1 +1 -0
  43. package/local-maven-repo/host/exp/exponent/expo.modules.network/57.0.2/expo.modules.network-57.0.2.module.sha256 +1 -0
  44. package/local-maven-repo/host/exp/exponent/expo.modules.network/57.0.2/expo.modules.network-57.0.2.module.sha512 +1 -0
  45. package/local-maven-repo/host/exp/exponent/expo.modules.network/57.0.2/expo.modules.network-57.0.2.pom +59 -0
  46. package/local-maven-repo/host/exp/exponent/expo.modules.network/57.0.2/expo.modules.network-57.0.2.pom.md5 +1 -0
  47. package/local-maven-repo/host/exp/exponent/expo.modules.network/57.0.2/expo.modules.network-57.0.2.pom.sha1 +1 -0
  48. package/local-maven-repo/host/exp/exponent/expo.modules.network/57.0.2/expo.modules.network-57.0.2.pom.sha256 +1 -0
  49. package/local-maven-repo/host/exp/exponent/expo.modules.network/57.0.2/expo.modules.network-57.0.2.pom.sha512 +1 -0
  50. package/local-maven-repo/host/exp/exponent/expo.modules.network/maven-metadata.xml +13 -0
  51. package/local-maven-repo/host/exp/exponent/expo.modules.network/maven-metadata.xml.md5 +1 -0
  52. package/local-maven-repo/host/exp/exponent/expo.modules.network/maven-metadata.xml.sha1 +1 -0
  53. package/local-maven-repo/host/exp/exponent/expo.modules.network/maven-metadata.xml.sha256 +1 -0
  54. package/local-maven-repo/host/exp/exponent/expo.modules.network/maven-metadata.xml.sha512 +1 -0
  55. package/package.json +41 -0
  56. package/spm.config.json +37 -0
  57. package/src/ExpoNetwork.ts +3 -0
  58. package/src/ExpoNetwork.web.ts +52 -0
  59. package/src/Network.ts +123 -0
  60. package/src/Network.types.ts +90 -0
  61. package/tsconfig.json +10 -0
@@ -0,0 +1,281 @@
1
+ package expo.modules.network
2
+
3
+ import android.content.Context
4
+ import android.net.ConnectivityManager
5
+ import android.net.Network
6
+ import android.net.NetworkCapabilities
7
+ import android.net.NetworkInfo
8
+ import android.net.wifi.WifiInfo
9
+ import android.net.wifi.WifiManager
10
+ import android.os.Build
11
+ import android.os.Bundle
12
+ import android.os.Handler
13
+ import android.os.Looper
14
+ import android.provider.Settings
15
+ import android.util.Log
16
+ import expo.modules.kotlin.exception.Exceptions
17
+ import expo.modules.kotlin.modules.Module
18
+ import expo.modules.kotlin.modules.ModuleDefinition
19
+ import java.math.BigInteger
20
+ import java.net.InetAddress
21
+ import java.net.UnknownHostException
22
+ import java.nio.ByteOrder
23
+
24
+ private val TAG = NetworkModule::class.java.simpleName
25
+
26
+ internal const val NETWORK_STATE_EVENT_NAME = "onNetworkStateChanged"
27
+
28
+ class NetworkModule : Module() {
29
+ private val context: Context
30
+ get() = appContext.reactContext ?: throw Exceptions.ReactContextLost()
31
+ private val connectivityManager: ConnectivityManager
32
+ get() = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
33
+ private val mainHandler = Handler(Looper.getMainLooper())
34
+ private val DELAY_MS = 250
35
+ private var isNetworkCallbackRegistered = false
36
+
37
+ private val emitRunnable = Runnable { emitNetworkState() }
38
+
39
+ private val networkCallback = object : ConnectivityManager.NetworkCallback() {
40
+ override fun onAvailable(network: android.net.Network) {
41
+ asyncEmitNetworkState(DELAY_MS)
42
+ }
43
+
44
+ override fun onLost(lostNetwork: android.net.Network) {
45
+ // We intentionally do NOT reuse asyncEmitNetworkState(DELAY_MS) here.
46
+ //
47
+ // The 250ms delay used by onAvailable cannot be applied to onLost. On
48
+ // Android 13+, connectivityManager.activeNetwork continues to return the
49
+ // just-lost Network object even after the delay. Re-querying it would
50
+ // emit a stale "isConnected = true" event — the exact bug reported in
51
+ // https://github.com/expo/expo/issues/37972. This behavior isn't
52
+ // documented by AOSP as version-specific, so we apply this check
53
+ // defensively on all API 29+ devices.
54
+ //
55
+ // Instead, we compare the lost network against the current active network.
56
+ // If they match (or activeNetwork is null), there is no replacement
57
+ // network and we emit a disconnected state directly. If a different
58
+ // network is active (e.g. cellular), we emit
59
+ // its state instead.
60
+ //
61
+ // Note: android.net.Network.equals() compares by netId, so the check
62
+ // below is a reliable "same network" comparison.
63
+ //
64
+ // We still post to the main looper because sendEvent must run on the
65
+ // main thread; we just skip the artificial delay.
66
+ mainHandler.removeCallbacks(emitRunnable)
67
+ mainHandler.post {
68
+ try {
69
+ val activeNetwork = connectivityManager.activeNetwork
70
+ if (activeNetwork == null || activeNetwork == lostNetwork) {
71
+ val result = Bundle().apply {
72
+ putString("type", NetworkStateType.NONE.value)
73
+ putBoolean("isInternetReachable", false)
74
+ putBoolean("isConnected", false)
75
+ }
76
+ sendEvent(NETWORK_STATE_EVENT_NAME, result)
77
+ } else {
78
+ emitNetworkState()
79
+ }
80
+ } catch (e: SecurityException) {
81
+ // Missing ACCESS_NETWORK_STATE permission (or runtime revocation).
82
+ // Ensure the permission is declared in AndroidManifest.xml and
83
+ // granted at runtime on devices that require it.
84
+ Log.w(TAG, "expo-network could not read network state in onLost: missing ACCESS_NETWORK_STATE permission", e)
85
+ } catch (e: Exception) {
86
+ // The runnable may outlive the module if the React context is torn
87
+ // down between the ConnectivityManager.NetworkCallback firing and
88
+ // the posted runnable executing. In that case, the `connectivityManager`
89
+ // getter throws `ReactContextLost` when it
90
+ // attempts to resolve `appContext.reactContext`. Since
91
+ // unregisterNetworkCallback only prevents future callbacks and
92
+ // cannot cancel a runnable that is already in the queue, we must
93
+ // catch teardown-time exceptions here to avoid crashing the
94
+ // host app's main thread.
95
+ Log.w(TAG, "expo-network dropped a network state update during teardown (the module or React context is no longer available)", e)
96
+ }
97
+ }
98
+ }
99
+
100
+ override fun onCapabilitiesChanged(network: android.net.Network, networkCapabilities: NetworkCapabilities) {
101
+ asyncEmitNetworkState(DELAY_MS)
102
+ }
103
+ }
104
+
105
+ override fun definition() = ModuleDefinition {
106
+ Name("ExpoNetwork")
107
+
108
+ Events(NETWORK_STATE_EVENT_NAME)
109
+
110
+ OnCreate {
111
+ try {
112
+ connectivityManager.registerDefaultNetworkCallback(networkCallback)
113
+ isNetworkCallbackRegistered = true
114
+ } catch (e: SecurityException) {
115
+ Log.w(TAG, "expo-network cannot observe internet reachability: missing ACCESS_NETWORK_STATE permission", e)
116
+ } catch (e: RuntimeException) {
117
+ Log.e(TAG, "expo-network cannot observe internet reachability, too many callbacks registered!", e)
118
+ }
119
+ }
120
+
121
+ OnDestroy {
122
+ mainHandler.removeCallbacks(emitRunnable)
123
+ if (isNetworkCallbackRegistered) {
124
+ try {
125
+ connectivityManager.unregisterNetworkCallback(networkCallback)
126
+ } catch (e: SecurityException) {
127
+ Log.w(TAG, "expo-network could not unregister network callback: missing ACCESS_NETWORK_STATE permission", e)
128
+ } catch (e: Exception) {
129
+ Log.w(TAG, "expo-network could not unregister network callback during teardown", e)
130
+ } finally {
131
+ isNetworkCallbackRegistered = false
132
+ }
133
+ }
134
+ }
135
+
136
+ AsyncFunction("getNetworkStateAsync") {
137
+ return@AsyncFunction fetchNetworkState()
138
+ }
139
+
140
+ AsyncFunction<String>("getIpAddressAsync") {
141
+ return@AsyncFunction rawIpToString(wifiInfo.ipAddress)
142
+ }
143
+
144
+ AsyncFunction<Boolean>("isAirplaneModeEnabledAsync") {
145
+ return@AsyncFunction Settings.Global.getInt(context.contentResolver, Settings.Global.AIRPLANE_MODE_ON, 0) != 0
146
+ }
147
+ }
148
+
149
+ enum class NetworkStateType(val value: String) {
150
+ NONE("NONE"),
151
+ UNKNOWN("UNKNOWN"),
152
+ CELLULAR("CELLULAR"),
153
+ WIFI("WIFI"),
154
+ BLUETOOTH("BLUETOOTH"),
155
+ ETHERNET("ETHERNET"),
156
+ WIMAX("WIMAX"),
157
+ VPN("VPN"),
158
+ OTHER("OTHER");
159
+
160
+ val isDefined: Boolean
161
+ get() = this.value != "NONE" && this.value != "UNKNOWN"
162
+ }
163
+
164
+ private fun emitNetworkState() {
165
+ try {
166
+ val networkState = fetchNetworkState()
167
+ sendEvent(NETWORK_STATE_EVENT_NAME, networkState)
168
+ } catch (e: SecurityException) {
169
+ Log.w(TAG, "expo-network could not read network state: missing ACCESS_NETWORK_STATE permission", e)
170
+ } catch (e: Exception) {
171
+ // The runnable may outlive the module if the React context is torn down between the
172
+ // ConnectivityManager callback firing and this runnable executing.
173
+ Log.w(TAG, "expo-network dropped a delayed network state update during teardown (the module or React context is no longer available)", e)
174
+ }
175
+ }
176
+
177
+ /**
178
+ * Emits the network state with a delay to prevent a race condition.
179
+ * This delay ensures we read the actual current network state rather than stale information.
180
+ * Debounced: rapid successive calls collapse into a single emission.
181
+ */
182
+ private fun asyncEmitNetworkState(delay: Int) {
183
+ mainHandler.removeCallbacks(emitRunnable)
184
+ mainHandler.postDelayed(emitRunnable, delay.toLong())
185
+ }
186
+
187
+ private fun fetchNetworkState(): Bundle {
188
+ val result = Bundle()
189
+
190
+ try {
191
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { // use getActiveNetworkInfo before api level 29
192
+ val netInfo = connectivityManager.activeNetworkInfo
193
+ val connectionType = getConnectionType(netInfo)
194
+ val isInternetReachable = isInternetReachable(connectivityManager)
195
+
196
+ result.apply {
197
+ putBoolean("isInternetReachable", isInternetReachable)
198
+ putString("type", connectionType.value)
199
+ putBoolean("isConnected", connectionType.isDefined)
200
+ }
201
+
202
+ return result
203
+ } else {
204
+ val network = connectivityManager.activeNetwork
205
+ val isInternetReachable = isInternetReachable(connectivityManager)
206
+
207
+ val connectionType = if (network != null) {
208
+ val netCapabilities = connectivityManager.getNetworkCapabilities(network)
209
+ getConnectionType(netCapabilities)
210
+ } else {
211
+ null
212
+ }
213
+
214
+ result.apply {
215
+ putString("type", connectionType?.value ?: NetworkStateType.NONE.value)
216
+ putBoolean("isInternetReachable", isInternetReachable)
217
+ putBoolean("isConnected", connectionType != null && connectionType.isDefined)
218
+ }
219
+ return result
220
+ }
221
+ } catch (e: Exception) {
222
+ result.apply {
223
+ putString("type", NetworkStateType.UNKNOWN.value)
224
+ putBoolean("isInternetReachable", false)
225
+ putBoolean("isConnected", false)
226
+ }
227
+ return result
228
+ }
229
+ }
230
+
231
+ private val wifiInfo: WifiInfo
232
+ get() = try {
233
+ val manager = context.getSystemService(Context.WIFI_SERVICE) as WifiManager
234
+ manager.connectionInfo
235
+ } catch (e: Exception) {
236
+ Log.e(TAG, e.message ?: "expo-network could not acquire Wi-Fi information")
237
+ throw NetworkWifiException(e)
238
+ }
239
+
240
+ private fun getConnectionType(netInfo: NetworkInfo?): NetworkStateType = when (netInfo?.type) {
241
+ ConnectivityManager.TYPE_MOBILE,
242
+ ConnectivityManager.TYPE_MOBILE_DUN -> NetworkStateType.CELLULAR
243
+ ConnectivityManager.TYPE_WIFI -> NetworkStateType.WIFI
244
+ ConnectivityManager.TYPE_BLUETOOTH -> NetworkStateType.BLUETOOTH
245
+ ConnectivityManager.TYPE_ETHERNET -> NetworkStateType.ETHERNET
246
+ ConnectivityManager.TYPE_WIMAX -> NetworkStateType.WIMAX
247
+ ConnectivityManager.TYPE_VPN -> NetworkStateType.VPN
248
+ else -> NetworkStateType.UNKNOWN
249
+ }
250
+
251
+ private fun getConnectionType(netCapabilities: NetworkCapabilities?): NetworkStateType =
252
+ when {
253
+ netCapabilities == null -> NetworkStateType.UNKNOWN
254
+ netCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> NetworkStateType.CELLULAR
255
+ netCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) || netCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI_AWARE) -> NetworkStateType.WIFI
256
+ netCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH) -> NetworkStateType.BLUETOOTH
257
+ netCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> NetworkStateType.ETHERNET
258
+ netCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN) -> NetworkStateType.VPN
259
+ else -> NetworkStateType.UNKNOWN
260
+ }
261
+
262
+ private fun rawIpToString(ipAddress: Int): String {
263
+ // Convert little-endian to big-endian if needed
264
+ val ip = if (ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN) {
265
+ Integer.reverseBytes(ipAddress)
266
+ } else {
267
+ ipAddress
268
+ }
269
+
270
+ var ipByteArray = BigInteger.valueOf(ip.toLong()).toByteArray()
271
+ if (ipByteArray.size < 4) {
272
+ ipByteArray = frontPadWithZeros(ipByteArray)
273
+ }
274
+
275
+ return try {
276
+ InetAddress.getByAddress(ipByteArray).hostAddress as String
277
+ } catch (e: UnknownHostException) {
278
+ "0.0.0.0"
279
+ }
280
+ }
281
+ }
@@ -0,0 +1,52 @@
1
+ package expo.modules.network
2
+
3
+ import android.net.ConnectivityManager
4
+ import android.net.NetworkCapabilities
5
+ import android.net.NetworkInfo
6
+ import android.os.Build
7
+ import android.util.Log
8
+
9
+ private val TAG = NetworkModule::class.java.simpleName
10
+
11
+ internal fun frontPadWithZeros(inputArray: ByteArray): ByteArray {
12
+ val newByteArray = byteArrayOf(0, 0, 0, 0)
13
+ System.arraycopy(inputArray, 0, newByteArray, 4 - inputArray.size, inputArray.size)
14
+ return newByteArray
15
+ }
16
+
17
+ /**
18
+ * Returns whether the device can reach the Internet.
19
+ * Checks that the active network has both the INTERNET and VALIDATED
20
+ * capabilities, has a usable connection state, and (for VPN connections)
21
+ * has non-zero downstream bandwidth.
22
+ */
23
+ internal fun isInternetReachable(
24
+ connectivityManager: ConnectivityManager,
25
+ sdkInt: Int = Build.VERSION.SDK_INT
26
+ ): Boolean {
27
+ val network = connectivityManager.activeNetwork ?: return false
28
+ val capabilities = connectivityManager.getNetworkCapabilities(network) ?: return false
29
+
30
+ val hasUsableConnectionState = if (sdkInt >= Build.VERSION_CODES.Q) {
31
+ capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)
32
+ } else {
33
+ val networkInfo = try {
34
+ connectivityManager.getNetworkInfo(network)
35
+ } catch (e: SecurityException) {
36
+ Log.w(TAG, "expo-network could not read network state: missing ACCESS_NETWORK_STATE permission", e)
37
+ null
38
+ }
39
+ networkInfo?.detailedState == NetworkInfo.DetailedState.CONNECTED
40
+ }
41
+
42
+ var isReachable =
43
+ capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) &&
44
+ capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) &&
45
+ hasUsableConnectionState
46
+
47
+ if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN)) {
48
+ isReachable = isReachable && capabilities.linkDownstreamBandwidthKbps != 0
49
+ }
50
+
51
+ return isReachable
52
+ }
@@ -0,0 +1,3 @@
1
+ declare const _default: any;
2
+ export default _default;
3
+ //# sourceMappingURL=ExpoNetwork.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExpoNetwork.d.ts","sourceRoot":"","sources":["../src/ExpoNetwork.ts"],"names":[],"mappings":";AAEA,wBAAkD"}
@@ -0,0 +1,3 @@
1
+ import { requireNativeModule } from 'expo-modules-core';
2
+ export default requireNativeModule('ExpoNetwork');
3
+ //# sourceMappingURL=ExpoNetwork.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExpoNetwork.js","sourceRoot":"","sources":["../src/ExpoNetwork.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAExD,eAAe,mBAAmB,CAAC,aAAa,CAAC,CAAC","sourcesContent":["import { requireNativeModule } from 'expo-modules-core';\n\nexport default requireNativeModule('ExpoNetwork');\n"]}
@@ -0,0 +1,14 @@
1
+ import { NativeModule } from 'expo-modules-core';
2
+ import type { NetworkEvents, NetworkState } from './Network.types';
3
+ declare class ExpoNetworkModule extends NativeModule<NetworkEvents> {
4
+ eventListener?: () => void;
5
+ updateNetworkState(): void;
6
+ getIpAddressAsync(): Promise<string>;
7
+ getNetworkStateAsync(): Promise<NetworkState>;
8
+ isAirplaneModeEnabledAsync(): Promise<boolean>;
9
+ startObserving(): void;
10
+ stopObserving(): void;
11
+ }
12
+ declare const _default: typeof ExpoNetworkModule;
13
+ export default _default;
14
+ //# sourceMappingURL=ExpoNetwork.web.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExpoNetwork.web.d.ts","sourceRoot":"","sources":["../src/ExpoNetwork.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAqB,MAAM,mBAAmB,CAAC;AAEpE,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAcnE,cAAM,iBAAkB,SAAQ,YAAY,CAAC,aAAa,CAAC;IACzD,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,kBAAkB;IAKZ,iBAAiB,IAAI,OAAO,CAAC,MAAM,CAAC;IASpC,oBAAoB,IAAI,OAAO,CAAC,YAAY,CAAC;IAG7C,0BAA0B,IAAI,OAAO,CAAC,OAAO,CAAC;IAGpD,cAAc;IAKd,aAAa;CAMd;;AAED,wBAAmE"}
@@ -0,0 +1,47 @@
1
+ import { NativeModule, registerWebModule } from 'expo-modules-core';
2
+ import { NetworkStateType } from './Network.types';
3
+ const onNetworkStateEventName = 'onNetworkStateChanged';
4
+ function getNetworkState() {
5
+ const isOnline = typeof navigator !== 'undefined' && navigator.onLine;
6
+ return {
7
+ type: isOnline ? NetworkStateType.UNKNOWN : NetworkStateType.NONE,
8
+ isConnected: isOnline,
9
+ isInternetReachable: isOnline,
10
+ };
11
+ }
12
+ class ExpoNetworkModule extends NativeModule {
13
+ eventListener;
14
+ updateNetworkState() {
15
+ const state = getNetworkState();
16
+ this.emit(onNetworkStateEventName, state);
17
+ }
18
+ async getIpAddressAsync() {
19
+ try {
20
+ const resp = await fetch('https://api.ipify.org?format=json');
21
+ const data = await resp.json();
22
+ return data.ip;
23
+ }
24
+ catch (e) {
25
+ throw e;
26
+ }
27
+ }
28
+ async getNetworkStateAsync() {
29
+ return getNetworkState();
30
+ }
31
+ async isAirplaneModeEnabledAsync() {
32
+ return false;
33
+ }
34
+ startObserving() {
35
+ this.eventListener = () => this.updateNetworkState();
36
+ window.addEventListener('online', this.eventListener);
37
+ window.addEventListener('offline', this.eventListener);
38
+ }
39
+ stopObserving() {
40
+ if (this.eventListener) {
41
+ window.removeEventListener('online', this.eventListener);
42
+ window.removeEventListener('offline', this.eventListener);
43
+ }
44
+ }
45
+ }
46
+ export default registerWebModule(ExpoNetworkModule, 'ExpoNetwork');
47
+ //# sourceMappingURL=ExpoNetwork.web.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExpoNetwork.web.js","sourceRoot":"","sources":["../src/ExpoNetwork.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAGpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAEnD,MAAM,uBAAuB,GAAG,uBAAuB,CAAC;AAExD,SAAS,eAAe;IACtB,MAAM,QAAQ,GAAG,OAAO,SAAS,KAAK,WAAW,IAAI,SAAS,CAAC,MAAM,CAAC;IACtE,OAAO;QACL,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC,gBAAgB,CAAC,IAAI;QACjE,WAAW,EAAE,QAAQ;QACrB,mBAAmB,EAAE,QAAQ;KAC9B,CAAC;AACJ,CAAC;AAED,MAAM,iBAAkB,SAAQ,YAA2B;IACzD,aAAa,CAAc;IAC3B,kBAAkB;QAChB,MAAM,KAAK,GAAG,eAAe,EAAE,CAAC;QAChC,IAAI,CAAC,IAAI,CAAC,uBAAuB,EAAE,KAAK,CAAC,CAAC;IAC5C,CAAC;IAED,KAAK,CAAC,iBAAiB;QACrB,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,mCAAmC,CAAC,CAAC;YAC9D,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;YAC/B,OAAO,IAAI,CAAC,EAAE,CAAC;QACjB,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,MAAM,CAAC,CAAC;QACV,CAAC;IACH,CAAC;IACD,KAAK,CAAC,oBAAoB;QACxB,OAAO,eAAe,EAAE,CAAC;IAC3B,CAAC;IACD,KAAK,CAAC,0BAA0B;QAC9B,OAAO,KAAK,CAAC;IACf,CAAC;IACD,cAAc;QACZ,IAAI,CAAC,aAAa,GAAG,GAAG,EAAE,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC;QACrD,MAAM,CAAC,gBAAgB,CAAC,QAAQ,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC;QACtD,MAAM,CAAC,gBAAgB,CAAC,SAAS,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC;IACzD,CAAC;IACD,aAAa;QACX,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACvB,MAAM,CAAC,mBAAmB,CAAC,QAAQ,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC;YACzD,MAAM,CAAC,mBAAmB,CAAC,SAAS,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC;QAC5D,CAAC;IACH,CAAC;CACF;AAED,eAAe,iBAAiB,CAAC,iBAAiB,EAAE,aAAa,CAAC,CAAC","sourcesContent":["import { NativeModule, registerWebModule } from 'expo-modules-core';\n\nimport type { NetworkEvents, NetworkState } from './Network.types';\nimport { NetworkStateType } from './Network.types';\n\nconst onNetworkStateEventName = 'onNetworkStateChanged';\n\nfunction getNetworkState(): NetworkState {\n const isOnline = typeof navigator !== 'undefined' && navigator.onLine;\n return {\n type: isOnline ? NetworkStateType.UNKNOWN : NetworkStateType.NONE,\n isConnected: isOnline,\n isInternetReachable: isOnline,\n };\n}\n\nclass ExpoNetworkModule extends NativeModule<NetworkEvents> {\n eventListener?: () => void;\n updateNetworkState() {\n const state = getNetworkState();\n this.emit(onNetworkStateEventName, state);\n }\n\n async getIpAddressAsync(): Promise<string> {\n try {\n const resp = await fetch('https://api.ipify.org?format=json');\n const data = await resp.json();\n return data.ip;\n } catch (e) {\n throw e;\n }\n }\n async getNetworkStateAsync(): Promise<NetworkState> {\n return getNetworkState();\n }\n async isAirplaneModeEnabledAsync(): Promise<boolean> {\n return false;\n }\n startObserving() {\n this.eventListener = () => this.updateNetworkState();\n window.addEventListener('online', this.eventListener);\n window.addEventListener('offline', this.eventListener);\n }\n stopObserving() {\n if (this.eventListener) {\n window.removeEventListener('online', this.eventListener);\n window.removeEventListener('offline', this.eventListener);\n }\n }\n}\n\nexport default registerWebModule(ExpoNetworkModule, 'ExpoNetwork');\n"]}
@@ -0,0 +1,80 @@
1
+ import { type EventSubscription } from 'expo-modules-core';
2
+ import type { NetworkState, NetworkStateEvent } from './Network.types';
3
+ export { type NetworkState, type NetworkStateEvent, NetworkStateType } from './Network.types';
4
+ /**
5
+ * Gets the device's current network connection state.
6
+ *
7
+ * On web, `navigator.connection.type` is not available on browsers. So if there is an active
8
+ * network connection, the field `type` returns `NetworkStateType.UNKNOWN`. Otherwise, it returns
9
+ * `NetworkStateType.NONE`.
10
+ * @return A `Promise` that fulfils with a `NetworkState` object.
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * await Network.getNetworkStateAsync();
15
+ * // {
16
+ * // type: NetworkStateType.CELLULAR,
17
+ * // isConnected: true,
18
+ * // isInternetReachable: true,
19
+ * // }
20
+ * ```
21
+ */
22
+ export declare function getNetworkStateAsync(): Promise<NetworkState>;
23
+ /**
24
+ * Gets the device's current IPv4 address. Returns `0.0.0.0` if the IP address could not be retrieved.
25
+ *
26
+ * On web, this method uses the third-party [`ipify service`](https://www.ipify.org/) to get the
27
+ * public IP address of the current device.
28
+ * @return A `Promise` that fulfils with a `string` of the current IP address of the device's main
29
+ * network interface. Can only be IPv4 address.
30
+ *
31
+ * @example
32
+ * ```ts
33
+ * await Network.getIpAddressAsync();
34
+ * // "92.168.32.44"
35
+ * ```
36
+ */
37
+ export declare function getIpAddressAsync(): Promise<string>;
38
+ /**
39
+ * Tells if the device is in airplane mode.
40
+ * @return Returns a `Promise` that fulfils with a `boolean` value for whether the device is in
41
+ * airplane mode or not.
42
+ * @platform android
43
+ *
44
+ * @example
45
+ * ```ts
46
+ * await Network.isAirplaneModeEnabledAsync();
47
+ * // false
48
+ * ```
49
+ */
50
+ export declare function isAirplaneModeEnabledAsync(): Promise<boolean>;
51
+ /**
52
+ * Adds a listener that will fire whenever the network state changes.
53
+ *
54
+ * @param listener Callback to execute when the network state changes. The callback is provided with
55
+ * a single argument that is an object containing information about the network state.
56
+ *
57
+ * @example
58
+ * ```ts
59
+ * const subscription = addNetworkStateListener(({ type, isConnected, isInternetReachable }) => {
60
+ * console.log(`Network type: ${type}, Connected: ${isConnected}, Internet Reachable: ${isInternetReachable}`);
61
+ * });
62
+ * ```
63
+ *
64
+ * @returns A subscription object with a remove function to unregister the listener.
65
+ */
66
+ export declare function addNetworkStateListener(listener: (event: NetworkStateEvent) => void): EventSubscription;
67
+ /**
68
+ * Returns the current network state of the device. This method
69
+ * initiates a listener for network state changes and cleans up before unmounting.
70
+ *
71
+ * @example
72
+ * ```ts
73
+ * const networkState = useNetworkState();
74
+ * console.log(`Current network type: ${networkState.type}`);
75
+ * ```
76
+ *
77
+ * @return The current network state of the device, including connectivity and type.
78
+ */
79
+ export declare function useNetworkState(): NetworkState;
80
+ //# sourceMappingURL=Network.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Network.d.ts","sourceRoot":"","sources":["../src/Network.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,iBAAiB,EAAuB,MAAM,mBAAmB,CAAC;AAIhF,OAAO,KAAK,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEvE,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAK9F;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,YAAY,CAAC,CAKlE;AAGD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,MAAM,CAAC,CAKzD;AAGD;;;;;;;;;;;GAWG;AACH,wBAAsB,0BAA0B,IAAI,OAAO,CAAC,OAAO,CAAC,CAKnE;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,GAC3C,iBAAiB,CAEnB;AAGD;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,IAAI,YAAY,CAU9C"}
@@ -0,0 +1,111 @@
1
+ import { UnavailabilityError } from 'expo-modules-core';
2
+ import { useEffect, useState } from 'react';
3
+ import ExpoNetwork from './ExpoNetwork';
4
+ export { NetworkStateType } from './Network.types';
5
+ const onNetworkStateEventName = 'onNetworkStateChanged';
6
+ // @needsAudit
7
+ /**
8
+ * Gets the device's current network connection state.
9
+ *
10
+ * On web, `navigator.connection.type` is not available on browsers. So if there is an active
11
+ * network connection, the field `type` returns `NetworkStateType.UNKNOWN`. Otherwise, it returns
12
+ * `NetworkStateType.NONE`.
13
+ * @return A `Promise` that fulfils with a `NetworkState` object.
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * await Network.getNetworkStateAsync();
18
+ * // {
19
+ * // type: NetworkStateType.CELLULAR,
20
+ * // isConnected: true,
21
+ * // isInternetReachable: true,
22
+ * // }
23
+ * ```
24
+ */
25
+ export async function getNetworkStateAsync() {
26
+ if (!ExpoNetwork.getNetworkStateAsync) {
27
+ throw new UnavailabilityError('expo-network', 'getNetworkStateAsync');
28
+ }
29
+ return await ExpoNetwork.getNetworkStateAsync();
30
+ }
31
+ // @needsAudit
32
+ /**
33
+ * Gets the device's current IPv4 address. Returns `0.0.0.0` if the IP address could not be retrieved.
34
+ *
35
+ * On web, this method uses the third-party [`ipify service`](https://www.ipify.org/) to get the
36
+ * public IP address of the current device.
37
+ * @return A `Promise` that fulfils with a `string` of the current IP address of the device's main
38
+ * network interface. Can only be IPv4 address.
39
+ *
40
+ * @example
41
+ * ```ts
42
+ * await Network.getIpAddressAsync();
43
+ * // "92.168.32.44"
44
+ * ```
45
+ */
46
+ export async function getIpAddressAsync() {
47
+ if (!ExpoNetwork.getIpAddressAsync) {
48
+ throw new UnavailabilityError('expo-network', 'getIpAddressAsync');
49
+ }
50
+ return await ExpoNetwork.getIpAddressAsync();
51
+ }
52
+ // @needsAudit
53
+ /**
54
+ * Tells if the device is in airplane mode.
55
+ * @return Returns a `Promise` that fulfils with a `boolean` value for whether the device is in
56
+ * airplane mode or not.
57
+ * @platform android
58
+ *
59
+ * @example
60
+ * ```ts
61
+ * await Network.isAirplaneModeEnabledAsync();
62
+ * // false
63
+ * ```
64
+ */
65
+ export async function isAirplaneModeEnabledAsync() {
66
+ if (!ExpoNetwork.isAirplaneModeEnabledAsync) {
67
+ throw new UnavailabilityError('expo-network', 'isAirplaneModeEnabledAsync');
68
+ }
69
+ return await ExpoNetwork.isAirplaneModeEnabledAsync();
70
+ }
71
+ /**
72
+ * Adds a listener that will fire whenever the network state changes.
73
+ *
74
+ * @param listener Callback to execute when the network state changes. The callback is provided with
75
+ * a single argument that is an object containing information about the network state.
76
+ *
77
+ * @example
78
+ * ```ts
79
+ * const subscription = addNetworkStateListener(({ type, isConnected, isInternetReachable }) => {
80
+ * console.log(`Network type: ${type}, Connected: ${isConnected}, Internet Reachable: ${isInternetReachable}`);
81
+ * });
82
+ * ```
83
+ *
84
+ * @returns A subscription object with a remove function to unregister the listener.
85
+ */
86
+ export function addNetworkStateListener(listener) {
87
+ return ExpoNetwork.addListener(onNetworkStateEventName, listener);
88
+ }
89
+ // @needsAudit
90
+ /**
91
+ * Returns the current network state of the device. This method
92
+ * initiates a listener for network state changes and cleans up before unmounting.
93
+ *
94
+ * @example
95
+ * ```ts
96
+ * const networkState = useNetworkState();
97
+ * console.log(`Current network type: ${networkState.type}`);
98
+ * ```
99
+ *
100
+ * @return The current network state of the device, including connectivity and type.
101
+ */
102
+ export function useNetworkState() {
103
+ const [networkState, setNetworkState] = useState({});
104
+ useEffect(() => {
105
+ getNetworkStateAsync().then(setNetworkState);
106
+ const listener = addNetworkStateListener((networkState) => setNetworkState(networkState));
107
+ return () => listener.remove();
108
+ }, []);
109
+ return networkState;
110
+ }
111
+ //# sourceMappingURL=Network.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Network.js","sourceRoot":"","sources":["../src/Network.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0B,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAChF,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAE5C,OAAO,WAAW,MAAM,eAAe,CAAC;AAGxC,OAAO,EAA6C,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAE9F,MAAM,uBAAuB,GAAG,uBAAuB,CAAC;AAExD,cAAc;AACd;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB;IACxC,IAAI,CAAC,WAAW,CAAC,oBAAoB,EAAE,CAAC;QACtC,MAAM,IAAI,mBAAmB,CAAC,cAAc,EAAE,sBAAsB,CAAC,CAAC;IACxE,CAAC;IACD,OAAO,MAAM,WAAW,CAAC,oBAAoB,EAAE,CAAC;AAClD,CAAC;AAED,cAAc;AACd;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB;IACrC,IAAI,CAAC,WAAW,CAAC,iBAAiB,EAAE,CAAC;QACnC,MAAM,IAAI,mBAAmB,CAAC,cAAc,EAAE,mBAAmB,CAAC,CAAC;IACrE,CAAC;IACD,OAAO,MAAM,WAAW,CAAC,iBAAiB,EAAE,CAAC;AAC/C,CAAC;AAED,cAAc;AACd;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,0BAA0B;IAC9C,IAAI,CAAC,WAAW,CAAC,0BAA0B,EAAE,CAAC;QAC5C,MAAM,IAAI,mBAAmB,CAAC,cAAc,EAAE,4BAA4B,CAAC,CAAC;IAC9E,CAAC;IACD,OAAO,MAAM,WAAW,CAAC,0BAA0B,EAAE,CAAC;AACxD,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,uBAAuB,CACrC,QAA4C;IAE5C,OAAO,WAAW,CAAC,WAAW,CAAC,uBAAuB,EAAE,QAAQ,CAAC,CAAC;AACpE,CAAC;AAED,cAAc;AACd;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,eAAe;IAC7B,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,QAAQ,CAAe,EAAE,CAAC,CAAC;IAEnE,SAAS,CAAC,GAAG,EAAE;QACb,oBAAoB,EAAE,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QAC7C,MAAM,QAAQ,GAAG,uBAAuB,CAAC,CAAC,YAAY,EAAE,EAAE,CAAC,eAAe,CAAC,YAAY,CAAC,CAAC,CAAC;QAC1F,OAAO,GAAG,EAAE,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;IACjC,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,OAAO,YAAY,CAAC;AACtB,CAAC","sourcesContent":["import { type EventSubscription, UnavailabilityError } from 'expo-modules-core';\nimport { useEffect, useState } from 'react';\n\nimport ExpoNetwork from './ExpoNetwork';\nimport type { NetworkState, NetworkStateEvent } from './Network.types';\n\nexport { type NetworkState, type NetworkStateEvent, NetworkStateType } from './Network.types';\n\nconst onNetworkStateEventName = 'onNetworkStateChanged';\n\n// @needsAudit\n/**\n * Gets the device's current network connection state.\n *\n * On web, `navigator.connection.type` is not available on browsers. So if there is an active\n * network connection, the field `type` returns `NetworkStateType.UNKNOWN`. Otherwise, it returns\n * `NetworkStateType.NONE`.\n * @return A `Promise` that fulfils with a `NetworkState` object.\n *\n * @example\n * ```ts\n * await Network.getNetworkStateAsync();\n * // {\n * // type: NetworkStateType.CELLULAR,\n * // isConnected: true,\n * // isInternetReachable: true,\n * // }\n * ```\n */\nexport async function getNetworkStateAsync(): Promise<NetworkState> {\n if (!ExpoNetwork.getNetworkStateAsync) {\n throw new UnavailabilityError('expo-network', 'getNetworkStateAsync');\n }\n return await ExpoNetwork.getNetworkStateAsync();\n}\n\n// @needsAudit\n/**\n * Gets the device's current IPv4 address. Returns `0.0.0.0` if the IP address could not be retrieved.\n *\n * On web, this method uses the third-party [`ipify service`](https://www.ipify.org/) to get the\n * public IP address of the current device.\n * @return A `Promise` that fulfils with a `string` of the current IP address of the device's main\n * network interface. Can only be IPv4 address.\n *\n * @example\n * ```ts\n * await Network.getIpAddressAsync();\n * // \"92.168.32.44\"\n * ```\n */\nexport async function getIpAddressAsync(): Promise<string> {\n if (!ExpoNetwork.getIpAddressAsync) {\n throw new UnavailabilityError('expo-network', 'getIpAddressAsync');\n }\n return await ExpoNetwork.getIpAddressAsync();\n}\n\n// @needsAudit\n/**\n * Tells if the device is in airplane mode.\n * @return Returns a `Promise` that fulfils with a `boolean` value for whether the device is in\n * airplane mode or not.\n * @platform android\n *\n * @example\n * ```ts\n * await Network.isAirplaneModeEnabledAsync();\n * // false\n * ```\n */\nexport async function isAirplaneModeEnabledAsync(): Promise<boolean> {\n if (!ExpoNetwork.isAirplaneModeEnabledAsync) {\n throw new UnavailabilityError('expo-network', 'isAirplaneModeEnabledAsync');\n }\n return await ExpoNetwork.isAirplaneModeEnabledAsync();\n}\n\n/**\n * Adds a listener that will fire whenever the network state changes.\n *\n * @param listener Callback to execute when the network state changes. The callback is provided with\n * a single argument that is an object containing information about the network state.\n *\n * @example\n * ```ts\n * const subscription = addNetworkStateListener(({ type, isConnected, isInternetReachable }) => {\n * console.log(`Network type: ${type}, Connected: ${isConnected}, Internet Reachable: ${isInternetReachable}`);\n * });\n * ```\n *\n * @returns A subscription object with a remove function to unregister the listener.\n */\nexport function addNetworkStateListener(\n listener: (event: NetworkStateEvent) => void\n): EventSubscription {\n return ExpoNetwork.addListener(onNetworkStateEventName, listener);\n}\n\n// @needsAudit\n/**\n * Returns the current network state of the device. This method\n * initiates a listener for network state changes and cleans up before unmounting.\n *\n * @example\n * ```ts\n * const networkState = useNetworkState();\n * console.log(`Current network type: ${networkState.type}`);\n * ```\n *\n * @return The current network state of the device, including connectivity and type.\n */\nexport function useNetworkState(): NetworkState {\n const [networkState, setNetworkState] = useState<NetworkState>({});\n\n useEffect(() => {\n getNetworkStateAsync().then(setNetworkState);\n const listener = addNetworkStateListener((networkState) => setNetworkState(networkState));\n return () => listener.remove();\n }, []);\n\n return networkState;\n}\n"]}