Load installed modules with coroutine

This commit is contained in:
topjohnwu 2020-07-07 03:37:53 -07:00
parent ae6dd50ccd
commit 86db0cd2cd
2 changed files with 46 additions and 56 deletions

View File

@ -1,9 +1,10 @@
package com.topjohnwu.magisk.core.model.module package com.topjohnwu.magisk.core.model.module
import androidx.annotation.WorkerThread
import com.topjohnwu.magisk.core.Const import com.topjohnwu.magisk.core.Const
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
import com.topjohnwu.superuser.io.SuFile import com.topjohnwu.superuser.io.SuFile
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
class Module(path: String) : BaseModule() { class Module(path: String) : BaseModule() {
override var id: String = "" override var id: String = ""
@ -64,18 +65,13 @@ class Module(path: String) : BaseModule() {
private val PERSIST get() = "${Const.MAGISKTMP}/mirror/persist/magisk" private val PERSIST get() = "${Const.MAGISKTMP}/mirror/persist/magisk"
@WorkerThread suspend fun installed() = withContext(Dispatchers.IO) {
fun loadModules(): List<Module> { SuFile(Const.MAGISK_PATH)
val moduleList = mutableListOf<Module>() .listFiles { _, name -> name != "lost+found" && name != ".core" }
val path = SuFile(Const.MAGISK_PATH) .orEmpty()
val modules = .filter { !it.isFile }
path.listFiles { _, name -> name != "lost+found" && name != ".core" }.orEmpty() .map { Module("${Const.MAGISK_PATH}/${it.name}") }
for (file in modules) { .sortedBy { it.name.toLowerCase() }
if (file.isFile) continue
val module = Module(Const.MAGISK_PATH + "/" + file.name)
moduleList.add(module)
}
return moduleList.sortedBy { it.name.toLowerCase() }
} }
} }
} }

View File

@ -1,7 +1,6 @@
package com.topjohnwu.magisk.ui.module package com.topjohnwu.magisk.ui.module
import android.Manifest import android.Manifest
import androidx.annotation.WorkerThread
import androidx.databinding.Bindable import androidx.databinding.Bindable
import androidx.databinding.ObservableArrayList import androidx.databinding.ObservableArrayList
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
@ -27,14 +26,13 @@ import com.topjohnwu.magisk.model.events.dialog.ModuleInstallDialog
import com.topjohnwu.magisk.ui.base.* import com.topjohnwu.magisk.ui.base.*
import com.topjohnwu.magisk.utils.EndlessRecyclerScrollListener import com.topjohnwu.magisk.utils.EndlessRecyclerScrollListener
import com.topjohnwu.magisk.utils.KObservableField import com.topjohnwu.magisk.utils.KObservableField
import com.topjohnwu.superuser.internal.UiThreadHandler
import io.reactivex.Single import io.reactivex.Single
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers import kotlinx.coroutines.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import me.tatarka.bindingcollectionadapter2.collections.MergeObservableList import me.tatarka.bindingcollectionadapter2.collections.MergeObservableList
import java.lang.Runnable
import kotlin.math.roundToInt import kotlin.math.roundToInt
/* /*
@ -143,7 +141,6 @@ class ModuleViewModel(
// --- // ---
private var remoteJob: Disposable? = null
private var refetch = false private var refetch = false
private val dao private val dao
get() = when (Config.repoOrder) { get() = when (Config.repoOrder) {
@ -176,33 +173,45 @@ class ModuleViewModel(
// --- // ---
override fun refresh(): Disposable { override fun refresh(): Disposable? {
if (itemsRemote.isEmpty()) if (itemsRemote.isEmpty())
loadRemote() loadRemote()
return loadInstalled().subscribeK() loadInstalled()
return null
} }
private fun loadInstalled() = Single.fromCallable { Module.loadModules() } private suspend fun loadUpdates(installed: List<ModuleItem>) = withContext(Dispatchers.IO) {
.map { it.map { ModuleItem(it) } } installed
.map { it.loadDetail() } .mapNotNull { dao.getUpdatableRepoById(it.item.id, it.item.versionCode) }
.map { it to itemsInstalled.calculateDiff(it) } .map { RepoItem.Update(it) }
.applyViewModel(this) }
.observeOn(AndroidSchedulers.mainThread())
.map { private suspend fun List<ModuleItem>.loadDetails() = withContext(Dispatchers.IO) {
itemsInstalled.update(it.first, it.second) onEach {
it.first launch {
it.repo = dao.getRepoById(it.item.id)
}
} }
.observeOn(Schedulers.io()) }
.map { loadUpdates(it) }
.map { it to itemsUpdatable.calculateDiff(it) } private fun loadInstalled() = viewModelScope.launch {
.observeOn(AndroidSchedulers.mainThread()) state = State.LOADING
.doOnSuccess { itemsUpdatable.update(it.first, it.second) } val installed = Module.installed().map { ModuleItem(it) }
.doOnSuccess { val detailLoad = async { installed.loadDetails() }
addInstalledEmptyMessage() val updates = loadUpdates(installed)
addUpdatableEmptyMessage() val diff = withContext(Dispatchers.Default) {
updateActiveState() val i = async { itemsInstalled.calculateDiff(installed) }
val u = async { itemsUpdatable.calculateDiff(updates) }
awaitAll(i, u)
} }
.ignoreElement()!! detailLoad.await()
itemsInstalled.update(installed, diff[0])
itemsUpdatable.update(updates, diff[1])
addInstalledEmptyMessage()
addUpdatableEmptyMessage()
updateActiveState()
state = State.LOADED
}
@Synchronized @Synchronized
fun loadRemote() { fun loadRemote() {
@ -226,8 +235,8 @@ class ModuleViewModel(
loadRemoteDB(itemsRemote.size) loadRemoteDB(itemsRemote.size)
} }
isRemoteLoading = false isRemoteLoading = false
itemsRemote.addAll(repos)
refetch = false refetch = false
UiThreadHandler.handler.post { itemsRemote.addAll(repos) }
} }
} }
@ -274,13 +283,6 @@ class ModuleViewModel(
// --- // ---
@WorkerThread
private fun List<ModuleItem>.loadDetail() = onEach { module ->
Single.fromCallable { dao.getRepoById(module.item.id)!! }
.subscribeK(onError = {}) { module.repo = it }
.add()
}
private fun update(repo: Repo, progress: Int) = private fun update(repo: Repo, progress: Int) =
Single.fromCallable { itemsRemote + itemsSearch } Single.fromCallable { itemsRemote + itemsSearch }
.map { it.first { it.item.id == repo.id } } .map { it.first { it.item.id == repo.id } }
@ -303,13 +305,6 @@ class ModuleViewModel(
// --- // ---
@WorkerThread
private fun loadUpdates(installed: List<ModuleItem>) = installed
.mapNotNull { dao.getUpdatableRepoById(it.item.id, it.item.versionCode) }
.map { RepoItem.Update(it) }
// ---
fun updateActiveState() = Single.fromCallable { itemsInstalled.any { it.isModified } } fun updateActiveState() = Single.fromCallable { itemsInstalled.any { it.isModified } }
.subscribeK { sectionActive.hasButton = it } .subscribeK { sectionActive.hasButton = it }
.add() .add()
@ -326,7 +321,6 @@ class ModuleViewModel(
Single.fromCallable { itemsRemote } Single.fromCallable { itemsRemote }
.subscribeK { .subscribeK {
itemsRemote.removeAll(it) itemsRemote.removeAll(it)
remoteJob?.dispose()
loadRemote() loadRemote()
}.add() }.add()
} }