Updated the logic that refreshes the modules

Also added empty states for installed and made updatable modules visible all the time to avoid unnecessary transitions
This commit is contained in:
Viktor De Pasquale 2020-01-06 17:46:08 +01:00
parent 13262fdb18
commit ed837ba26f
4 changed files with 89 additions and 47 deletions

View File

@ -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<TextItem>() {
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)
}

View File

@ -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<ModuleItem>()
private val itemsUpdatable = diffListOf<RepoItem.Update>()
private val itemsRemote = diffListOf<RepoItem.Remote>()
val adapter = adapterOf<ComparableRvItem<*>>()
val items = diffListOf<ComparableRvItem<*>>()
val items = MergeObservableList<ComparableRvItem<*>>()
.insertItem(sectionActive)
.insertItem(itemNoneInstalled)
.insertList(itemsInstalled)
.insertItem(InstallModule)
.insertItem(sectionUpdate)
.insertItem(itemNoneUpdatable)
.insertList(itemsUpdatable)
.insertItem(sectionRemote)
.insertList(itemsRemote)!!
val itemBinding = itemBindingOf<ComparableRvItem<*>> {
it.bindExtra(BR.viewModel, this)
}
@ -94,15 +113,6 @@ class ModuleViewModel(
// ---
private val itemsInstalled
@WorkerThread get() = items.filterIsInstance<ModuleItem>()
private val itemsUpdatable
@WorkerThread get() = items.filterIsInstance<RepoItem.Update>()
private val itemsRemote
@WorkerThread get() = items.filterIsInstance<RepoItem.Remote>()
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<List<RepoItem>> = Single.fromCallable { dao.getRepos(offset) }
): Single<List<RepoItem.Remote>> = 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<ModuleItem> = itemsInstalled,
updatable: List<RepoItem.Update> = itemsUpdatable,
remote: List<RepoItem.Remote> = itemsRemote
) = (active + InstallModule)
.prependIfNotEmpty { sectionActive }
.prependIf(Config.coreOnly) { SafeModeNotice } +
updatable.prependIfNotEmpty { sectionUpdate } +
remote.prependIfNotEmpty { sectionRemote }
private fun <T> List<T>.prependIf(condition: Boolean, item: () -> T) =
if (condition) listOf(item()) + this else this
private fun <T> List<T>.prependIfNotEmpty(item: () -> T) =
prependIf(isNotEmpty(), item)
}

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="item"
type="com.topjohnwu.magisk.model.entity.recycler.TextItem" />
</data>
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="@dimen/l1"
android:text="@{item.text}"
android:textAppearance="@style/AppearanceFoundation.Tiny.Variant"
tools:text="@tools:sample/lorem/random" />
</layout>

View File

@ -85,6 +85,8 @@
<string name="module_state_remove">Remove</string>
<string name="module_state_restore">Restore</string>
<string name="module_action_install_external">Install from storage</string>
<string name="module_update_none">Your modules are up to date!</string>
<string name="module_install_none">No modules detected, try installing one from the list below.</string>
<string name="superuser_toggle_log">Logs</string>
<string name="superuser_toggle_notification">Notifications</string>