Improve network state observer
parent
d224cd99bb
commit
f320f22863
@ -0,0 +1,54 @@
|
|||||||
|
package org.koitharu.kotatsu.core.os
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.net.ConnectivityManager.NetworkCallback
|
||||||
|
import android.net.Network
|
||||||
|
import android.net.NetworkRequest
|
||||||
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
import org.koitharu.kotatsu.utils.MediatorStateFlow
|
||||||
|
import org.koitharu.kotatsu.utils.ext.connectivityManager
|
||||||
|
import org.koitharu.kotatsu.utils.ext.isNetworkAvailable
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
class NetworkState @Inject constructor(
|
||||||
|
@ApplicationContext context: Context,
|
||||||
|
) : MediatorStateFlow<Boolean>() {
|
||||||
|
|
||||||
|
private val connectivityManager = context.connectivityManager
|
||||||
|
private val callback = NetworkCallbackImpl()
|
||||||
|
|
||||||
|
override val initialValue: Boolean
|
||||||
|
get() = connectivityManager.isNetworkAvailable
|
||||||
|
|
||||||
|
override fun onActive() {
|
||||||
|
val request = NetworkRequest.Builder().build()
|
||||||
|
connectivityManager.registerNetworkCallback(request, callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onInactive() {
|
||||||
|
connectivityManager.unregisterNetworkCallback(callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun awaitForConnection() {
|
||||||
|
if (value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
first { it }
|
||||||
|
}
|
||||||
|
|
||||||
|
private inner class NetworkCallbackImpl : NetworkCallback() {
|
||||||
|
|
||||||
|
override fun onAvailable(network: Network) = update()
|
||||||
|
|
||||||
|
override fun onLost(network: Network) = update()
|
||||||
|
|
||||||
|
override fun onUnavailable() = update()
|
||||||
|
|
||||||
|
private fun update() {
|
||||||
|
publishValue(connectivityManager.isNetworkAvailable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,78 +0,0 @@
|
|||||||
package org.koitharu.kotatsu.core.os
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.net.ConnectivityManager.NetworkCallback
|
|
||||||
import android.net.Network
|
|
||||||
import android.net.NetworkRequest
|
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
|
||||||
import kotlinx.coroutines.channels.ProducerScope
|
|
||||||
import kotlinx.coroutines.channels.awaitClose
|
|
||||||
import kotlinx.coroutines.channels.onSuccess
|
|
||||||
import kotlinx.coroutines.channels.trySendBlocking
|
|
||||||
import kotlinx.coroutines.flow.FlowCollector
|
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
|
||||||
import kotlinx.coroutines.flow.callbackFlow
|
|
||||||
import kotlinx.coroutines.flow.first
|
|
||||||
import org.koitharu.kotatsu.utils.ext.connectivityManager
|
|
||||||
import org.koitharu.kotatsu.utils.ext.isNetworkAvailable
|
|
||||||
import javax.inject.Inject
|
|
||||||
import javax.inject.Singleton
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
class NetworkStateObserver @Inject constructor(
|
|
||||||
@ApplicationContext context: Context,
|
|
||||||
) : StateFlow<Boolean> {
|
|
||||||
|
|
||||||
private val connectivityManager = context.connectivityManager
|
|
||||||
|
|
||||||
override val replayCache: List<Boolean>
|
|
||||||
get() = listOf(value)
|
|
||||||
|
|
||||||
override val value: Boolean
|
|
||||||
get() = connectivityManager.isNetworkAvailable
|
|
||||||
|
|
||||||
override suspend fun collect(collector: FlowCollector<Boolean>): Nothing {
|
|
||||||
collector.emit(value)
|
|
||||||
while (true) {
|
|
||||||
observeImpl().collect(collector)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun awaitForConnection(): Unit {
|
|
||||||
if (value) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
first { it }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun observeImpl() = callbackFlow<Boolean> {
|
|
||||||
val request = NetworkRequest.Builder().build()
|
|
||||||
val callback = FlowNetworkCallback(this)
|
|
||||||
connectivityManager.registerNetworkCallback(request, callback)
|
|
||||||
awaitClose {
|
|
||||||
connectivityManager.unregisterNetworkCallback(callback)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private inner class FlowNetworkCallback(
|
|
||||||
private val producerScope: ProducerScope<Boolean>,
|
|
||||||
) : NetworkCallback() {
|
|
||||||
|
|
||||||
private var prevValue = value
|
|
||||||
|
|
||||||
override fun onAvailable(network: Network) = update()
|
|
||||||
|
|
||||||
override fun onLost(network: Network) = update()
|
|
||||||
|
|
||||||
override fun onUnavailable() = update()
|
|
||||||
|
|
||||||
private fun update() {
|
|
||||||
val newValue = connectivityManager.isNetworkAvailable
|
|
||||||
if (newValue != prevValue) {
|
|
||||||
producerScope.trySendBlocking(newValue).onSuccess {
|
|
||||||
prevValue = newValue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -0,0 +1,42 @@
|
|||||||
|
package org.koitharu.kotatsu.utils
|
||||||
|
|
||||||
|
import kotlinx.coroutines.flow.FlowCollector
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
|
|
||||||
|
abstract class MediatorStateFlow<T> : StateFlow<T> {
|
||||||
|
|
||||||
|
@Suppress("LeakingThis")
|
||||||
|
private val delegate = MutableStateFlow(initialValue)
|
||||||
|
private val collectors = AtomicInteger(0)
|
||||||
|
|
||||||
|
protected abstract val initialValue: T
|
||||||
|
|
||||||
|
final override val replayCache: List<T>
|
||||||
|
get() = delegate.replayCache
|
||||||
|
|
||||||
|
final override val value: T
|
||||||
|
get() = delegate.value
|
||||||
|
|
||||||
|
final override suspend fun collect(collector: FlowCollector<T>): Nothing {
|
||||||
|
try {
|
||||||
|
if (collectors.getAndIncrement() == 0) {
|
||||||
|
onActive()
|
||||||
|
}
|
||||||
|
delegate.collect(collector)
|
||||||
|
} finally {
|
||||||
|
if (collectors.decrementAndGet() == 0) {
|
||||||
|
onInactive()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun publishValue(v: T) {
|
||||||
|
delegate.value = v
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract fun onActive()
|
||||||
|
|
||||||
|
abstract fun onInactive()
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue