Updated modules so they are properly arranged to respective sections
Small updates to module UI
This commit is contained in:
parent
1c8988d3f7
commit
c7cad7e4aa
@ -1,8 +1,6 @@
|
|||||||
package com.topjohnwu.magisk.model.entity.recycler
|
package com.topjohnwu.magisk.model.entity.recycler
|
||||||
|
|
||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import com.topjohnwu.magisk.R
|
import com.topjohnwu.magisk.R
|
||||||
import com.topjohnwu.magisk.databinding.ComparableRvItem
|
import com.topjohnwu.magisk.databinding.ComparableRvItem
|
||||||
@ -13,8 +11,6 @@ import com.topjohnwu.magisk.model.entity.module.Module
|
|||||||
import com.topjohnwu.magisk.model.entity.module.Repo
|
import com.topjohnwu.magisk.model.entity.module.Repo
|
||||||
import com.topjohnwu.magisk.redesign.module.ModuleViewModel
|
import com.topjohnwu.magisk.redesign.module.ModuleViewModel
|
||||||
import com.topjohnwu.magisk.utils.KObservableField
|
import com.topjohnwu.magisk.utils.KObservableField
|
||||||
import com.topjohnwu.magisk.utils.rotationTo
|
|
||||||
import com.topjohnwu.magisk.utils.setRevealed
|
|
||||||
|
|
||||||
class ModuleRvItem(val item: Module) : ComparableRvItem<ModuleRvItem>() {
|
class ModuleRvItem(val item: Module) : ComparableRvItem<ModuleRvItem>() {
|
||||||
|
|
||||||
@ -83,22 +79,29 @@ class ModuleItem(val item: Module) : ComparableRvItem<ModuleItem>() {
|
|||||||
|
|
||||||
override val layoutRes = R.layout.item_module_md2
|
override val layoutRes = R.layout.item_module_md2
|
||||||
|
|
||||||
val isExpanded = KObservableField(false)
|
|
||||||
val isEnabled = KObservableField(item.enable)
|
val isEnabled = KObservableField(item.enable)
|
||||||
|
val isRemoved = KObservableField(item.remove)
|
||||||
|
val isUpdated get() = item.updated
|
||||||
|
|
||||||
val isModified get() = item.enable != isEnabled.value
|
val isModified get() = item.remove || item.updated
|
||||||
|
|
||||||
|
init {
|
||||||
|
isEnabled.addOnPropertyChangedCallback {
|
||||||
|
item.enable = it ?: return@addOnPropertyChangedCallback
|
||||||
|
}
|
||||||
|
isRemoved.addOnPropertyChangedCallback {
|
||||||
|
item.remove = it ?: return@addOnPropertyChangedCallback
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun toggle(viewModel: ModuleViewModel) {
|
fun toggle(viewModel: ModuleViewModel) {
|
||||||
isEnabled.toggle()
|
isEnabled.toggle()
|
||||||
viewModel.moveToState(this)
|
viewModel.updateState()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun toggle(view: View) {
|
fun delete(viewModel: ModuleViewModel) {
|
||||||
isExpanded.toggle()
|
isRemoved.toggle()
|
||||||
view.rotationTo(if (isExpanded.value) 225 else 180)
|
viewModel.updateState()
|
||||||
(view.parent as ViewGroup)
|
|
||||||
.findViewById<View>(R.id.module_expand_container)
|
|
||||||
.setRevealed(isExpanded.value)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun contentSameAs(other: ModuleItem): Boolean = item.version == other.item.version
|
override fun contentSameAs(other: ModuleItem): Boolean = item.version == other.item.version
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package com.topjohnwu.magisk.redesign.module
|
package com.topjohnwu.magisk.redesign.module
|
||||||
|
|
||||||
|
import androidx.annotation.WorkerThread
|
||||||
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
import com.topjohnwu.magisk.BR
|
import com.topjohnwu.magisk.BR
|
||||||
import com.topjohnwu.magisk.extensions.subscribeK
|
import com.topjohnwu.magisk.extensions.subscribeK
|
||||||
import com.topjohnwu.magisk.model.entity.module.Module
|
import com.topjohnwu.magisk.model.entity.module.Module
|
||||||
@ -7,6 +9,8 @@ import com.topjohnwu.magisk.model.entity.recycler.ModuleItem
|
|||||||
import com.topjohnwu.magisk.redesign.compat.CompatViewModel
|
import com.topjohnwu.magisk.redesign.compat.CompatViewModel
|
||||||
import com.topjohnwu.magisk.redesign.home.itemBindingOf
|
import com.topjohnwu.magisk.redesign.home.itemBindingOf
|
||||||
import com.topjohnwu.magisk.redesign.superuser.diffListOf
|
import com.topjohnwu.magisk.redesign.superuser.diffListOf
|
||||||
|
import com.topjohnwu.magisk.utils.DiffObservableList
|
||||||
|
import com.topjohnwu.magisk.utils.currentLocale
|
||||||
import io.reactivex.Single
|
import io.reactivex.Single
|
||||||
|
|
||||||
class ModuleViewModel : CompatViewModel() {
|
class ModuleViewModel : CompatViewModel() {
|
||||||
@ -19,31 +23,65 @@ class ModuleViewModel : CompatViewModel() {
|
|||||||
|
|
||||||
override fun refresh() = Single.fromCallable { Module.loadModules() }
|
override fun refresh() = Single.fromCallable { Module.loadModules() }
|
||||||
.map { it.map { ModuleItem(it) } }
|
.map { it.map { ModuleItem(it) } }
|
||||||
.map { it to items.calculateDiff(it) }
|
.map { it.order() }
|
||||||
.subscribeK {
|
.subscribeK { it.forEach { it.update() } }
|
||||||
items.update(it.first, it.second)
|
|
||||||
items.forEach { moveToState(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun moveToState(item: ModuleItem) {
|
@WorkerThread
|
||||||
val isActive = items.indexOfFirst { it.itemSameAs(item) } != -1
|
private fun List<ModuleItem>.order() = sortedBy { it.item.name.toLowerCase(currentLocale) }
|
||||||
val isPending = itemsPending.indexOfFirst { it.itemSameAs(item) } != -1
|
.groupBy {
|
||||||
|
when {
|
||||||
when {
|
it.isModified -> ModuleState.Modified
|
||||||
isActive && isPending -> if (item.isModified) {
|
else -> ModuleState.Normal
|
||||||
items.remove(item)
|
|
||||||
} else {
|
|
||||||
itemsPending.remove(item)
|
|
||||||
}
|
|
||||||
isActive && item.isModified -> {
|
|
||||||
items.remove(item)
|
|
||||||
itemsPending.add(item)
|
|
||||||
}
|
|
||||||
isPending && !item.isModified -> {
|
|
||||||
itemsPending.remove(item)
|
|
||||||
items.add(item)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.map {
|
||||||
|
val diff = when (it.key) {
|
||||||
|
ModuleState.Modified -> itemsPending
|
||||||
|
ModuleState.Normal -> items
|
||||||
|
}.calculateDiff(it.value)
|
||||||
|
ResultEnclosure(it.key, it.value, diff)
|
||||||
|
}
|
||||||
|
.ensureAllStates()
|
||||||
|
|
||||||
|
private fun List<ResultEnclosure>.ensureAllStates(): List<ResultEnclosure> {
|
||||||
|
val me = this as? MutableList<ResultEnclosure> ?: this.toMutableList()
|
||||||
|
ModuleState.values().forEach {
|
||||||
|
if (me.none { rit -> it == rit.state }) {
|
||||||
|
me.add(ResultEnclosure(it, listOf(), null))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return me
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateState() {
|
||||||
|
// I don't feel like bothering with moving every single item to its updated state,
|
||||||
|
// so this kind of wasteful operation helps with that
|
||||||
|
Single.fromCallable { items + itemsPending }
|
||||||
|
.map { it.order() }
|
||||||
|
.subscribeK { it.forEach { it.update() } }
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum class ModuleState {
|
||||||
|
Modified, Normal
|
||||||
|
}
|
||||||
|
|
||||||
|
private data class ResultEnclosure(
|
||||||
|
val state: ModuleState,
|
||||||
|
val list: List<ModuleItem>,
|
||||||
|
val diff: DiffUtil.DiffResult?
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun ResultEnclosure.update() = when (state) {
|
||||||
|
ModuleState.Modified -> itemsPending
|
||||||
|
ModuleState.Normal -> items
|
||||||
|
}.update(list, diff)
|
||||||
|
|
||||||
|
private fun <T> DiffObservableList<T>.update(list: List<T>, diff: DiffUtil.DiffResult?) {
|
||||||
|
diff ?: let {
|
||||||
|
update(list)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
update(list, diff)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
5
app/src/main/res/drawable/ic_delete_restore.xml
Normal file
5
app/src/main/res/drawable/ic_delete_restore.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:drawable="@drawable/ic_restart" android:state_selected="true" />
|
||||||
|
<item android:drawable="@drawable/ic_delete_md2" />
|
||||||
|
</selector>
|
@ -5,6 +5,8 @@
|
|||||||
|
|
||||||
<data>
|
<data>
|
||||||
|
|
||||||
|
<import type="com.topjohnwu.magisk.R" />
|
||||||
|
|
||||||
<import type="com.topjohnwu.magisk.Config" />
|
<import type="com.topjohnwu.magisk.Config" />
|
||||||
|
|
||||||
<variable
|
<variable
|
||||||
@ -19,7 +21,7 @@
|
|||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
<com.google.android.material.card.MaterialCardView
|
||||||
style="?styleCardVariant"
|
style="?styleCardVariant"
|
||||||
isEnabled="@{!Config.coreOnly}"
|
isEnabled="@{!Config.coreOnly && !item.isRemoved()}"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:alpha="@{item.isEnabled() && !Config.coreOnly ? 1f : .5f}"
|
android:alpha="@{item.isEnabled() && !Config.coreOnly ? 1f : .5f}"
|
||||||
@ -33,28 +35,41 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:paddingBottom="@dimen/l1">
|
android:paddingBottom="@dimen/l1">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
|
android:id="@+id/module_state_icon"
|
||||||
|
style="?styleImageSmall"
|
||||||
|
gone="@{!item.isRemoved && !item.updated}"
|
||||||
|
srcCompat="@{item.isRemoved ? R.drawable.ic_delete_md2 : R.drawable.ic_update_md2}"
|
||||||
|
android:layout_marginStart="@dimen/l1"
|
||||||
|
android:background="@null"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/module_version_author"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/module_title"
|
||||||
|
tools:srcCompat="@drawable/ic_update_md2" />
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
android:id="@+id/module_title"
|
android:id="@+id/module_title"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="@dimen/l1"
|
android:layout_marginStart="@dimen/l1"
|
||||||
android:layout_marginTop="@dimen/l1"
|
android:layout_marginTop="@dimen/l1"
|
||||||
android:layout_marginEnd="32dp"
|
|
||||||
android:text="@{item.item.name}"
|
android:text="@{item.item.name}"
|
||||||
android:textAppearance="?appearanceTextBodyNormal"
|
android:textAppearance="?appearanceTextBodyNormal"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintEnd_toStartOf="@+id/module_remove"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/module_state_icon"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:text="@tools:sample/lorem" />
|
tools:text="@tools:sample/lorem" />
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
android:id="@+id/module_version_author"
|
android:id="@+id/module_version_author"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="@dimen/l1"
|
android:layout_marginStart="@dimen/l1"
|
||||||
android:layout_marginEnd="@dimen/l1"
|
|
||||||
android:text="@{@string/module_version_author(item.item.version, item.item.author)}"
|
android:text="@{@string/module_version_author(item.item.version, item.item.author)}"
|
||||||
android:textAppearance="?appearanceTextCaptionVariant"
|
android:textAppearance="?appearanceTextCaptionVariant"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/module_remove"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/module_state_icon"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/module_title"
|
app:layout_constraintTop_toBottomOf="@+id/module_title"
|
||||||
tools:text="v1 by topjohnwu" />
|
tools:text="v1 by topjohnwu" />
|
||||||
|
|
||||||
@ -71,35 +86,18 @@
|
|||||||
tools:lines="4"
|
tools:lines="4"
|
||||||
tools:text="@tools:sample/lorem/random" />
|
tools:text="@tools:sample/lorem/random" />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
|
android:id="@+id/module_remove"
|
||||||
|
style="?styleIconNormal"
|
||||||
|
isSelected="@{item.isRemoved}"
|
||||||
|
android:alpha=".5"
|
||||||
|
android:onClick="@{(v) -> item.delete(viewModel)}"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:srcCompat="@drawable/ic_delete_restore" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
<FrameLayout
|
|
||||||
android:id="@+id/module_expand_container"
|
|
||||||
revealFix="@{item.isExpanded}"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:background="?colorSurfaceVariant"
|
|
||||||
android:visibility="gone">
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
style="?styleButtonError"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:text="Remove"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
app:icon="@drawable/ic_delete_md2" />
|
|
||||||
|
|
||||||
</FrameLayout>
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatImageView
|
|
||||||
style="?styleIconNormal"
|
|
||||||
isSelected="@{item.isExpanded}"
|
|
||||||
android:layout_gravity="top|end"
|
|
||||||
android:onClick="@{(v) -> item.toggle(v)}"
|
|
||||||
android:rotation="@{item.isExpanded ? 225 : 180}"
|
|
||||||
app:srcCompat="@drawable/ic_more_collapse" />
|
|
||||||
|
|
||||||
</com.google.android.material.card.MaterialCardView>
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user