package com.ablsolutions.wificonnect import android.net.wifi.WifiEnterpriseConfig import android.os.Build //import android.util.Log // import android.widget.Toast import androidx.core.app.NotificationManagerCompat import com.ablsolutions.wificonnect.connector.Android10WifiConnector import com.ablsolutions.wificonnect.connector.WifiConnector import com.ablsolutions.wificonnect.connector.WifiConnectorFactory import com.ablwificonnectivity.settings.ConnectionSettings import com.ablwificonnectivity.settings.DeleteConfigurationSettings import com.ablwificonnectivity.settings.IsWifiConfiguredSettings import com.facebook.react.bridge.* import java.io.ByteArrayInputStream import java.security.cert.CertificateFactory import java.security.cert.X509Certificate import java.util.* class WifiConnectModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) { override fun getName(): String { return "WifiConnect" } @ReactMethod fun connectToWifi(args: ReadableMap, promise: Promise) { val connector: WifiConnector = WifiConnectorFactory.getWifiConnector(reactApplicationContext) val connectionSettings = getConnectionSettings(args) // Checking is notifications enabled // If not -> rejecting and returning alert for the user to enable notifications // It's actually will always return 'true' for all android versions except Android 13 and higher // (latest versions of Android will behave the same way as IOS and will ask user for permissions to receive push) // val isNotificationsEnabled = NotificationManagerCompat.from(reactApplicationContext).areNotificationsEnabled() // if (!isNotificationsEnabled) { // val text = "Push notifications permission should be accepted" // val duration = Toast.LENGTH_LONG // val toast = Toast.makeText(reactContext, text, duration) // toast.show() // promise.reject("FAILED_TO_ENABLE_NETWORK", text) // return // } if (isRunningOnEmulator()) { if (connectionSettings.ignoreNetworkErrorOnSimulator) { promise.resolve(null) } else { promise.reject("E_RUNNING_ON_SIMULATOR", "Connecting to a WiFi network on simulator is not supported.") } } else { try { connector.connect( connectionSettings = connectionSettings, successCallback = { promise.resolve(null) }, failCallback = { code: String, message: String -> promise.reject(code, message) } ) } catch (e: Exception) { promise.reject(e) } } } @ReactMethod fun deleteConfiguration(args: ReadableMap, promise: Promise) { val connector: WifiConnector = WifiConnectorFactory.getWifiConnector(reactApplicationContext) val deleteConfigurationSettings = getDeleteConfigurationSettings(args) if (isRunningOnEmulator()) { if (deleteConfigurationSettings.ignoreNetworkErrorOnSimulator) { promise.resolve(null) } else { promise.reject( "E_RUNNING_ON_SIMULATOR", "Connecting to a WiFi network on simulator is not supported." ) } } else { try { connector.deleteConfiguration( successCallback = { promise.resolve(null) }, failCallback = { code: String, message: String -> promise.reject(code, message) } ) } catch (e: Exception) { promise.reject(e) } } } @ReactMethod fun isWifiConfigured(promise: Promise) { try { val connector: WifiConnector = WifiConnectorFactory.getWifiConnector(reactApplicationContext) promise.resolve(connector.isWifiConfigured()) } catch (e: Exception) { promise.reject(e) } } @ReactMethod fun addListener(eventName: String) { if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) { Android10WifiConnector.BackgroundPermissionChecker.isListenerRegistered = true } } @ReactMethod fun removeListeners(count: Int) { if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) { Android10WifiConnector.BackgroundPermissionChecker.isListenerRegistered = false } } private fun getConnectionSettings(args: ReadableMap): ConnectionSettings { val username = args.getRequiredString("username") val password = args.getRequiredString("password") val ssid = args.getRequiredString("ssid") val isHiddenSSID = args.getBoolean("isHiddenSSID") val domainSuffix = args.getRequiredString("domainSuffix") val caCertificate = args.getRequiredString("caCertificate") val ignoreNetworkErrorOnSimulator = args.getBoolean("ignoreNetworkErrorOnSimulator") val timeSpanToWaitForPermissionDialogConfirmationInSeconds = args.getInt("timeSpanToWaitForPermissionDialogConfirmationInSeconds") val enterpriseConfig = WifiEnterpriseConfig() enterpriseConfig.password = password enterpriseConfig.identity = username enterpriseConfig.eapMethod = WifiEnterpriseConfig.Eap.PEAP enterpriseConfig.phase2Method = WifiEnterpriseConfig.Phase2.MSCHAPV2 enterpriseConfig.domainSuffixMatch = domainSuffix enterpriseConfig.caCertificate = getCertificate(caCertificate) return ConnectionSettings( enterpriseConfig, ignoreNetworkErrorOnSimulator, ssid, isHiddenSSID, timeSpanToWaitForPermissionDialogConfirmationInSeconds ) } private fun getDeleteConfigurationSettings(args: ReadableMap): DeleteConfigurationSettings { val ignoreNetworkErrorOnSimulator = args.getBoolean("ignoreNetworkErrorOnSimulator") return DeleteConfigurationSettings( ignoreNetworkErrorOnSimulator = ignoreNetworkErrorOnSimulator ) } private fun getIsWifiConfiguredSettings(args: ReadableMap): IsWifiConfiguredSettings { val ignoreNetworkErrorOnSimulator = args.getBoolean("ignoreNetworkErrorOnSimulator") return IsWifiConfiguredSettings( ignoreNetworkErrorOnSimulator = ignoreNetworkErrorOnSimulator ) } private fun getCertificate(caCertificateString: String): X509Certificate { val certificateStream = ByteArrayInputStream(caCertificateString.toByteArray()) return CertificateFactory .getInstance("X509") .generateCertificate(certificateStream) as X509Certificate } private fun ReadableMap.getRequiredString(name: String): String { return getString(name) ?: throw IllegalArgumentException("$name is required") } private fun isRunningOnEmulator(): Boolean { // https://ray-chong.medium.com/android-emulator-detection-4d0f994aab5e return (Build.MANUFACTURER.contains("Genymotion") || Build.MODEL.contains("google_sdk") || Build.MODEL.toLowerCase(Locale.ROOT).contains("droid4x") || Build.MODEL.contains("Emulator") || Build.MODEL.contains("Android SDK built for x86") || Build.HARDWARE == "goldfish" || Build.HARDWARE == "vbox86" || Build.HARDWARE.toLowerCase(Locale.ROOT).contains("nox") || Build.FINGERPRINT.startsWith("generic") || Build.PRODUCT == "sdk" || Build.PRODUCT == "google_sdk" || Build.PRODUCT == "sdk_x86" || Build.PRODUCT == "vbox86p" || Build.PRODUCT.toLowerCase(Locale.ROOT).contains("nox") || Build.BOARD.toLowerCase(Locale.ROOT).contains("nox") || (Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic"))) } }