NetworkMonitor.kt 3.61 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
package org.videolan.tools

import android.annotation.SuppressLint
import android.annotation.TargetApi
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.net.ConnectivityManager
import android.net.NetworkCapabilities
import android.os.Build
12
13
14
15
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent
import androidx.lifecycle.ProcessLifecycleOwner
16
import kotlinx.coroutines.flow.MutableStateFlow
17
18
19
20
21
22
import java.net.NetworkInterface
import java.net.SocketException

class NetworkMonitor(private val context: Context) : LifecycleObserver {
    private var registered = false
    private val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
23
    val connectionFlow = MutableStateFlow(Connection(connected = false, mobile = true, vpn = false))
Geoffrey Métais's avatar
Geoffrey Métais committed
24
    val connected : Boolean
25
        get() = connectionFlow.value.connected
Geoffrey Métais's avatar
Geoffrey Métais committed
26
    val isLan : Boolean
27
        get() = connectionFlow.value.run { connected && !mobile }
Geoffrey Métais's avatar
Geoffrey Métais committed
28
    val lanAllowed : Boolean
29
        get() = connectionFlow.value.run { connected && (!mobile || vpn) }
30
31
32
33
34
35
36
37
38
    val receiver = object : BroadcastReceiver() {
        @SuppressLint("MissingPermission")
        override fun onReceive(context: Context?, intent: Intent?) {
            when (intent?.action) {
                ConnectivityManager.CONNECTIVITY_ACTION -> {
                    val networkInfo = cm.activeNetworkInfo
                    val isConnected = networkInfo != null && networkInfo.isConnected
                    val isMobile = isConnected && networkInfo!!.type == ConnectivityManager.TYPE_MOBILE
                    val isVPN = isConnected && updateVPNStatus()
39
                    connectionFlow.value = Connection(isConnected, isMobile, isVPN)
40
41
42
43
44
                }

            }
        }
    }
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

    init {
        ProcessLifecycleOwner.get().lifecycle.addObserver(this@NetworkMonitor)
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    fun start() {
        if (registered) return
        registered = true
        val networkFilter = IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)
        context.registerReceiver(receiver, networkFilter)
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    fun stop() {
60
        if (!registered) return
61
62
63
64
        registered = false
        context.unregisterReceiver(receiver)
    }

65
66
67
68
    protected fun finalize() {
        stop()
    }

69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
    @SuppressLint("MissingPermission")
    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    private fun updateVPNStatus(): Boolean {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            for (network in cm.allNetworks) {
                val nc = cm.getNetworkCapabilities(network) ?: return false
                if (nc.hasTransport(NetworkCapabilities.TRANSPORT_VPN)) return true
            }
            return false
        } else {
            try {
                val networkInterfaces = NetworkInterface.getNetworkInterfaces()
                while (networkInterfaces.hasMoreElements()) {
                    val networkInterface = networkInterfaces.nextElement()
                    val name = networkInterface.displayName
                    if (name.startsWith("ppp") || name.startsWith("tun") || name.startsWith("tap"))
                        return true
                }
            } catch (ignored: SocketException) {}
            return false
        }
    }

Geoffrey Métais's avatar
Geoffrey Métais committed
92
    companion object : SingletonHolder<NetworkMonitor, Context>({ NetworkMonitor(it.applicationContext) })
93
}
Geoffrey Métais's avatar
Geoffrey Métais committed
94
95

class Connection(val connected: Boolean, val mobile: Boolean, val vpn: Boolean)