Commit 86db0cd2 authored by topjohnwu's avatar topjohnwu

Load installed modules with coroutine

parent ae6dd50c
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() { ...@@ -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() }
} }
} }
} }
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 ...@@ -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( ...@@ -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( ...@@ -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 {
itemsInstalled.update(it.first, it.second)
it.first
} }
.observeOn(Schedulers.io())
.map { loadUpdates(it) } private suspend fun List<ModuleItem>.loadDetails() = withContext(Dispatchers.IO) {
.map { it to itemsUpdatable.calculateDiff(it) } onEach {
.observeOn(AndroidSchedulers.mainThread()) launch {
.doOnSuccess { itemsUpdatable.update(it.first, it.second) } it.repo = dao.getRepoById(it.item.id)
.doOnSuccess { }
}
}
private fun loadInstalled() = viewModelScope.launch {
state = State.LOADING
val installed = Module.installed().map { ModuleItem(it) }
val detailLoad = async { installed.loadDetails() }
val updates = loadUpdates(installed)
val diff = withContext(Dispatchers.Default) {
val i = async { itemsInstalled.calculateDiff(installed) }
val u = async { itemsUpdatable.calculateDiff(updates) }
awaitAll(i, u)
}
detailLoad.await()
itemsInstalled.update(installed, diff[0])
itemsUpdatable.update(updates, diff[1])
addInstalledEmptyMessage() addInstalledEmptyMessage()
addUpdatableEmptyMessage() addUpdatableEmptyMessage()
updateActiveState() updateActiveState()
state = State.LOADED
} }
.ignoreElement()!!
@Synchronized @Synchronized
fun loadRemote() { fun loadRemote() {
...@@ -226,8 +235,8 @@ class ModuleViewModel( ...@@ -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( ...@@ -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( ...@@ -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( ...@@ -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()
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment