Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ANDROID: Timeout parameter on connectToProtectedSSID method #355

Merged
merged 8 commits into from
Mar 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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!");
},
Expand Down Expand Up @@ -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.
Expand All @@ -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.
Expand All @@ -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.

Expand Down
112 changes: 81 additions & 31 deletions android/src/main/java/com/reactlibrary/rnwifi/RNWifiModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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);
Expand All @@ -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;
}

Expand All @@ -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.
*/
Expand Down Expand Up @@ -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 {
Expand All @@ -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);
}
Expand Down Expand Up @@ -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);
Expand All @@ -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);
}
Expand Down
8 changes: 7 additions & 1 deletion example/with-expo/components/ConnectToSSID.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
13 changes: 12 additions & 1 deletion ios/RNWifi.m
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
20 changes: 20 additions & 0 deletions lib/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,26 @@ declare module 'react-native-wifi-reborn' {
isHidden: boolean
): Promise<void>;

/**
* 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<void>;

export enum GET_CURRENT_WIFI_SSID_ERRRORS {
CouldNotDetectSSID = 'CouldNotDetectSSID',
}
Expand Down
Loading