package com.ablsolutions.wificonnect.connector import android.app.AppOpsManager import android.net.wifi.WifiManager import android.net.wifi.WifiManager.* import android.net.wifi.WifiNetworkSuggestion import android.os.Build import android.os.Handler import android.os.Looper import android.os.Process.myUid 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.connector.hasWifiPermission import com.ablwificonnectivity.settings.ConnectionSettings import com.facebook.react.bridge.ReactApplicationContext @RequiresApi(Build.VERSION_CODES.R) class Android11WifiConnector(private val reactContext: ReactApplicationContext) : WifiConnector { private val appOpsManager = reactContext.getSystemService(AppCompatActivity.APP_OPS_SERVICE) as AppOpsManager private val wifiManager = reactContext.getSystemService(AppCompatActivity.WIFI_SERVICE) as WifiManager override fun connect(connectionSettings: ConnectionSettings, successCallback: () -> Unit, failCallback: (errorCode: String, errorMessage: String) -> Unit ) { var lostWindowFocusBefore = false var permissionDialogVisible = false reactContext.addWindowFocusChangeListener { if (it && lostWindowFocusBefore) { if (appOpsManager.hasWifiPermission(reactContext)) { successCallback() } else { failCallback(userRejected, "User rejected to apply configuration") } } if (!it) { permissionDialogVisible = true lostWindowFocusBefore = true } } val suggestions = ArrayList() suggestions.add( WifiNetworkSuggestion.Builder() .setSsid(connectionSettings.ssid) .setIsHiddenSsid(connectionSettings.isHiddenSSID) .setWpa2EnterpriseConfig(connectionSettings.enterpriseConfig) .setPriority(100) .build() ) 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 -> { // Configuration saved or modified // If this is the first try to store the configuration, a permission dialog will be shown. // This case will be handled by the windows focus listener (at the top of this method). // But if this is not the first try, no permission dialog will be shown. In this case // we will wait one second to check if no dialog was shown. Handler(Looper.getMainLooper()).postDelayed({ if (!permissionDialogVisible) { successCallback() } }, 1000) } 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) } } } override fun deleteConfiguration(successCallback: () -> Unit, failCallback: (errorCode: String, errorMessage: String) -> Unit) { 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") } } } override fun isWifiConfigured(): Boolean { return wifiManager.networkSuggestions.any() } }