diff --git a/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/TextItem.kt b/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/TextItem.kt new file mode 100644 index 000000000..1ce559ab8 --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/TextItem.kt @@ -0,0 +1,19 @@ +package com.topjohnwu.magisk.model.entity.recycler + +import androidx.databinding.ViewDataBinding +import androidx.recyclerview.widget.StaggeredGridLayoutManager +import com.topjohnwu.magisk.R +import com.topjohnwu.magisk.databinding.ComparableRvItem + +class TextItem(val text: Int) : ComparableRvItem() { + override val layoutRes = R.layout.item_text + + override fun onBindingBound(binding: ViewDataBinding) { + super.onBindingBound(binding) + val params = binding.root.layoutParams as? StaggeredGridLayoutManager.LayoutParams + params?.isFullSpan = true + } + + override fun contentSameAs(other: TextItem) = text == other.text + override fun itemSameAs(other: TextItem) = contentSameAs(other) +} \ No newline at end of file diff --git a/app/src/main/java/com/topjohnwu/magisk/redesign/module/ModuleViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/redesign/module/ModuleViewModel.kt index 362802949..5c516aa3b 100644 --- a/app/src/main/java/com/topjohnwu/magisk/redesign/module/ModuleViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/redesign/module/ModuleViewModel.kt @@ -22,9 +22,12 @@ import com.topjohnwu.magisk.redesign.compat.* import com.topjohnwu.magisk.tasks.RepoUpdater import com.topjohnwu.magisk.utils.KObservableField import com.topjohnwu.magisk.utils.currentLocale +import io.reactivex.Completable import io.reactivex.Single import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.Disposable +import io.reactivex.schedulers.Schedulers +import me.tatarka.bindingcollectionadapter2.collections.MergeObservableList import timber.log.Timber import kotlin.math.roundToInt @@ -54,8 +57,24 @@ class ModuleViewModel( it.bindExtra(BR.viewModel, this) } + private val itemNoneInstalled = TextItem(R.string.module_install_none) + private val itemNoneUpdatable = TextItem(R.string.module_update_none) + + private val itemsInstalled = diffListOf() + private val itemsUpdatable = diffListOf() + private val itemsRemote = diffListOf() + val adapter = adapterOf>() - val items = diffListOf>() + val items = MergeObservableList>() + .insertItem(sectionActive) + .insertItem(itemNoneInstalled) + .insertList(itemsInstalled) + .insertItem(InstallModule) + .insertItem(sectionUpdate) + .insertItem(itemNoneUpdatable) + .insertList(itemsUpdatable) + .insertItem(sectionRemote) + .insertList(itemsRemote)!! val itemBinding = itemBindingOf> { it.bindExtra(BR.viewModel, this) } @@ -94,15 +113,6 @@ class ModuleViewModel( // --- - private val itemsInstalled - @WorkerThread get() = items.filterIsInstance() - - private val itemsUpdatable - @WorkerThread get() = items.filterIsInstance() - - private val itemsRemote - @WorkerThread get() = items.filterIsInstance() - private var remoteJob: Disposable? = null private val dao get() = when (Config.repoOrder) { @@ -126,22 +136,34 @@ class ModuleViewModel( // --- - override fun refresh() = Single.fromCallable { Module.loadModules() } + override fun refresh(): Disposable { + val installedTask = loadInstalled() + val remoteTask = if (itemsRemote.isEmpty()) { + Completable.fromAction { loadRemote() } + } else { + Completable.complete() + } + return Completable.merge(listOf(installedTask, remoteTask)).subscribeK() + } + + private fun loadInstalled() = Single.fromCallable { Module.loadModules() } .map { it.map { ModuleItem(it) } } .map { it.order() } .map { it.loadDetail() } - .map { build(active = it, updatable = loadUpdates(it)) } - .map { it to items.calculateDiff(it) } + .map { it to itemsInstalled.calculateDiff(it) } .applyViewModel(this) - .subscribeK { - items.update(it.first, it.second) - if (!items.contains(sectionRemote)) { - loadRemote() - } - updateActiveState() - } + .observeOn(AndroidSchedulers.mainThread()) + .doOnSuccess { itemsInstalled.update(it.first, it.second) } + .doOnSuccess { if (itemsInstalled.isNotEmpty()) items.remove(itemNoneInstalled) } + .observeOn(Schedulers.io()) + .map { loadUpdates(it.first) } + .observeOn(AndroidSchedulers.mainThread()) + .map { it to itemsUpdatable.calculateDiff(it) } + .doOnSuccess { itemsUpdatable.update(it.first, it.second) } + .doOnSuccess { if (itemsUpdatable.isNotEmpty()) items.remove(itemNoneUpdatable) } + .ignoreElement()!! - fun loadRemoteImplicit() = let { items.clear(); itemsSearch.clear() } + fun loadRemoteImplicit() = let { itemsRemote.clear(); itemsSearch.clear() } .run { downloadRepos() } .applyViewModel(this, false) .subscribeK { refresh(); submitQuery() } @@ -156,10 +178,7 @@ class ModuleViewModel( remoteJob = Single.fromCallable { itemsRemote.size } .flatMap { loadRemoteInternal(offset = it) } .subscribeK(onError = Timber::e) { - if (!items.contains(sectionRemote)) { - items.add(sectionRemote) - } - items.addAll(it) + itemsRemote.addAll(it) } } @@ -201,7 +220,7 @@ class ModuleViewModel( private fun loadRemoteInternal( offset: Int = 0, downloadRepos: Boolean = offset == 0 - ): Single> = Single.fromCallable { dao.getRepos(offset) } + ): Single> = Single.fromCallable { dao.getRepos(offset) } .map { it.map { RepoItem.Remote(it) } } .flatMap { when { @@ -260,7 +279,7 @@ class ModuleViewModel( updateOrderIcon() Single.fromCallable { itemsRemote } .subscribeK { - items.removeAll(it) + itemsRemote.removeAll(it) remoteJob?.dispose() loadRemote() }.add() @@ -275,24 +294,4 @@ class ModuleViewModel( OpenChangelogEvent(item.repo ?: return).publish() } - // --- - - /** Callable only from worker thread because of expensive list filtering */ - @WorkerThread - private fun build( - active: List = itemsInstalled, - updatable: List = itemsUpdatable, - remote: List = itemsRemote - ) = (active + InstallModule) - .prependIfNotEmpty { sectionActive } - .prependIf(Config.coreOnly) { SafeModeNotice } + - updatable.prependIfNotEmpty { sectionUpdate } + - remote.prependIfNotEmpty { sectionRemote } - - private fun List.prependIf(condition: Boolean, item: () -> T) = - if (condition) listOf(item()) + this else this - - private fun List.prependIfNotEmpty(item: () -> T) = - prependIf(isNotEmpty(), item) - } \ No newline at end of file diff --git a/app/src/main/res/layout/item_text.xml b/app/src/main/res/layout/item_text.xml new file mode 100644 index 000000000..7bf74922f --- /dev/null +++ b/app/src/main/res/layout/item_text.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings_md2.xml b/app/src/main/res/values/strings_md2.xml index 272950e7a..a49909a1a 100644 --- a/app/src/main/res/values/strings_md2.xml +++ b/app/src/main/res/values/strings_md2.xml @@ -85,6 +85,8 @@ Remove Restore Install from storage + Your modules are up to date! + No modules detected, try installing one from the list below. Logs Notifications