Skip to content

Instantly share code, notes, and snippets.

@gradlevv
Last active April 26, 2023 14:42
Show Gist options
  • Save gradlevv/49befe05431e19d5670a0cd6a3e33b5b to your computer and use it in GitHub Desktop.
Save gradlevv/49befe05431e19d5670a0cd6a3e33b5b to your computer and use it in GitHub Desktop.
Lifecycle-aware VPN detector
@Suppress("Deprecation")
private class LegacyVPNDetector(
private val context: Context,
connectivityManager: ConnectivityManager
) : VPNDetector(connectivityManager) {
private val filter = IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)
private val isVPNConnected: Boolean
get() = checkVPNConnectivity()
private fun checkVPNConnectivity(): Boolean {
val networkList = mutableListOf<String>()
for (networkInterface in NetworkInterface.getNetworkInterfaces()) {
if (networkInterface.isUp) {
networkList.add(networkInterface.name)
}
}
return networkList.contains("tun0")
}
override fun startDetecting(callback: (Boolean) -> Unit) {
vpnCallback = callback
context.registerReceiver(receiver, filter)
}
override fun stopDetecting() {
context.unregisterReceiver(receiver)
vpnCallback = {}
}
private val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
vpnCallback(isVPNConnected)
}
}
}
private class MarshmallowVPNDetector(connectivityManager: ConnectivityManager) :
VPNDetector(connectivityManager) {
private val networkCallback = object : ConnectivityManager.NetworkCallback() {
override fun onCapabilitiesChanged(
network: Network,
networkCapabilities: NetworkCapabilities
) {
vpnCallback(networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN))
}
}
override fun startDetecting(callback: (Boolean) -> Unit) {
vpnCallback = callback
connectivityManager.registerDefaultNetworkCallback(networkCallback)
}
override fun stopDetecting() {
connectivityManager.unregisterNetworkCallback(networkCallback)
vpnCallback = {}
}
}
package your.package.name
import androidx.lifecycle.MutableLiveData
class VPNConnectionListener constructor(context: Context) : MutableLiveData<VpnState>() {
private val vpnDetector = VPNDetector.getInstance(context.applicationContext)
override fun onActive() {
super.onActive()
vpnDetector.startDetecting(::checkState)
}
override fun onInactive() {
vpnDetector.stopDetecting()
super.onInactive()
}
private fun checkState(isConnected: Boolean) {
postValue(if (isConnected) VpnState.CONNECTED else VpnState.DISCONNECTED)
}
}
package your.package.name
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkCapabilities
import android.os.Build
import java.net.NetworkInterface
internal abstract class VPNDetector(protected val connectivityManager: ConnectivityManager) {
protected var vpnCallback: ((Boolean) -> Unit) = {}
abstract fun startDetecting(callback: (Boolean) -> Unit)
abstract fun stopDetecting()
companion object {
fun getInstance(context: Context): VPNUtils {
val connectivityManager =
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
MarshmallowVPNDetector(connectivityManager)
} else {
LegacyVPNDetector(context, connectivityManager)
}
}
}
}
enum class VpnState {
CONNECTED, DISCONNECTED
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment