Magisk/app/src/main/java/com/topjohnwu/magisk/ui/hide/HideRvItems.kt

116 lines
3.5 KiB
Kotlin

package com.topjohnwu.magisk.ui.hide
import android.view.View
import android.view.ViewGroup
import androidx.databinding.Bindable
import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.databinding.ObservableItem
import com.topjohnwu.magisk.ktx.startAnimations
import com.topjohnwu.magisk.utils.addOnPropertyChangedCallback
import com.topjohnwu.magisk.utils.set
import com.topjohnwu.superuser.Shell
import kotlin.math.roundToInt
class HideRvItem(
val info: HideAppInfo
) : ObservableItem<HideRvItem>(), Comparable<HideRvItem> {
override val layoutRes get() = R.layout.item_hide_md2
val processes = info.processes.map { HideProcessRvItem(it) }
@get:Bindable
var isExpanded = false
set(value) = set(value, field, { field = it }, BR.expanded)
var itemsChecked = 0
set(value) = set(value, field, { field = it }, BR.checkedPercent)
@get:Bindable
val checkedPercent get() = (itemsChecked.toFloat() / processes.size * 100).roundToInt()
private var state: Boolean? = false
set(value) = set(value, field, { field = it }, BR.hiddenState)
@get:Bindable
var hiddenState: Boolean?
get() = state
set(value) = set(value, state, { state = it }, BR.hiddenState) {
if (value == true) {
processes
.filterNot { it.isHidden }
.filter { isExpanded || it.defaultSelection }
} else {
processes
.filter { it.isHidden }
}.forEach { it.toggle() }
}
init {
processes.forEach { it.addOnPropertyChangedCallback(BR.hidden) { recalculateChecked() } }
addOnPropertyChangedCallback(BR.expanded) { recalculateChecked() }
recalculateChecked()
}
fun toggleExpand(v: View) {
(v.parent as? ViewGroup)?.startAnimations()
isExpanded = !isExpanded
}
private fun recalculateChecked() {
itemsChecked = processes.count { it.isHidden }
state = if (isExpanded) {
when (itemsChecked) {
0 -> false
processes.size -> true
else -> null
}
} else {
val defaultProcesses = processes.filter { it.defaultSelection }
when (defaultProcesses.count { it.isHidden }) {
0 -> false
defaultProcesses.size -> true
else -> null
}
}
}
override fun compareTo(other: HideRvItem) = comparator.compare(this, other)
companion object {
private val comparator = compareBy<HideRvItem>(
{ it.itemsChecked == 0 },
{ it.info }
)
}
}
class HideProcessRvItem(
val process: HideProcessInfo
) : ObservableItem<HideProcessRvItem>() {
override val layoutRes get() = R.layout.item_hide_process_md2
@get:Bindable
var isHidden
get() = process.isHidden
set(value) = set(value, process.isHidden, { process.isHidden = it }, BR.hidden) {
val arg = if (it) "add" else "rm"
val (name, pkg) = process
Shell.su("magiskhide $arg $pkg $name").submit()
}
fun toggle() {
isHidden = !isHidden
}
val defaultSelection get() =
process.isIsolated || process.isAppZygote || process.name == process.packageName
override fun contentSameAs(other: HideProcessRvItem) = process == other.process
override fun itemSameAs(other: HideProcessRvItem) = process.name == other.process.name
}