diff --git a/app/build.gradle.kts b/app/build.gradle.kts index f88e8891f..e487dd1a6 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -76,7 +76,6 @@ dependencies { implementation("com.github.topjohnwu:jtar:1.0.0") 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:rxkotlin:2.4.0") diff --git a/app/src/main/java/com/topjohnwu/magisk/core/Info.kt b/app/src/main/java/com/topjohnwu/magisk/core/Info.kt index b268935a9..ee86c7235 100644 --- a/app/src/main/java/com/topjohnwu/magisk/core/Info.kt +++ b/app/src/main/java/com/topjohnwu/magisk/core/Info.kt @@ -1,15 +1,15 @@ package com.topjohnwu.magisk.core import androidx.databinding.ObservableField -import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork import com.topjohnwu.magisk.DynAPK 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.subscribeK import com.topjohnwu.magisk.extensions.value import com.topjohnwu.magisk.utils.CachedValue import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.ShellUtils.fastCmd +import com.topjohnwu.superuser.internal.UiThreadHandler import java.io.FileInputStream import java.io.IOException @@ -37,10 +37,9 @@ object Info { val isConnected by lazy { ObservableField(false).also { field -> - ReactiveNetwork.observeNetworkConnectivity(get()) - .subscribeK { - field.value = it.available() - } + NetworkObserver.observe(get()) { + UiThreadHandler.run { field.value = it.isAvailable } + } } } diff --git a/app/src/main/java/com/topjohnwu/magisk/core/net/Connectivity.kt b/app/src/main/java/com/topjohnwu/magisk/core/net/Connectivity.kt new file mode 100644 index 000000000..4c7c3f956 --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/core/net/Connectivity.kt @@ -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() + } + } +} diff --git a/app/src/main/java/com/topjohnwu/magisk/core/net/LollipopNetworkObserver.kt b/app/src/main/java/com/topjohnwu/magisk/core/net/LollipopNetworkObserver.kt new file mode 100644 index 000000000..93eb11ff2 --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/core/net/LollipopNetworkObserver.kt @@ -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)) + } + } +} diff --git a/app/src/main/java/com/topjohnwu/magisk/core/net/MarshmallowNetworkObserver.kt b/app/src/main/java/com/topjohnwu/magisk/core/net/MarshmallowNetworkObserver.kt new file mode 100644 index 000000000..809959c3b --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/core/net/MarshmallowNetworkObserver.kt @@ -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()!! + 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)) + } + } + } +} diff --git a/app/src/main/java/com/topjohnwu/magisk/core/net/NetworkObserver.kt b/app/src/main/java/com/topjohnwu/magisk/core/net/NetworkObserver.kt new file mode 100644 index 000000000..1e6268fbc --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/core/net/NetworkObserver.kt @@ -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()!! + 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) + } + } + } +} diff --git a/app/src/main/java/com/topjohnwu/magisk/core/net/PreLollipopNetworkObserver.kt b/app/src/main/java/com/topjohnwu/magisk/core/net/PreLollipopNetworkObserver.kt new file mode 100644 index 000000000..888db5a83 --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/core/net/PreLollipopNetworkObserver.kt @@ -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)) + } + } + +} diff --git a/app/src/main/java/com/topjohnwu/magisk/extensions/RxJava.kt b/app/src/main/java/com/topjohnwu/magisk/extensions/RxJava.kt deleted file mode 100644 index d2f0a333d..000000000 --- a/app/src/main/java/com/topjohnwu/magisk/extensions/RxJava.kt +++ /dev/null @@ -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 Observable.applySchedulers( - subscribeOn: Scheduler = Schedulers.io(), - observeOn: Scheduler = AndroidSchedulers.mainThread() -): Observable = this.subscribeOn(subscribeOn).observeOn(observeOn) - -/*=== ALIASES FOR OBSERVABLES ===*/ - -typealias OnCompleteListener = () -> Unit -typealias OnSuccessListener = (T) -> Unit -typealias OnErrorListener = (Throwable) -> Unit - -/*=== ALIASES FOR OBSERVABLES ===*/ - -fun Observable.subscribeK( - onError: OnErrorListener = { it.printStackTrace() }, - onComplete: OnCompleteListener = {}, - onNext: OnSuccessListener = {} -) = applySchedulers() - .subscribe(onNext, onError, onComplete) -