mirror of
https://github.com/topjohnwu/Magisk.git
synced 2024-11-23 18:15:30 +00:00
Updated module sections so it looks more consistent
This commit is contained in:
parent
82120cf47f
commit
495e734428
@ -88,6 +88,8 @@ class SectionTitle(
|
||||
) : ComparableRvItem<SectionTitle>() {
|
||||
override val layoutRes = R.layout.item_section_md2
|
||||
|
||||
val hasButton = KObservableField(button != 0 || icon != 0)
|
||||
|
||||
override fun onBindingBound(binding: ViewDataBinding) {
|
||||
super.onBindingBound(binding)
|
||||
val params = binding.root.layoutParams as StaggeredGridLayoutManager.LayoutParams
|
||||
@ -136,7 +138,7 @@ class ModuleItem(val item: Module) : ObservableItem<ModuleItem>(), Observable {
|
||||
|
||||
fun delete(viewModel: ModuleViewModel) {
|
||||
isRemoved = !isRemoved
|
||||
viewModel.moveToState(this)
|
||||
viewModel.moveToState()
|
||||
}
|
||||
|
||||
override fun contentSameAs(other: ModuleItem): Boolean = item.version == other.item.version
|
||||
|
@ -1,6 +1,5 @@
|
||||
package com.topjohnwu.magisk.redesign.module
|
||||
|
||||
import androidx.annotation.UiThread
|
||||
import androidx.annotation.WorkerThread
|
||||
import androidx.databinding.ViewDataBinding
|
||||
import com.topjohnwu.magisk.BR
|
||||
@ -9,6 +8,7 @@ import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.data.database.RepoByNameDao
|
||||
import com.topjohnwu.magisk.data.database.RepoByUpdatedDao
|
||||
import com.topjohnwu.magisk.databinding.ComparableRvItem
|
||||
import com.topjohnwu.magisk.extensions.reboot
|
||||
import com.topjohnwu.magisk.extensions.subscribeK
|
||||
import com.topjohnwu.magisk.model.download.RemoteFileService
|
||||
import com.topjohnwu.magisk.model.entity.internal.DownloadSubject
|
||||
@ -24,9 +24,7 @@ import com.topjohnwu.magisk.redesign.home.itemBindingOf
|
||||
import com.topjohnwu.magisk.redesign.superuser.diffListOf
|
||||
import com.topjohnwu.magisk.tasks.RepoUpdater
|
||||
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 me.tatarka.bindingcollectionadapter2.BindingRecyclerViewAdapter
|
||||
import timber.log.Timber
|
||||
@ -46,22 +44,18 @@ class ModuleViewModel(
|
||||
|
||||
companion object {
|
||||
private val sectionRemote = SectionTitle(R.string.module_section_remote)
|
||||
private val sectionActive = SectionTitle(R.string.module_section_active)
|
||||
private val sectionPending =
|
||||
SectionTitle(R.string.module_section_pending, R.string.reboot, R.drawable.ic_restart)
|
||||
private val sectionActive = SectionTitle(
|
||||
R.string.module_section_installed,
|
||||
R.string.reboot,
|
||||
R.drawable.ic_restart
|
||||
).also { it.hasButton.value = false }
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
private val itemsPending
|
||||
private val itemsInstalled
|
||||
@WorkerThread get() = items.asSequence()
|
||||
.filterIsInstance<ModuleItem>()
|
||||
.filter { it.isModified }
|
||||
.toList()
|
||||
private val itemsActive
|
||||
@WorkerThread get() = items.asSequence()
|
||||
.filterIsInstance<ModuleItem>()
|
||||
.filter { !it.isModified }
|
||||
.toList()
|
||||
private val itemsRemote
|
||||
@WorkerThread get() = items.filterIsInstance<RepoItem>()
|
||||
@ -92,29 +86,25 @@ class ModuleViewModel(
|
||||
override fun refresh() = Single.fromCallable { Module.loadModules() }
|
||||
.map { it.map { ModuleItem(it) } }
|
||||
.map { it.order() }
|
||||
.map {
|
||||
val pending = it.getValue(ModuleState.Modified)
|
||||
val active = it.getValue(ModuleState.Normal)
|
||||
build(pending = pending, active = active)
|
||||
}
|
||||
.map { build(active = it) }
|
||||
.map { it to items.calculateDiff(it) }
|
||||
.subscribeK {
|
||||
items.update(it.first, it.second)
|
||||
if (!items.contains(sectionRemote)) {
|
||||
loadRemote()
|
||||
}
|
||||
moveToState()
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun loadRemote() {
|
||||
// check for existing jobs
|
||||
val size = itemsRemote.size
|
||||
if (remoteJob?.isDisposed?.not() == true || size % 10 != 0) {
|
||||
if (remoteJob?.isDisposed?.not() == true) {
|
||||
return
|
||||
}
|
||||
remoteJob = loadRepos(offset = size)
|
||||
remoteJob = Single.fromCallable { itemsRemote.size }
|
||||
.flatMap { loadRepos(offset = it) }
|
||||
.map { it.map { RepoItem(it) } }
|
||||
.applyViewModel(this)
|
||||
.subscribeK(onError = {
|
||||
Timber.e(it)
|
||||
items.remove(LoadingItem)
|
||||
@ -125,6 +115,7 @@ class ModuleViewModel(
|
||||
}
|
||||
items.addAll(it)
|
||||
}
|
||||
// do on subscribe doesn't perform the action on main thread, so this is perfectly fine
|
||||
items.add(LoadingItem)
|
||||
}
|
||||
|
||||
@ -149,23 +140,7 @@ class ModuleViewModel(
|
||||
@WorkerThread
|
||||
private fun List<ModuleItem>.order() = asSequence()
|
||||
.sortedBy { it.item.name.toLowerCase(currentLocale) }
|
||||
.groupBy {
|
||||
when {
|
||||
it.isModified -> ModuleState.Modified
|
||||
else -> ModuleState.Normal
|
||||
}
|
||||
}
|
||||
.ensureAllStates()
|
||||
|
||||
private fun Map<ModuleState, List<ModuleItem>>.ensureAllStates(): Map<ModuleState, List<ModuleItem>> {
|
||||
val me = this as? MutableMap<ModuleState, List<ModuleItem>> ?: this.toMutableMap()
|
||||
ModuleState.values().forEach {
|
||||
if (me.none { rit -> it == rit.key }) {
|
||||
me[it] = listOf()
|
||||
}
|
||||
}
|
||||
return me
|
||||
}
|
||||
.toList()
|
||||
|
||||
private fun update(repo: Repo, progress: Int) = Single.fromCallable { itemsRemote }
|
||||
.map { it.first { it.item.id == repo.id } }
|
||||
@ -174,58 +149,15 @@ class ModuleViewModel(
|
||||
|
||||
// ---
|
||||
|
||||
@UiThread
|
||||
fun moveToState(item: ModuleItem) {
|
||||
items.removeAll { it.genericItemSameAs(item) }
|
||||
fun moveToState() = Single.fromCallable { itemsInstalled.any { it.isModified } }
|
||||
.subscribeK { sectionActive.hasButton.value = it }
|
||||
.add()
|
||||
|
||||
val isPending = item.isModified
|
||||
fun download(item: RepoItem) = ModuleInstallDialog(item.item).publish()
|
||||
|
||||
Single.fromCallable { if (isPending) itemsPending else itemsActive }
|
||||
.map { (listOf(item) + it).toMutableList() }
|
||||
.map { it.apply { sortWith(compareBy { it.item.name.toLowerCase(currentLocale) }) } }
|
||||
.map {
|
||||
if (isPending) build(pending = it)
|
||||
else build(active = it)
|
||||
}
|
||||
.map { it to items.calculateDiff(it) }
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doOnSuccess { items.update(it.first, it.second) }
|
||||
.ignoreElement()
|
||||
.andThen(cleanup())
|
||||
.subscribeK()
|
||||
}
|
||||
|
||||
fun download(item: RepoItem) {
|
||||
ModuleInstallDialog(item.item).publish()
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
private fun cleanup() = Completable
|
||||
.concat(listOf(cleanPending(), cleanActive(), cleanRemote()))
|
||||
|
||||
private fun cleanPending() = Single.fromCallable { itemsPending }
|
||||
.filter { it.isEmpty() }
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doOnSuccess { items.remove(sectionPending) }
|
||||
.ignoreElement()
|
||||
|
||||
private fun cleanActive() = Single.fromCallable { itemsActive }
|
||||
.filter { it.isEmpty() }
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doOnSuccess { items.remove(sectionActive) }
|
||||
.ignoreElement()
|
||||
|
||||
private fun cleanRemote() = Single.fromCallable { itemsRemote }
|
||||
.filter { it.isEmpty() }
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doOnSuccess { items.remove(sectionRemote) }
|
||||
.ignoreElement()
|
||||
|
||||
// ---
|
||||
|
||||
private enum class ModuleState {
|
||||
Modified, Normal
|
||||
fun sectionPressed(item: SectionTitle) = when (item) {
|
||||
sectionActive -> reboot()
|
||||
else -> Unit
|
||||
}
|
||||
|
||||
// ---
|
||||
@ -233,11 +165,9 @@ class ModuleViewModel(
|
||||
/** Callable only from worker thread because of expensive list filtering */
|
||||
@WorkerThread
|
||||
private fun build(
|
||||
pending: List<ModuleItem> = itemsPending,
|
||||
active: List<ModuleItem> = itemsActive,
|
||||
active: List<ModuleItem> = itemsInstalled,
|
||||
remote: List<RepoItem> = itemsRemote
|
||||
) = pending.prependIfNotEmpty { sectionPending } +
|
||||
active.prependIfNotEmpty { sectionActive } +
|
||||
) = active.prependIfNotEmpty { sectionActive } +
|
||||
remote.prependIfNotEmpty { sectionRemote }
|
||||
|
||||
private fun <T> List<T>.prependIfNotEmpty(item: () -> T) =
|
||||
|
@ -32,13 +32,14 @@
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="wrap_content"
|
||||
android:animateLayoutChanges="true">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/module_state_icon"
|
||||
style="?styleImageSmall"
|
||||
gone="@{!item.removed && !item.updated}"
|
||||
srcCompat="@{item.removed ? R.drawable.ic_delete_md2 : R.drawable.ic_update_md2}"
|
||||
srcCompat="@{item.removed ? R.drawable.ic_delete_md2 : (item.updated ? R.drawable.ic_update_md2 : 0)}"
|
||||
android:layout_marginStart="@dimen/l1"
|
||||
android:background="@null"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/module_version_author"
|
||||
@ -101,7 +102,7 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="@{(v) -> item.delete(viewModel)}"
|
||||
android:text="@{item.removed ? `Restore` : `Remove`}"
|
||||
android:text="@{item.removed ? @string/module_state_restore : @string/module_state_remove}"
|
||||
android:textAllCaps="false"
|
||||
app:icon="@{item.removed ? R.drawable.ic_restart : R.drawable.ic_delete_md2}"
|
||||
app:iconGravity="textEnd"
|
||||
|
@ -8,10 +8,15 @@
|
||||
name="item"
|
||||
type="com.topjohnwu.magisk.model.entity.recycler.SectionTitle" />
|
||||
|
||||
<variable
|
||||
name="viewModel"
|
||||
type="com.topjohnwu.magisk.redesign.module.ModuleViewModel" />
|
||||
|
||||
</data>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:animateLayoutChanges="true"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
@ -30,7 +35,8 @@
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/module_button"
|
||||
style="?styleButtonText"
|
||||
gone="@{item.button == 0 || item.icon == 0}"
|
||||
gone="@{!item.hasButton}"
|
||||
android:onClick="@{() -> viewModel.sectionPressed(item)}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/l1"
|
||||
|
@ -76,8 +76,10 @@
|
||||
<string name="module_safe_mode_message">You\'re in safe mode. None of user modules will work.\nThis message will disappear once safe mode is disabled.</string>
|
||||
<string name="module_version_author">%1$s by %2$s</string>
|
||||
<string name="module_section_pending">Pending changes</string>
|
||||
<string name="module_section_active">Active</string>
|
||||
<string name="module_section_installed">Installed</string>
|
||||
<string name="module_section_remote">Remote</string>
|
||||
<string name="module_state_remove">Remove</string>
|
||||
<string name="module_state_restore">Restore</string>
|
||||
|
||||
<string name="superuser_toggle_log">Toggles logging</string>
|
||||
<string name="superuser_toggle_notification">Toggles “toast” notifications</string>
|
||||
|
Loading…
Reference in New Issue
Block a user