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:
parent
13262fdb18
commit
ed837ba26f
@ -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)
|
||||||
|
}
|
@ -22,9 +22,12 @@ import com.topjohnwu.magisk.redesign.compat.*
|
|||||||
import com.topjohnwu.magisk.tasks.RepoUpdater
|
import com.topjohnwu.magisk.tasks.RepoUpdater
|
||||||
import com.topjohnwu.magisk.utils.KObservableField
|
import com.topjohnwu.magisk.utils.KObservableField
|
||||||
import com.topjohnwu.magisk.utils.currentLocale
|
import com.topjohnwu.magisk.utils.currentLocale
|
||||||
|
import io.reactivex.Completable
|
||||||
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 me.tatarka.bindingcollectionadapter2.collections.MergeObservableList
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
@ -54,8 +57,24 @@ class ModuleViewModel(
|
|||||||
it.bindExtra(BR.viewModel, this)
|
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 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<*>> {
|
val itemBinding = itemBindingOf<ComparableRvItem<*>> {
|
||||||
it.bindExtra(BR.viewModel, this)
|
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 var remoteJob: Disposable? = null
|
||||||
private val dao
|
private val dao
|
||||||
get() = when (Config.repoOrder) {
|
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.map { ModuleItem(it) } }
|
||||||
.map { it.order() }
|
.map { it.order() }
|
||||||
.map { it.loadDetail() }
|
.map { it.loadDetail() }
|
||||||
.map { build(active = it, updatable = loadUpdates(it)) }
|
.map { it to itemsInstalled.calculateDiff(it) }
|
||||||
.map { it to items.calculateDiff(it) }
|
|
||||||
.applyViewModel(this)
|
.applyViewModel(this)
|
||||||
.subscribeK {
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
items.update(it.first, it.second)
|
.doOnSuccess { itemsInstalled.update(it.first, it.second) }
|
||||||
if (!items.contains(sectionRemote)) {
|
.doOnSuccess { if (itemsInstalled.isNotEmpty()) items.remove(itemNoneInstalled) }
|
||||||
loadRemote()
|
.observeOn(Schedulers.io())
|
||||||
}
|
.map { loadUpdates(it.first) }
|
||||||
updateActiveState()
|
.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() }
|
.run { downloadRepos() }
|
||||||
.applyViewModel(this, false)
|
.applyViewModel(this, false)
|
||||||
.subscribeK { refresh(); submitQuery() }
|
.subscribeK { refresh(); submitQuery() }
|
||||||
@ -156,10 +178,7 @@ class ModuleViewModel(
|
|||||||
remoteJob = Single.fromCallable { itemsRemote.size }
|
remoteJob = Single.fromCallable { itemsRemote.size }
|
||||||
.flatMap { loadRemoteInternal(offset = it) }
|
.flatMap { loadRemoteInternal(offset = it) }
|
||||||
.subscribeK(onError = Timber::e) {
|
.subscribeK(onError = Timber::e) {
|
||||||
if (!items.contains(sectionRemote)) {
|
itemsRemote.addAll(it)
|
||||||
items.add(sectionRemote)
|
|
||||||
}
|
|
||||||
items.addAll(it)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -201,7 +220,7 @@ class ModuleViewModel(
|
|||||||
private fun loadRemoteInternal(
|
private fun loadRemoteInternal(
|
||||||
offset: Int = 0,
|
offset: Int = 0,
|
||||||
downloadRepos: Boolean = offset == 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) } }
|
.map { it.map { RepoItem.Remote(it) } }
|
||||||
.flatMap {
|
.flatMap {
|
||||||
when {
|
when {
|
||||||
@ -260,7 +279,7 @@ class ModuleViewModel(
|
|||||||
updateOrderIcon()
|
updateOrderIcon()
|
||||||
Single.fromCallable { itemsRemote }
|
Single.fromCallable { itemsRemote }
|
||||||
.subscribeK {
|
.subscribeK {
|
||||||
items.removeAll(it)
|
itemsRemote.removeAll(it)
|
||||||
remoteJob?.dispose()
|
remoteJob?.dispose()
|
||||||
loadRemote()
|
loadRemote()
|
||||||
}.add()
|
}.add()
|
||||||
@ -275,24 +294,4 @@ class ModuleViewModel(
|
|||||||
OpenChangelogEvent(item.repo ?: return).publish()
|
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)
|
|
||||||
|
|
||||||
}
|
}
|
22
app/src/main/res/layout/item_text.xml
Normal file
22
app/src/main/res/layout/item_text.xml
Normal 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>
|
@ -85,6 +85,8 @@
|
|||||||
<string name="module_state_remove">Remove</string>
|
<string name="module_state_remove">Remove</string>
|
||||||
<string name="module_state_restore">Restore</string>
|
<string name="module_state_restore">Restore</string>
|
||||||
<string name="module_action_install_external">Install from storage</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_log">Logs</string>
|
||||||
<string name="superuser_toggle_notification">Notifications</string>
|
<string name="superuser_toggle_notification">Notifications</string>
|
||||||
|
Loading…
Reference in New Issue
Block a user