diff --git a/README.md b/README.md index d1347681..b39d830c 100644 --- a/README.md +++ b/README.md @@ -142,7 +142,7 @@ The plugin provides props for extra customization. Every time you change the pro ```javascript import WifiManager from "react-native-wifi-reborn"; -WifiManager.connectToProtectedSSID(ssid, password, isWep).then( +WifiManager.connectToProtectedWifiSSID(ssid, password, isWep).then( () => { console.log("Connected successfully!"); }, @@ -171,6 +171,17 @@ _The api documentation is in progress._ The following methods work on both Android and iOS +### ```### NEW VERSION WITH OPTIONAL PARAMETERS ###``` +``` +connectToProtectedWifiSSID({ + ssid: string; + password: string | null; + isWEP?: boolean; + isHidden?: boolean; + timeout?: number + ;}): Promise +``` + ### `connectToProtectedSSID(SSID: string, password: string, isWEP: boolean, isHidden: boolean): Promise` Returns a promise that resolves when connected or rejects with the error when it couldn't connect to the wifi network. @@ -197,6 +208,10 @@ Used on iOS. If true, the network is WEP Wi-Fi; otherwise it is a WPA or WPA2 pe Type: `boolean` Used on Android. If true, the network is a hidden Wi-Fi network. +#### timeout - ```ONLY NEW VERSION``` +TypeL `number` +Used on Android to set a timeout in seconds. Default 15 seconds. + #### Errors: * iOS: * `unavailableForOSVersion`: Starting from iOS 11, NEHotspotConfigurationError is available. @@ -216,7 +231,7 @@ Used on Android. If true, the network is a hidden Wi-Fi network. * `didNotFindNetwork`: If the wifi network is not in range, the security type is unknown and WifiUtils doesn't support connecting to the network. * `authenticationErrorOccurred`: Authentication error occurred while trying to connect. The password could be incorrect or the user could have a saved network configuration with a different password! * `android10ImmediatelyDroppedConnection` : Firmware bugs on OnePlus prevent it from connecting on some firmware versions. More info: https://github.com/ThanosFisherman/WifiUtils/issues/63. - * `timeoutOccurred`: Could not connect in the timeout window. + * `timeoutOccurred`: Could not connect in the timeout window. - ```ONLY NEW VERSION``` * Both: * `unableToConnect`: When an unknown error occurred. diff --git a/android/src/main/java/com/reactlibrary/rnwifi/RNWifiModule.java b/android/src/main/java/com/reactlibrary/rnwifi/RNWifiModule.java index b84a9148..88bd0d3a 100644 --- a/android/src/main/java/com/reactlibrary/rnwifi/RNWifiModule.java +++ b/android/src/main/java/com/reactlibrary/rnwifi/RNWifiModule.java @@ -18,6 +18,8 @@ import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.net.wifi.WifiNetworkSpecifier; +import android.os.Handler; +import android.os.Looper; import android.util.Log; import android.provider.Settings; import android.os.Build; @@ -56,6 +58,8 @@ public class RNWifiModule extends ReactContextBaseJavaModule { private final ReactApplicationContext context; private static String TAG = "RNWifiModule"; + private static final int TIMEOUT_MILLIS = 15000; + RNWifiModule(ReactApplicationContext context) { super(context); @@ -208,6 +212,7 @@ public void openWifiSettings() { this.context.startActivity(intent); } + /** * Use this to connect with a wifi network. * Example: wifi.findAndConnect(ssid, password, false); @@ -221,7 +226,7 @@ public void openWifiSettings() { */ @ReactMethod public void connectToProtectedSSID(@NonNull final String SSID, @NonNull final String password, final boolean isWep, final boolean isHidden, final Promise promise) { - if(!assertLocationPermissionGranted(promise)) { + if(!assertLocationPermissionGranted(promise)) { return; } @@ -231,11 +236,45 @@ public void connectToProtectedSSID(@NonNull final String SSID, @NonNull final St } this.removeWifiNetwork(SSID, promise, () -> { - connectToWifiDirectly(SSID, password, isHidden, promise); + connectToWifiDirectly(SSID, password, isHidden, TIMEOUT_MILLIS, promise); + }); + } + + + /** + * Use this to connect with a wifi network. + * Example: wifi.findAndConnect(ssid, password, false); + * The promise will resolve with the message 'connected' when the user is connected on Android. + * + * @param options to connect with a wifi network + * @param promise to send success/error feedback + */ + @ReactMethod + public void connectToProtectedWifiSSID(@NonNull ReadableMap options, final Promise promise) { + if(!assertLocationPermissionGranted(promise)) { + return; + } + + if (!wifi.isWifiEnabled() && !wifi.setWifiEnabled(true)) { + promise.reject(ConnectErrorCodes.couldNotEnableWifi.toString(), "On Android 10, the user has to enable wifi manually."); + return; + } + + String ssid = options.getString("ssid"); + String password = options.getString("password"); + boolean isHidden = options.getBoolean("isHidden"); + int secondsTimeout = options.hasKey("timeout") ? options.getInt("timeout") * 1000 : TIMEOUT_MILLIS; + + + this.removeWifiNetwork(ssid, promise, () -> { + assert ssid != null; + connectToWifiDirectly(ssid, password, isHidden, secondsTimeout, promise); }); } + + /** * Returns if the device is currently connected to a WiFi network. */ @@ -406,7 +445,7 @@ public void reScanAndLoadWifiList(final Promise promise) { boolean wifiStartScan = wifi.startScan(); Log.d(TAG, "wifi start scan: " + wifiStartScan); - if (wifiStartScan == true) { + if (wifiStartScan) { final WifiScanResultReceiver wifiScanResultReceiver = new WifiScanResultReceiver(wifi, promise); getReactApplicationContext().registerReceiver(wifiScanResultReceiver, new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)); } else { @@ -415,9 +454,9 @@ public void reScanAndLoadWifiList(final Promise promise) { } } - private void connectToWifiDirectly(@NonNull final String SSID, @NonNull final String password, final boolean isHidden, final Promise promise) { + private void connectToWifiDirectly(@NonNull final String SSID, @NonNull final String password, final boolean isHidden, final int timeout, final Promise promise) { if (isAndroidTenOrLater()) { - connectAndroidQ(SSID, password, isHidden, promise); + connectAndroidQ(SSID, password, isHidden,timeout, promise); } else { connectPreAndroidQ(SSID, password, promise); } @@ -454,7 +493,7 @@ private void connectPreAndroidQ(@NonNull final String SSID, @NonNull final Strin } @RequiresApi(api = Build.VERSION_CODES.Q) - private void connectAndroidQ(@NonNull final String SSID, @NonNull final String password, final boolean isHidden, final Promise promise) { + private void connectAndroidQ(@NonNull final String SSID, @NonNull final String password, final boolean isHidden, final int timeout, final Promise promise) { WifiNetworkSpecifier.Builder wifiNetworkSpecifier = new WifiNetworkSpecifier.Builder() .setIsHiddenSsid(isHidden) .setSsid(SSID); @@ -470,34 +509,45 @@ private void connectAndroidQ(@NonNull final String SSID, @NonNull final String p .build(); ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + + final Handler timeoutHandler = new Handler(Looper.getMainLooper()); + final Runnable timeoutRunnable = () -> { + promise.reject(ConnectErrorCodes.timeoutOccurred.toString(), "Connection timeout"); + DisconnectCallbackHolder.getInstance().unbindProcessFromNetwork(); + DisconnectCallbackHolder.getInstance().disconnect(); + }; - ConnectivityManager.NetworkCallback networkCallback = new - ConnectivityManager.NetworkCallback() { - @Override - public void onAvailable(Network network) { - super.onAvailable(network); - DisconnectCallbackHolder.getInstance().bindProcessToNetwork(network); - connectivityManager.setNetworkPreference(ConnectivityManager.DEFAULT_NETWORK_PREFERENCE); - if (!pollForValidSSID(3, SSID)) { - promise.reject(ConnectErrorCodes.android10ImmediatelyDroppedConnection.toString(), "Firmware bugs on OnePlus prevent it from connecting on some firmware versions."); - return; - } - promise.resolve("connected"); - } + timeoutHandler.postDelayed(timeoutRunnable, timeout); - @Override - public void onUnavailable() { - super.onUnavailable(); - promise.reject(ConnectErrorCodes.didNotFindNetwork.toString(), "Network not found or network request cannot be fulfilled."); - } + ConnectivityManager.NetworkCallback networkCallback = new ConnectivityManager.NetworkCallback() { + @Override + public void onAvailable(@NonNull Network network) { + super.onAvailable(network); + timeoutHandler.removeCallbacks(timeoutRunnable); + DisconnectCallbackHolder.getInstance().bindProcessToNetwork(network); + connectivityManager.setNetworkPreference(ConnectivityManager.DEFAULT_NETWORK_PREFERENCE); + if (!pollForValidSSID(3, SSID)) { + promise.reject(ConnectErrorCodes.android10ImmediatelyDroppedConnection.toString(), "Firmware bugs on OnePlus prevent it from connecting on some firmware versions."); + return; + } + promise.resolve("connected"); + } + + @Override + public void onUnavailable() { + super.onUnavailable(); + timeoutHandler.removeCallbacks(timeoutRunnable); + promise.reject(ConnectErrorCodes.didNotFindNetwork.toString(), "Network not found or network request cannot be fulfilled."); + } + + @Override + public void onLost(@NonNull Network network) { + super.onLost(network); + DisconnectCallbackHolder.getInstance().unbindProcessFromNetwork(); + DisconnectCallbackHolder.getInstance().disconnect(); + } + }; - @Override - public void onLost(@NonNull Network network) { - super.onLost(network); - DisconnectCallbackHolder.getInstance().unbindProcessFromNetwork(); - DisconnectCallbackHolder.getInstance().disconnect(); - } - }; DisconnectCallbackHolder.getInstance().addNetworkCallback(networkCallback, connectivityManager); DisconnectCallbackHolder.getInstance().requestNetwork(nr); } diff --git a/example/with-expo/components/ConnectToSSID.tsx b/example/with-expo/components/ConnectToSSID.tsx index fae629e3..be0675b9 100644 --- a/example/with-expo/components/ConnectToSSID.tsx +++ b/example/with-expo/components/ConnectToSSID.tsx @@ -14,7 +14,13 @@ export const ConnectToSSID = () => { setError(''); setResponse(''); setIsLoading(true); - WifiManager.connectToProtectedSSID(ssid, pass, false, false) + WifiManager.connectToProtectedWifiSSID({ + ssid: ssid, + password: pass, + isWEP: false, + isHidden: false, + timeout: 10, + }) .then((r) => setResponse(JSON.stringify(r, null, 2))) .catch((e) => setError(e.toString())) .finally(() => setIsLoading(false)); diff --git a/ios/RNWifi.m b/ios/RNWifi.m index d7d83661..be3b07b4 100644 --- a/ios/RNWifi.m +++ b/ios/RNWifi.m @@ -63,7 +63,7 @@ + (BOOL)requiresMainQueueSetup RCT_EXPORT_METHOD(connectToSSID:(NSString*)ssid resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { - [self connectToProtectedSSID:ssid withPassphrase:@"" isWEP:false isHidden:false resolver:resolve rejecter:reject]; + [self connectToProtectedSSID:ssid withPassphrase:@"" isWEP:false isHidden:false timeout:nil resolver:resolve rejecter:reject]; } RCT_EXPORT_METHOD(connectToSSIDPrefix:(NSString*)ssid @@ -121,11 +121,22 @@ + (BOOL)requiresMainQueueSetup withPassphrase:(NSString*)passphrase isWEP:(BOOL)isWEP isHidden:(BOOL)isHidden + timeout:(nullable NSNumber *)timeout resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { [self connectToProtectedSSIDOnce:ssid withPassphrase:passphrase isWEP:isWEP joinOnce:false resolver:resolve rejecter:reject]; } +RCT_EXPORT_METHOD(connectToProtectedWifiSSID:(NSDictionary *)params + resolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) { + NSString *ssid = params[@"ssid"]; + NSString *passphrase = params[@"password"]; + BOOL isWEP = [params[@"isWEP"] boolValue]; + + [self connectToProtectedSSIDOnce:ssid withPassphrase:passphrase isWEP:isWEP joinOnce:false resolver:resolve rejecter:reject]; +} + RCT_EXPORT_METHOD(connectToProtectedSSIDOnce:(NSString*)ssid withPassphrase:(NSString*)passphrase isWEP:(BOOL)isWEP diff --git a/lib/types/index.d.ts b/lib/types/index.d.ts index fdbc25f3..7a99ee32 100644 --- a/lib/types/index.d.ts +++ b/lib/types/index.d.ts @@ -104,6 +104,26 @@ declare module 'react-native-wifi-reborn' { isHidden: boolean ): Promise; + /** + * Connects to a WiFi network. Rejects with an error if it couldn't connect. + * + * @param SSID Wifi name. + * @param password `null` for open networks. + * @param isWep Used on iOS. If `true`, the network is WEP Wi-Fi; otherwise it is a WPA or WPA2 personal Wi-Fi network. + * @param isHidden only for Android, use if Wi-Fi is hidden. + * @param timeout only for Android, timeout in seconds. If the connection is not established in this time, it will reject. Default is 15 seconds. + */ + type ConnectToProtectedSSIDParams = { + ssid: string; + password: string | null; + isWEP?: boolean; + isHidden?: boolean; + timeout?: number; + }; + export function connectToProtectedWifiSSID( + options: ConnectToProtectedSSIDParams + ): Promise; + export enum GET_CURRENT_WIFI_SSID_ERRRORS { CouldNotDetectSSID = 'CouldNotDetectSSID', }