package com.ablsolutions.wificonnect.connector import android.net.wifi.WifiManager import android.net.wifi.WifiManager.* import android.net.wifi.WifiNetworkSuggestion import android.os.Build import androidx.annotation.RequiresApi import androidx.appcompat.app.AppCompatActivity import com.ablsolutions.wificonnect.connector.ErrorCodes.failedToRemoveNetwork import com.ablsolutions.wificonnect.connector.ErrorCodes.invalidWifiConfiguration import com.ablsolutions.wificonnect.connector.ErrorCodes.permissionDenied import com.ablsolutions.wificonnect.connector.ErrorCodes.unknownError import com.ablsolutions.wificonnect.connector.ErrorCodes.userRejected import com.ablwificonnectivity.settings.ConnectionSettings import com.facebook.react.bridge.ReactApplicationContext @RequiresApi(Build.VERSION_CODES.S) class Android12AndLaterWifiConnector(private val reactContext: ReactApplicationContext) : WifiConnector { @RequiresApi(Build.VERSION_CODES.Q) override fun connect(connectionSettings: ConnectionSettings, successCallback: () -> Unit, failCallback: (errorCode: String, errorMessage: String) -> Unit ) { val suggestions = ArrayList() suggestions.add( WifiNetworkSuggestion.Builder() .setSsid(connectionSettings.ssid) .setIsHiddenSsid(connectionSettings.isHiddenSSID) .setWpa2EnterpriseConfig(connectionSettings.enterpriseConfig) .setPriority(100) .build() ) val wifiManager = reactContext.getSystemService(AppCompatActivity.WIFI_SERVICE) as WifiManager // On first attempt to add a network the OS will show a user approval dialog and the // addNetworkSuggestion() method will immediately return "success". Do not invoke // successCallback() until the user has approved/declined. Instead, wait for the // SuggestionUserApprovalStatusListener. This listener also works if the approval // dialog is not shown the user. // see: https://issuetracker.google.com/issues/224071894?pli=1 wifiManager.addSuggestionUserApprovalStatusListener(reactContext.mainExecutor, SuggestionUserApprovalStatusListener { newStatus -> when (newStatus) { STATUS_SUGGESTION_APPROVAL_APPROVED_BY_USER -> successCallback() STATUS_SUGGESTION_APPROVAL_APPROVED_BY_CARRIER_PRIVILEGE -> successCallback() STATUS_SUGGESTION_APPROVAL_REJECTED_BY_USER -> failCallback(userRejected, "User rejected to apply configuration") } }) val result: Int try { result = wifiManager.addNetworkSuggestions(suggestions) } catch (e: SecurityException) { failCallback(permissionDenied, "Permission android:change_wifi_state denied") return } when (result) { STATUS_NETWORK_SUGGESTIONS_SUCCESS -> { // do nothing - wait for suggestion user approval status listener } STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_DUPLICATE -> { // Configuration existed (as-is) on device, nothing changed. // Usually, this should not happen on android 11 and later because in place updates are allowed successCallback() } STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_NOT_ALLOWED -> { failCallback(permissionDenied, "Configuring this network is not allowed by the operating system") } STATUS_NETWORK_SUGGESTIONS_ERROR_APP_DISALLOWED -> { failCallback(userRejected, "User rejected to apply configuration") } STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_INVALID -> { failCallback(invalidWifiConfiguration, "WiFi configuration seems to be invalid") } else -> { val errorMessage = "An unknown error occurred - internal status code = $result - see status codes starting with 'STATUS_NETWORK_SUGGESTIONS_' at https://developer.android.com/reference/android/net/wifi/WifiManager" failCallback(unknownError, errorMessage) } } } @RequiresApi(Build.VERSION_CODES.Q) override fun deleteConfiguration(successCallback: () -> Unit, failCallback: (errorCode: String, errorMessage: String) -> Unit) { val wifiManager = reactContext.getSystemService(AppCompatActivity.WIFI_SERVICE) as WifiManager val result = wifiManager.removeNetworkSuggestions(wifiManager.networkSuggestions) when (result) { STATUS_NETWORK_SUGGESTIONS_SUCCESS -> { successCallback() } STATUS_NETWORK_SUGGESTIONS_ERROR_APP_DISALLOWED -> { failCallback(permissionDenied, "Deleting this network is not allowed by the operating system") } else -> { failCallback(failedToRemoveNetwork, "Failed to remove network configuration from device") } } } @RequiresApi(Build.VERSION_CODES.R) override fun isWifiConfigured(): Boolean { val wifiManager = reactContext.getSystemService(AppCompatActivity.WIFI_SERVICE) as WifiManager return wifiManager.networkSuggestions.any() } }