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

WifiNetworkSuggestion API for android 10 and above. #392

Merged
merged 10 commits into from
Sep 9, 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
88 changes: 81 additions & 7 deletions android/src/main/java/com/reactlibrary/rnwifi/RNWifiModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@
import android.util.Log;
import android.provider.Settings;
import android.os.Build;
import android.content.BroadcastReceiver;
import android.net.wifi.WifiNetworkSuggestion;


import java.util.ArrayList;
import java.util.List;

import androidx.annotation.NonNull;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
Expand All @@ -34,6 +42,7 @@
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.ReadableArray;
import com.reactlibrary.rnwifi.errors.ConnectErrorCodes;
import com.reactlibrary.rnwifi.errors.DisconnectErrorCodes;
import com.reactlibrary.rnwifi.errors.ForceWifiUsageErrorCodes;
Expand Down Expand Up @@ -226,7 +235,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 @@ -247,11 +256,11 @@ public void connectToProtectedSSID(@NonNull final String SSID, @NonNull final St
* 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
* @param promise to send success/error feedback
*/
@ReactMethod
public void connectToProtectedWifiSSID(@NonNull ReadableMap options, final Promise promise) {
if(!assertLocationPermissionGranted(promise)) {
if (!assertLocationPermissionGranted(promise)) {
return;
}

Expand All @@ -273,6 +282,71 @@ public void connectToProtectedWifiSSID(@NonNull ReadableMap options, final Promi
}


/**
* Use this to suggest a list of Wi-Fi networks on Android.
* The promise will resolve with the message 'connected' when the suggestions are added successfully.
* This method only works for Android and requires a minimum SDK version of 29.
*
* @param networkConfigs list of network configurations containing SSID, password, WPA3 flag, and app interaction flag
* @param promise to send success/error feedback
*/
@ReactMethod
public void suggestWifiNetwork(@NonNull final ReadableArray networkConfigs, final Promise promise) {
if (!assertLocationPermissionGranted(promise)) {
return;
}

List<WifiNetworkSuggestion> suggestionsList = new ArrayList<>();

for (int i = 0; i < networkConfigs.size(); i++) {
ReadableMap config = networkConfigs.getMap(i);
if (config == null) {
continue;
}

String ssid = config.getString("ssid");
String password = config.hasKey("password") ? config.getString("password") : "";
boolean isWpa3 = config.hasKey("isWpa3") && config.getBoolean("isWpa3");
boolean isAppInteractionRequired = config.hasKey("isAppInteractionRequired") && config.getBoolean("isAppInteractionRequired");

WifiNetworkSuggestion.Builder builder = new WifiNetworkSuggestion.Builder()
.setSsid(ssid);

if (isAppInteractionRequired) {
builder.setIsAppInteractionRequired(true); // Conditional (Needs location permission)
}

if (isWpa3) {
builder.setWpa3Passphrase(password);
} else if (!password.isEmpty()) {
builder.setWpa2Passphrase(password);
}

suggestionsList.add(builder.build());
}

final WifiManager wifiManager = (WifiManager) getReactApplicationContext().getSystemService(Context.WIFI_SERVICE);
final int status = wifiManager.addNetworkSuggestions(suggestionsList);

if (status != WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS) {
promise.reject(ConnectErrorCodes.unableToConnect.toString(), "Failed to add network suggestions.");
return;
}

// Optional (Wait for post connection broadcast to one of your suggestions)
final IntentFilter intentFilter = new IntentFilter(WifiManager.ACTION_WIFI_NETWORK_SUGGESTION_POST_CONNECTION);
final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (!WifiManager.ACTION_WIFI_NETWORK_SUGGESTION_POST_CONNECTION.equals(intent.getAction())) {
promise.reject("failed to connect");
return;
}
promise.resolve("connected");
}
};
getReactApplicationContext().registerReceiver(broadcastReceiver, intentFilter);
}


/**
Expand Down Expand Up @@ -446,8 +520,8 @@ public void reScanAndLoadWifiList(final Promise promise) {
boolean wifiStartScan = wifi.startScan();
Log.d(TAG, "wifi start scan: " + wifiStartScan);
if (wifiStartScan) {
final WifiScanResultReceiver wifiScanResultReceiver = new WifiScanResultReceiver(wifi, promise);
getReactApplicationContext().registerReceiver(wifiScanResultReceiver, new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
final WifiScanResultReceiver wifiScanResultReceiver = new WifiScanResultReceiver(wifi, promise);
getReactApplicationContext().registerReceiver(wifiScanResultReceiver, new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
} else {
Log.d(TAG, "Wifi scan rejected");
promise.resolve("Starting Android 9, it's only allowed to scan 4 times per 2 minuts in a foreground app.");
Expand All @@ -456,7 +530,7 @@ public void reScanAndLoadWifiList(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,timeout, promise);
connectAndroidQ(SSID, password, isHidden, timeout, promise);
} else {
connectPreAndroidQ(SSID, password, promise);
}
Expand Down Expand Up @@ -509,7 +583,7 @@ 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");
Expand Down
16 changes: 16 additions & 0 deletions lib/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@ declare module 'react-native-wifi-reborn' {
timestamp: number;
};

export type SuggestedNetworkConfig = {
ssid: string;
password?: string;
isWpa3?: boolean;
isAppInteractionRequired?: boolean;
};

export enum CONNECT_ERRORS {
/**
* Starting from iOS 11, NEHotspotConfigurationError is available
Expand Down Expand Up @@ -104,6 +111,15 @@ declare module 'react-native-wifi-reborn' {
isHidden: boolean
): Promise<void>;

/**
* Suggests a list of Wi-Fi networks on Android. Resolves with 'connected' when the suggestions are added successfully.
* Only works for Android and requires a minimum SDK version of 29.
*
* @param networkConfigs List of network configurations containing SSID, password, WPA3 flag, and app interaction flag.
* @returns Promise that resolves with 'connected' on success, or rejects with an error message on failure.
*/
export function suggestWifiNetwork(networkConfigs: SuggestedNetworkConfig[]): Promise<string>;

/**
* Connects to a WiFi network. Rejects with an error if it couldn't connect.
*
Expand Down
Loading