@@ -2,21 +2,153 @@ package com.x8bit.bitwarden.data.platform.manager.network
2
2
3
3
import android.content.Context
4
4
import android.net.ConnectivityManager
5
+ import android.net.LinkProperties
6
+ import android.net.Network
5
7
import android.net.NetworkCapabilities
8
+ import android.net.NetworkCapabilities.SIGNAL_STRENGTH_UNSPECIFIED
9
+ import android.net.NetworkRequest
10
+ import com.bitwarden.core.data.repository.util.bufferedMutableSharedFlow
11
+ import com.bitwarden.data.manager.DispatcherManager
12
+ import com.x8bit.bitwarden.data.platform.manager.model.NetworkConnection
13
+ import com.x8bit.bitwarden.data.platform.manager.model.NetworkSignalStrength
14
+ import kotlinx.coroutines.CoroutineScope
15
+ import kotlinx.coroutines.flow.MutableSharedFlow
16
+ import kotlinx.coroutines.flow.SharedFlow
17
+ import kotlinx.coroutines.flow.SharingStarted
18
+ import kotlinx.coroutines.flow.StateFlow
19
+ import kotlinx.coroutines.flow.asSharedFlow
20
+ import kotlinx.coroutines.flow.distinctUntilChanged
21
+ import kotlinx.coroutines.flow.map
22
+ import kotlinx.coroutines.flow.stateIn
6
23
7
24
/* *
8
25
* Primary implementation of [NetworkConnectionManager].
9
26
*/
10
27
class NetworkConnectionManagerImpl (
11
28
context : Context ,
29
+ dispatcherManager : DispatcherManager ,
12
30
) : NetworkConnectionManager {
31
+ private val unconfinedScope = CoroutineScope (context = dispatcherManager.unconfined)
32
+ private val networkChangeCallback = ConnectionChangeCallback ()
33
+
13
34
private val connectivityManager: ConnectivityManager = context
14
35
.applicationContext
15
36
.getSystemService(Context .CONNECTIVITY_SERVICE ) as ConnectivityManager
16
37
38
+ init {
39
+ connectivityManager.registerNetworkCallback(
40
+ NetworkRequest .Builder ()
41
+ .addCapability(NetworkCapabilities .NET_CAPABILITY_INTERNET )
42
+ .addTransportType(NetworkCapabilities .TRANSPORT_WIFI )
43
+ .addTransportType(NetworkCapabilities .TRANSPORT_CELLULAR )
44
+ .build(),
45
+ networkChangeCallback,
46
+ )
47
+ }
48
+
17
49
override val isNetworkConnected: Boolean
18
50
get() = connectivityManager
19
51
.getNetworkCapabilities(connectivityManager.activeNetwork)
20
52
?.hasCapability(NetworkCapabilities .NET_CAPABILITY_INTERNET )
21
53
? : false
54
+
55
+ override val isNetworkConnectedFlow: StateFlow <Boolean > =
56
+ networkChangeCallback
57
+ .connectionChangeFlow
58
+ .map { isNetworkConnected }
59
+ .distinctUntilChanged()
60
+ .stateIn(
61
+ scope = unconfinedScope,
62
+ started = SharingStarted .Eagerly ,
63
+ initialValue = isNetworkConnected,
64
+ )
65
+
66
+ override val networkConnection: NetworkConnection
67
+ get() = connectivityManager
68
+ .getNetworkCapabilities(connectivityManager.activeNetwork)
69
+ .networkConnection
70
+
71
+ override val networkConnectionFlow: StateFlow <NetworkConnection > = networkChangeCallback
72
+ .connectionChangeFlow
73
+ .map { _ -> networkConnection }
74
+ .distinctUntilChanged()
75
+ .stateIn(
76
+ scope = unconfinedScope,
77
+ started = SharingStarted .Eagerly ,
78
+ initialValue = networkConnection,
79
+ )
80
+
81
+ /* *
82
+ * A callback used to monitor the connection of a [Network].
83
+ */
84
+ private class ConnectionChangeCallback : ConnectivityManager .NetworkCallback () {
85
+ private val mutableConnectionState: MutableSharedFlow <Unit > = bufferedMutableSharedFlow()
86
+
87
+ /* *
88
+ * A [StateFlow] that emits when the connection state to a network changes.
89
+ */
90
+ val connectionChangeFlow: SharedFlow <Unit > = mutableConnectionState.asSharedFlow()
91
+
92
+ override fun onCapabilitiesChanged (
93
+ network : Network ,
94
+ networkCapabilities : NetworkCapabilities ,
95
+ ) {
96
+ super .onCapabilitiesChanged(network, networkCapabilities)
97
+ mutableConnectionState.tryEmit(Unit )
98
+ }
99
+
100
+ override fun onLinkPropertiesChanged (network : Network , linkProperties : LinkProperties ) {
101
+ super .onLinkPropertiesChanged(network, linkProperties)
102
+ mutableConnectionState.tryEmit(Unit )
103
+ }
104
+
105
+ override fun onAvailable (network : Network ) {
106
+ super .onAvailable(network)
107
+ mutableConnectionState.tryEmit(Unit )
108
+ }
109
+
110
+ override fun onLost (network : Network ) {
111
+ super .onLost(network)
112
+ mutableConnectionState.tryEmit(Unit )
113
+ }
114
+ }
22
115
}
116
+
117
+ /* *
118
+ * Converts the [NetworkCapabilities] to a [NetworkConnection].
119
+ */
120
+ private val NetworkCapabilities ?.networkConnection: NetworkConnection
121
+ get() = this
122
+ ?.let {
123
+ if (it.hasTransport(NetworkCapabilities .TRANSPORT_WIFI )) {
124
+ NetworkConnection .Wifi (it.networkStrength)
125
+ } else if (it.hasTransport(NetworkCapabilities .TRANSPORT_CELLULAR )) {
126
+ NetworkConnection .Cellular
127
+ } else {
128
+ NetworkConnection .Other
129
+ }
130
+ }
131
+ ? : NetworkConnection .None
132
+
133
+ /* *
134
+ * Converts an integer value to an enum signal strength based on the RSSI standard.
135
+ *
136
+ * * -50 dBm: Excellent signal
137
+ * * -60 to -75 dBm: Good signal
138
+ * * -76 to -90 dBm: Fair signal
139
+ * * -91 to -110 dBm: Weak signal
140
+ * * -110 dBm and below: No signal
141
+ */
142
+ @Suppress(" MagicNumber" )
143
+ private val NetworkCapabilities .networkStrength: NetworkSignalStrength
144
+ get() {
145
+ val strength = this .signalStrength
146
+ return when {
147
+ (strength <= SIGNAL_STRENGTH_UNSPECIFIED ) -> NetworkSignalStrength .UNKNOWN
148
+ (strength <= - 110 ) -> NetworkSignalStrength .NONE
149
+ (strength <= - 91 ) -> NetworkSignalStrength .WEAK
150
+ (strength <= - 76 ) -> NetworkSignalStrength .FAIR
151
+ (strength <= - 60 ) -> NetworkSignalStrength .GOOD
152
+ else -> NetworkSignalStrength .EXCELLENT
153
+ }
154
+ }
0 commit comments