Merge branch 'develop' into feature/fga/live_location_sharing_setup
This commit is contained in:
@@ -24,5 +24,11 @@ interface NetworkMonitor {
|
||||
/**
|
||||
* Checks if the active network is being blocked by Doze, even if it's available.
|
||||
*/
|
||||
fun isNetworkBlocked(): Boolean
|
||||
val isNetworkBlocked: StateFlow<Boolean>
|
||||
|
||||
/**
|
||||
* A flow indicating whether the app is running in an air-gapped environment.
|
||||
* An air-gapped environment is an environment that is not connected to the internet, and where the app can only communicate with a limited set of servers.
|
||||
*/
|
||||
val isInAirGappedEnvironment: StateFlow<Boolean>
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import extension.setupDependencyInjection
|
||||
import extension.testCommonDependencies
|
||||
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
@@ -23,4 +24,8 @@ dependencies {
|
||||
implementation(projects.libraries.core)
|
||||
implementation(projects.libraries.di)
|
||||
api(projects.features.networkmonitor.api)
|
||||
|
||||
testCommonDependencies(libs)
|
||||
testImplementation(projects.libraries.matrix.test)
|
||||
testImplementation(projects.features.networkmonitor.test)
|
||||
}
|
||||
|
||||
@@ -13,18 +13,21 @@ package io.element.android.features.networkmonitor.impl
|
||||
import android.content.Context
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.Network
|
||||
import android.net.NetworkCapabilities
|
||||
import android.net.NetworkRequest
|
||||
import dev.zacsweers.metro.AppScope
|
||||
import dev.zacsweers.metro.ContributesBinding
|
||||
import dev.zacsweers.metro.SingleIn
|
||||
import io.element.android.features.networkmonitor.api.NetworkMonitor
|
||||
import io.element.android.features.networkmonitor.api.NetworkStatus
|
||||
import io.element.android.libraries.core.meta.BuildMeta
|
||||
import io.element.android.libraries.di.annotations.AppCoroutineScope
|
||||
import io.element.android.libraries.di.annotations.ApplicationContext
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.channels.trySendBlocking
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.callbackFlow
|
||||
@@ -39,13 +42,13 @@ import java.util.concurrent.atomic.AtomicInteger
|
||||
@SingleIn(AppScope::class)
|
||||
class DefaultNetworkMonitor(
|
||||
@ApplicationContext context: Context,
|
||||
@AppCoroutineScope
|
||||
appCoroutineScope: CoroutineScope,
|
||||
@AppCoroutineScope appCoroutineScope: CoroutineScope,
|
||||
private val buildMeta: BuildMeta,
|
||||
) : NetworkMonitor {
|
||||
private val connectivityManager: ConnectivityManager = context.getSystemService(ConnectivityManager::class.java)
|
||||
private val blockedNetworkBlockedChecker = NetworkBlockedChecker(connectivityManager)
|
||||
|
||||
override fun isNetworkBlocked(): Boolean = blockedNetworkBlockedChecker.isNetworkBlocked()
|
||||
override val isNetworkBlocked = MutableStateFlow(NetworkBlockedChecker(connectivityManager).isNetworkBlocked())
|
||||
override val isInAirGappedEnvironment = MutableStateFlow(false)
|
||||
|
||||
override val connectivity: StateFlow<NetworkStatus> = callbackFlow {
|
||||
|
||||
@@ -63,6 +66,27 @@ class DefaultNetworkMonitor(
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBlockedStatusChanged(network: Network, blocked: Boolean) {
|
||||
Timber.d("Network ${network.networkHandle} blocked status changed: $blocked.")
|
||||
if (network.networkHandle == connectivityManager.activeNetwork?.networkHandle) {
|
||||
// If the network is blocked, it means that Doze is preventing the app from using the network, even if it's available.
|
||||
isNetworkBlocked.value = blocked
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
|
||||
if (!buildMeta.isEnterpriseBuild) {
|
||||
// The air-gapped environment detection is only relevant for the enterprise build.
|
||||
return
|
||||
}
|
||||
|
||||
if (network.networkHandle == connectivityManager.activeNetwork?.networkHandle) {
|
||||
// If the network doesn't have the NET_CAPABILITY_VALIDATED capability, it means that the network is not able to reach the internet
|
||||
// (according to Google), which is a common case in air-gapped environments.
|
||||
isInAirGappedEnvironment.value = !networkCapabilities.capabilities.contains(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAvailable(network: Network) {
|
||||
if (activeNetworksCount.incrementAndGet() > 0) {
|
||||
trySendBlocking(NetworkStatus.Connected)
|
||||
|
||||
@@ -14,10 +14,10 @@ import android.net.ConnectivityManager
|
||||
import android.net.NetworkInfo
|
||||
|
||||
/**
|
||||
* Helper to check if the active network in [ConnectivityManager] is blocked.
|
||||
* Helper to synchronously check if the active network in [ConnectivityManager] is blocked.
|
||||
*
|
||||
* This is extracted to its own class because it uses deprecated APIs (but the only ones that are reliable)
|
||||
* and we don't want to suppress deprecations everywhere.
|
||||
* and we don't want to suppress deprecations everywhere in the file this would be called.
|
||||
*/
|
||||
class NetworkBlockedChecker(
|
||||
private val connectivityManager: ConnectivityManager,
|
||||
|
||||
@@ -14,8 +14,16 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
||||
class FakeNetworkMonitor(
|
||||
initialStatus: NetworkStatus = NetworkStatus.Connected,
|
||||
private val isNetworkBlockedLambda: () -> Boolean = { false },
|
||||
) : NetworkMonitor {
|
||||
override val connectivity = MutableStateFlow(initialStatus)
|
||||
override fun isNetworkBlocked(): Boolean = isNetworkBlockedLambda()
|
||||
override val isNetworkBlocked = MutableStateFlow(false)
|
||||
override val isInAirGappedEnvironment = MutableStateFlow(false)
|
||||
|
||||
fun givenNetworkBlocked(isBlocked: Boolean) {
|
||||
isNetworkBlocked.value = isBlocked
|
||||
}
|
||||
|
||||
fun givenIsInAirGappedEnvironment(isInAirGapped: Boolean) {
|
||||
isInAirGappedEnvironment.value = isInAirGapped
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user