From 86db0cd2cd07f17e16ebc50f436d36b727058c7c Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Tue, 7 Jul 2020 03:37:53 -0700 Subject: [PATCH] Load installed modules with coroutine --- .../magisk/core/model/module/Module.kt | 22 +++-- .../magisk/ui/module/ModuleViewModel.kt | 80 +++++++++---------- 2 files changed, 46 insertions(+), 56 deletions(-) diff --git a/app/src/main/java/com/topjohnwu/magisk/core/model/module/Module.kt b/app/src/main/java/com/topjohnwu/magisk/core/model/module/Module.kt index 823d4f9fc..840a2a162 100644 --- a/app/src/main/java/com/topjohnwu/magisk/core/model/module/Module.kt +++ b/app/src/main/java/com/topjohnwu/magisk/core/model/module/Module.kt @@ -1,9 +1,10 @@ package com.topjohnwu.magisk.core.model.module -import androidx.annotation.WorkerThread import com.topjohnwu.magisk.core.Const import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.io.SuFile +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext class Module(path: String) : BaseModule() { override var id: String = "" @@ -64,18 +65,13 @@ class Module(path: String) : BaseModule() { private val PERSIST get() = "${Const.MAGISKTMP}/mirror/persist/magisk" - @WorkerThread - fun loadModules(): List { - val moduleList = mutableListOf() - val path = SuFile(Const.MAGISK_PATH) - val modules = - path.listFiles { _, name -> name != "lost+found" && name != ".core" }.orEmpty() - for (file in modules) { - if (file.isFile) continue - val module = Module(Const.MAGISK_PATH + "/" + file.name) - moduleList.add(module) - } - return moduleList.sortedBy { it.name.toLowerCase() } + suspend fun installed() = withContext(Dispatchers.IO) { + SuFile(Const.MAGISK_PATH) + .listFiles { _, name -> name != "lost+found" && name != ".core" } + .orEmpty() + .filter { !it.isFile } + .map { Module("${Const.MAGISK_PATH}/${it.name}") } + .sortedBy { it.name.toLowerCase() } } } } diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/module/ModuleViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/ui/module/ModuleViewModel.kt index c7ec95935..7ca8a6d7d 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/module/ModuleViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/module/ModuleViewModel.kt @@ -1,7 +1,6 @@ package com.topjohnwu.magisk.ui.module import android.Manifest -import androidx.annotation.WorkerThread import androidx.databinding.Bindable import androidx.databinding.ObservableArrayList 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.utils.EndlessRecyclerScrollListener import com.topjohnwu.magisk.utils.KObservableField +import com.topjohnwu.superuser.internal.UiThreadHandler import io.reactivex.Single import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.Disposable -import io.reactivex.schedulers.Schedulers -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext +import kotlinx.coroutines.* import me.tatarka.bindingcollectionadapter2.collections.MergeObservableList +import java.lang.Runnable import kotlin.math.roundToInt /* @@ -143,7 +141,6 @@ class ModuleViewModel( // --- - private var remoteJob: Disposable? = null private var refetch = false private val dao get() = when (Config.repoOrder) { @@ -176,33 +173,45 @@ class ModuleViewModel( // --- - override fun refresh(): Disposable { + override fun refresh(): Disposable? { if (itemsRemote.isEmpty()) loadRemote() - return loadInstalled().subscribeK() + loadInstalled() + return null } - private fun loadInstalled() = Single.fromCallable { Module.loadModules() } - .map { it.map { ModuleItem(it) } } - .map { it.loadDetail() } - .map { it to itemsInstalled.calculateDiff(it) } - .applyViewModel(this) - .observeOn(AndroidSchedulers.mainThread()) - .map { - itemsInstalled.update(it.first, it.second) - it.first + private suspend fun loadUpdates(installed: List) = withContext(Dispatchers.IO) { + installed + .mapNotNull { dao.getUpdatableRepoById(it.item.id, it.item.versionCode) } + .map { RepoItem.Update(it) } + } + + private suspend fun List.loadDetails() = withContext(Dispatchers.IO) { + onEach { + launch { + it.repo = dao.getRepoById(it.item.id) + } } - .observeOn(Schedulers.io()) - .map { loadUpdates(it) } - .map { it to itemsUpdatable.calculateDiff(it) } - .observeOn(AndroidSchedulers.mainThread()) - .doOnSuccess { itemsUpdatable.update(it.first, it.second) } - .doOnSuccess { - addInstalledEmptyMessage() - addUpdatableEmptyMessage() - updateActiveState() + } + + 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) } - .ignoreElement()!! + detailLoad.await() + itemsInstalled.update(installed, diff[0]) + itemsUpdatable.update(updates, diff[1]) + addInstalledEmptyMessage() + addUpdatableEmptyMessage() + updateActiveState() + state = State.LOADED + } @Synchronized fun loadRemote() { @@ -226,8 +235,8 @@ class ModuleViewModel( loadRemoteDB(itemsRemote.size) } isRemoteLoading = false - itemsRemote.addAll(repos) refetch = false + UiThreadHandler.handler.post { itemsRemote.addAll(repos) } } } @@ -274,13 +283,6 @@ class ModuleViewModel( // --- - @WorkerThread - private fun List.loadDetail() = onEach { module -> - Single.fromCallable { dao.getRepoById(module.item.id)!! } - .subscribeK(onError = {}) { module.repo = it } - .add() - } - private fun update(repo: Repo, progress: Int) = Single.fromCallable { itemsRemote + itemsSearch } .map { it.first { it.item.id == repo.id } } @@ -303,13 +305,6 @@ class ModuleViewModel( // --- - @WorkerThread - private fun loadUpdates(installed: List) = installed - .mapNotNull { dao.getUpdatableRepoById(it.item.id, it.item.versionCode) } - .map { RepoItem.Update(it) } - - // --- - fun updateActiveState() = Single.fromCallable { itemsInstalled.any { it.isModified } } .subscribeK { sectionActive.hasButton = it } .add() @@ -326,7 +321,6 @@ class ModuleViewModel( Single.fromCallable { itemsRemote } .subscribeK { itemsRemote.removeAll(it) - remoteJob?.dispose() loadRemote() }.add() }