Roll our own NetworkObserver

This commit is contained in:
topjohnwu 2020-07-10 22:54:25 -07:00
parent 6348d0a6fb
commit 52d19d3ea2
8 changed files with 223 additions and 34 deletions

View File

@ -76,7 +76,6 @@ dependencies {
implementation("com.github.topjohnwu:jtar:1.0.0") implementation("com.github.topjohnwu:jtar:1.0.0")
implementation("com.jakewharton.timber:timber:4.7.1") implementation("com.jakewharton.timber:timber:4.7.1")
implementation("com.github.pwittchen:reactivenetwork-rx2:3.0.8")
implementation("io.reactivex.rxjava2:rxjava:2.2.19") implementation("io.reactivex.rxjava2:rxjava:2.2.19")
implementation("io.reactivex.rxjava2:rxkotlin:2.4.0") implementation("io.reactivex.rxjava2:rxkotlin:2.4.0")

View File

@ -1,15 +1,15 @@
package com.topjohnwu.magisk.core package com.topjohnwu.magisk.core
import androidx.databinding.ObservableField import androidx.databinding.ObservableField
import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
import com.topjohnwu.magisk.DynAPK import com.topjohnwu.magisk.DynAPK
import com.topjohnwu.magisk.core.model.UpdateInfo import com.topjohnwu.magisk.core.model.UpdateInfo
import com.topjohnwu.magisk.core.net.NetworkObserver
import com.topjohnwu.magisk.extensions.get import com.topjohnwu.magisk.extensions.get
import com.topjohnwu.magisk.extensions.subscribeK
import com.topjohnwu.magisk.extensions.value import com.topjohnwu.magisk.extensions.value
import com.topjohnwu.magisk.utils.CachedValue import com.topjohnwu.magisk.utils.CachedValue
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
import com.topjohnwu.superuser.ShellUtils.fastCmd import com.topjohnwu.superuser.ShellUtils.fastCmd
import com.topjohnwu.superuser.internal.UiThreadHandler
import java.io.FileInputStream import java.io.FileInputStream
import java.io.IOException import java.io.IOException
@ -37,9 +37,8 @@ object Info {
val isConnected by lazy { val isConnected by lazy {
ObservableField(false).also { field -> ObservableField(false).also { field ->
ReactiveNetwork.observeNetworkConnectivity(get()) NetworkObserver.observe(get()) {
.subscribeK { UiThreadHandler.run { field.value = it.isAvailable }
field.value = it.available()
} }
} }
} }

View File

@ -0,0 +1,47 @@
package com.topjohnwu.magisk.core.net
import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkInfo
import android.net.NetworkInfo.DetailedState
import androidx.annotation.RequiresApi
// Recreate NetworkInfo with Kotlin data class
data class Connectivity(
val state : NetworkInfo.State = NetworkInfo.State.DISCONNECTED,
val detailedState : DetailedState = DetailedState.IDLE,
val type : Int = -1,
val subType : Int = -1,
val isAvailable : Boolean = false,
val isFailover : Boolean = false,
val isRoaming : Boolean = false,
val typeName : String = "NONE",
val subTypeName : String = "NONE",
val reason : String? = null,
val extraInfo : String? = null
) {
private constructor(info: NetworkInfo) : this(
info.state,
info.detailedState,
info.type,
info.subtype,
info.isAvailable,
info.isFailover,
info.isRoaming,
info.typeName,
info.subtypeName,
info.reason,
info.extraInfo
)
companion object {
fun create(manager: ConnectivityManager): Connectivity {
return manager.activeNetworkInfo?.let { Connectivity(it) } ?: Connectivity()
}
@RequiresApi(21)
fun create(manager: ConnectivityManager, network: Network): Connectivity {
return manager.getNetworkInfo(network)?.let { Connectivity(it) } ?: Connectivity()
}
}
}

View File

@ -0,0 +1,38 @@
package com.topjohnwu.magisk.core.net
import android.annotation.TargetApi
import android.content.Context
import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkCapabilities
import android.net.NetworkRequest
@TargetApi(21)
open class LollipopNetworkObserver(
context: Context,
callback: NetInfoCallback
): NetworkObserver(context, callback) {
private val networkCallback = NetCallback()
init {
val request = NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.build()
manager.registerNetworkCallback(request, networkCallback)
}
override fun stopObserving() {
manager.unregisterNetworkCallback(networkCallback)
}
private inner class NetCallback : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
emit(Connectivity.create(manager, network))
}
override fun onLost(network: Network) {
emit(Connectivity.create(manager, network))
}
}
}

View File

@ -0,0 +1,61 @@
package com.topjohnwu.magisk.core.net
import android.annotation.TargetApi
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.net.NetworkInfo
import android.os.PowerManager
import androidx.core.content.getSystemService
@TargetApi(23)
class MarshmallowNetworkObserver(
context: Context,
callback: NetInfoCallback
): LollipopNetworkObserver(context, callback) {
private val idleReceiver = IdleBroadcastReceiver()
init {
val filter = IntentFilter(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED)
context.registerReceiver(idleReceiver, filter)
}
override fun stopObserving() {
super.stopObserving()
context.unregisterReceiver(idleReceiver)
}
override fun emit(current: Connectivity) {
val typeChanged = last.type != current.type
val wasConnected = last.state == NetworkInfo.State.CONNECTED
val isDisconnected = current.state == NetworkInfo.State.DISCONNECTED
val isNotIdle = current.detailedState != NetworkInfo.DetailedState.IDLE
if (typeChanged && wasConnected && isDisconnected && isNotIdle) {
super.emit(current)
super.emit(last)
last = current
} else {
super.emit(current)
}
}
private inner class IdleBroadcastReceiver: BroadcastReceiver() {
private fun isIdleMode(context: Context): Boolean {
val packageName = context.packageName
val manager = context.getSystemService<PowerManager>()!!
val isIgnoringOptimizations = manager.isIgnoringBatteryOptimizations(packageName)
return manager.isDeviceIdleMode && !isIgnoringOptimizations
}
override fun onReceive(context: Context, intent: Intent) {
if (isIdleMode(context)) {
emit(Connectivity())
} else {
emit(Connectivity.create(manager))
}
}
}
}

View File

@ -0,0 +1,40 @@
package com.topjohnwu.magisk.core.net
import android.content.Context
import android.net.ConnectivityManager
import android.os.Build
import androidx.core.content.getSystemService
typealias NetInfoCallback = (Connectivity) -> Unit
abstract class NetworkObserver protected constructor(
context: Context,
private val callback: NetInfoCallback
) {
protected val context = context.applicationContext
protected val manager = context.getSystemService<ConnectivityManager>()!!
protected var last = Connectivity.create(manager)
init {
callback(last)
}
protected open fun emit(current: Connectivity) {
if (last != current)
callback(current)
last = current
}
protected abstract fun stopObserving()
companion object {
fun observe(context: Context, callback: NetInfoCallback): NetworkObserver {
return when (Build.VERSION.SDK_INT) {
in 23 until Int.MAX_VALUE -> MarshmallowNetworkObserver(context, callback)
in 21 until 23 -> LollipopNetworkObserver(context, callback)
else -> PreLollipopNetworkObserver(context, callback)
}
}
}
}

View File

@ -0,0 +1,32 @@
package com.topjohnwu.magisk.core.net
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.net.ConnectivityManager
class PreLollipopNetworkObserver(
context: Context,
callback: NetInfoCallback
): NetworkObserver(context, callback) {
private val receiver = ConnectivityBroadcastReceiver()
init {
val filter = IntentFilter()
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION)
context.registerReceiver(receiver, filter)
}
override fun stopObserving() {
context.unregisterReceiver(receiver)
}
private inner class ConnectivityBroadcastReceiver: BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
emit(Connectivity.create(manager))
}
}
}

View File

@ -1,27 +0,0 @@
package com.topjohnwu.magisk.extensions
import io.reactivex.Observable
import io.reactivex.Scheduler
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
fun <T> Observable<T>.applySchedulers(
subscribeOn: Scheduler = Schedulers.io(),
observeOn: Scheduler = AndroidSchedulers.mainThread()
): Observable<T> = this.subscribeOn(subscribeOn).observeOn(observeOn)
/*=== ALIASES FOR OBSERVABLES ===*/
typealias OnCompleteListener = () -> Unit
typealias OnSuccessListener<T> = (T) -> Unit
typealias OnErrorListener = (Throwable) -> Unit
/*=== ALIASES FOR OBSERVABLES ===*/
fun <T> Observable<T>.subscribeK(
onError: OnErrorListener = { it.printStackTrace() },
onComplete: OnCompleteListener = {},
onNext: OnSuccessListener<T> = {}
) = applySchedulers()
.subscribe(onNext, onError, onComplete)