class WifiConnectionViewModel( private val context: Context, wifiScanUseCase: WiFiScanUseCase, private val wifiConnectUseCase: WiFiConnectDockUseCase, private val wifiConnectLegacyUseCase: WiFiConnectDockLegacyUseCase, private val wifiDisconnectUseCase: WiFiDisconnectDockUseCase ) : ViewModel() { private val disposables = CompositeDisposable(); private var networkCallback: ConnectivityManager.NetworkCallback? = null init { disposables.add( findDockUseCase() .andThen(connect()) .doOnSuccess { lockNetwork(it) } // here, once connected, we usually communicate with the device .doOnSuccess { unlockNetwork() } .doOnTerminate { disconnectDockUseCase(networkCallback) } .doOnTerminate { networkCallback = null } .subscribe({ Timber.v("Successfully finished test.") }, { Timber.v("Test failed: ${it.message}") }) ) } private fun connect(): Single<Network> { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { connectDockUseCase() .doOnSuccess { networkCallback = it.second } .map { it.first } } else { connectDockLegacyUseCase() } } private fun lockNetwork(it: Network) { Timber.v("Binding process to network") context.bindProcessToNetwork(it) } private fun unlockNetwork() { Timber.v("Unbinding process to network") context.unbindProcessToNetwork() } override fun onCleared() { super.onCleared() disposables.clear() } } /** * If the network doesn't have internet connectivity network, requests will not be routed to it. * To direct all the network requests from your app to an external Wi-Fi device bind a network using * this function and all traffic will be routed to this network even if it has not internet connection. * * Call Context#unbindProcessToNetwork an extension function in order to unbind. */ fun Context.bindProcessToNetwork(network: Network) { val connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { connectivityManager.bindProcessToNetwork(network) } else { ConnectivityManager.setProcessDefaultNetwork(network) } } /** * To unbind direction of all network requests and let the system decide which the traffic channel * will use if there is no internet connection. */ fun Context.unbindProcessToNetwork() { val connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { connectivityManager.bindProcessToNetwork(null) } else { ConnectivityManager.setProcessDefaultNetwork(null) } }