Remove more code using RxJava

This commit is contained in:
topjohnwu
2020-07-10 04:19:18 -07:00
parent f7a650b9a4
commit 6348d0a6fb
31 changed files with 309 additions and 429 deletions
@@ -8,23 +8,22 @@ import com.topjohnwu.magisk.core.Const
import com.topjohnwu.magisk.core.base.BaseActivity
import com.topjohnwu.magisk.core.model.module.Repo
import com.topjohnwu.magisk.core.utils.SafetyNetHelper
import com.topjohnwu.magisk.data.repository.MagiskRepository
import com.topjohnwu.magisk.data.network.GithubRawServices
import com.topjohnwu.magisk.extensions.DynamicClassLoader
import com.topjohnwu.magisk.extensions.OnErrorListener
import com.topjohnwu.magisk.extensions.subscribeK
import com.topjohnwu.magisk.extensions.writeTo
import com.topjohnwu.magisk.ui.safetynet.SafetyNetResult
import com.topjohnwu.magisk.view.MagiskDialog
import com.topjohnwu.magisk.view.MarkDownWindow
import com.topjohnwu.superuser.Shell
import dalvik.system.DexFile
import io.reactivex.Completable
import io.reactivex.subjects.PublishSubject
import kotlinx.coroutines.*
import org.json.JSONObject
import org.koin.core.KoinComponent
import org.koin.core.inject
import timber.log.Timber
import java.io.File
import java.io.IOException
import java.lang.reflect.InvocationHandler
/**
@@ -38,11 +37,15 @@ abstract class ViewEvent {
var handled = false
}
abstract class ViewEventsWithScope: ViewEvent() {
lateinit var scope: CoroutineScope
}
class CheckSafetyNetEvent(
private val callback: (SafetyNetResult) -> Unit = {}
) : ViewEvent(), ContextExecutor, KoinComponent, SafetyNetHelper.Callback {
) : ViewEventsWithScope(), ContextExecutor, KoinComponent, SafetyNetHelper.Callback {
private val magiskRepo by inject<MagiskRepository>()
private val svc by inject<GithubRawServices>()
private lateinit var apk: File
private lateinit var dex: File
@@ -51,51 +54,72 @@ class CheckSafetyNetEvent(
apk = File("${context.filesDir.parent}/snet", "snet.jar")
dex = File(apk.parent, "snet.dex")
attest(context) {
// Download and retry
Shell.sh("rm -rf " + apk.parent).exec()
apk.parentFile?.mkdir()
download(context, true)
scope.launch {
attest(context) {
// Download and retry
withContext(Dispatchers.IO) {
Shell.sh("rm -rf " + apk.parent).exec()
apk.parentFile?.mkdir()
}
download(context, true)
}
}
}
private fun attest(context: Context, onError: OnErrorListener) {
Completable.fromAction {
val loader = DynamicClassLoader(apk)
val dex = DexFile.loadDex(apk.path, dex.path, 0)
private suspend fun attest(context: Context, onError: suspend (Exception) -> Unit) {
try {
val helper = withContext(Dispatchers.IO) {
val loader = DynamicClassLoader(apk)
val dex = DexFile.loadDex(apk.path, dex.path, 0)
// Scan through the dex and find our helper class
var helperClass: Class<*>? = null
for (className in dex.entries()) {
if (className.startsWith("x.")) {
val cls = loader.loadClass(className)
if (InvocationHandler::class.java.isAssignableFrom(cls)) {
helperClass = cls
break
// Scan through the dex and find our helper class
var helperClass: Class<*>? = null
for (className in dex.entries()) {
if (className.startsWith("x.")) {
val cls = loader.loadClass(className)
if (InvocationHandler::class.java.isAssignableFrom(cls)) {
helperClass = cls
break
}
}
}
helperClass ?: throw Exception()
val helper = helperClass
.getMethod("get", Class::class.java, Context::class.java, Any::class.java)
.invoke(null, SafetyNetHelper::class.java,
context, this@CheckSafetyNetEvent) as SafetyNetHelper
if (helper.version < Const.SNET_EXT_VER)
throw Exception()
helper
}
helperClass ?: throw Exception()
val helper = helperClass
.getMethod("get", Class::class.java, Context::class.java, Any::class.java)
.invoke(null, SafetyNetHelper::class.java, context, this) as SafetyNetHelper
if (helper.version < Const.SNET_EXT_VER)
throw Exception()
helper.attest()
}.subscribeK(onError = onError)
} catch (e: Exception) {
if (e is CancellationException)
throw e
onError(e)
}
}
@Suppress("SameParameterValue")
private fun download(context: Context, askUser: Boolean) {
fun downloadInternal() = magiskRepo.fetchSafetynet()
.map { it.byteStream().writeTo(apk) }
.subscribeK { attest(context) {
fun downloadInternal() = scope.launch {
val abort: suspend (Exception) -> Unit = {
Timber.e(it)
callback(SafetyNetResult())
} }
}
try {
withContext(Dispatchers.IO) {
svc.fetchSafetynet().byteStream().writeTo(apk)
}
attest(context, abort)
} catch (e: IOException) {
if (e is CancellationException)
throw e
abort(e)
}
}
if (!askUser) {
downloadInternal()
@@ -126,27 +150,12 @@ class ViewActionEvent(val action: BaseActivity.() -> Unit) : ViewEvent(), Activi
override fun invoke(activity: BaseActivity) = activity.run(action)
}
class OpenChangelogEvent(val item: Repo) : ViewEvent(), ContextExecutor {
class OpenChangelogEvent(val item: Repo) : ViewEventsWithScope(), ContextExecutor {
override fun invoke(context: Context) {
MarkDownWindow.show(context, null, item.readme)
}
}
class RxPermissionEvent(
val permissions: List<String>,
val callback: PublishSubject<Boolean>
) : ViewEvent(), ActivityExecutor {
override fun invoke(activity: BaseActivity) =
activity.withPermissions(*permissions.toTypedArray()) {
onSuccess {
callback.onNext(true)
}
onFailure {
callback.onNext(false)
callback.onError(SecurityException("User refused permissions"))
}
scope.launch {
MarkDownWindow.show(context, null, item::readme)
}
}
}
class PermissionEvent(
@@ -8,6 +8,9 @@ import com.topjohnwu.magisk.model.entity.internal.Configuration
import com.topjohnwu.magisk.model.entity.internal.DownloadSubject
import com.topjohnwu.magisk.view.MagiskDialog
import com.topjohnwu.magisk.view.MarkDownWindow
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
class ManagerInstallDialog : DialogEvent() {
@@ -28,7 +31,11 @@ class ManagerInstallDialog : DialogEvent() {
if (Info.remote.app.note.isEmpty()) return
applyButton(MagiskDialog.ButtonType.NEGATIVE) {
titleRes = R.string.app_changelog
onClick { MarkDownWindow.show(context, null, Info.remote.app.note) }
onClick {
GlobalScope.launch(Dispatchers.Main.immediate) {
MarkDownWindow.show(context, null, Info.remote.app.note)
}
}
}
}
}