Support modules update

This commit is contained in:
vvb2060
2022-01-17 15:08:54 +08:00
committed by John Wu
parent 2997258fd0
commit bc0c1980db
15 changed files with 120 additions and 664 deletions
@@ -5,7 +5,6 @@ import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.Info
import com.topjohnwu.magisk.core.model.module.LocalModule
import com.topjohnwu.magisk.core.model.module.OnlineModule
import com.topjohnwu.magisk.databinding.DiffRvItem
import com.topjohnwu.magisk.databinding.ObservableDiffRvItem
import com.topjohnwu.magisk.databinding.RvContainer
@@ -16,62 +15,29 @@ object InstallModule : DiffRvItem<InstallModule>() {
override val layoutRes = R.layout.item_module_download
}
class SectionTitle(
val title: Int,
_button: Int = 0,
_icon: Int = 0
) : ObservableDiffRvItem<SectionTitle>() {
override val layoutRes = R.layout.item_section_md2
@get:Bindable
var button = _button
set(value) = set(value, field, { field = it }, BR.button)
@get:Bindable
var icon = _icon
set(value) = set(value, field, { field = it }, BR.icon)
@get:Bindable
var hasButton = _button != 0 && _icon != 0
set(value) = set(value, field, { field = it }, BR.hasButton)
}
class OnlineModuleRvItem(
override val item: OnlineModule
) : ObservableDiffRvItem<OnlineModuleRvItem>(), RvContainer<OnlineModule> {
override val layoutRes: Int = R.layout.item_repo_md2
@get:Bindable
var progress = 0
set(value) = set(value, field, { field = it }, BR.progress)
var hasUpdate = false
override fun itemSameAs(other: OnlineModuleRvItem): Boolean = item.id == other.item.id
}
class LocalModuleRvItem(
override val item: LocalModule
) : ObservableDiffRvItem<LocalModuleRvItem>(), RvContainer<LocalModule> {
override val layoutRes = R.layout.item_module_md2
@get:Bindable
var online: OnlineModule? = null
set(value) = set(value, field, { field = it }, BR.online)
@get:Bindable
var isEnabled = item.enable
set(value) = set(value, field, { field = it }, BR.enabled) {
set(value) = set(value, field, { field = it }, BR.enabled, BR.updateReady) {
item.enable = value
}
@get:Bindable
var isRemoved = item.remove
set(value) = set(value, field, { field = it }, BR.removed) {
set(value) = set(value, field, { field = it }, BR.removed, BR.updateReady) {
item.remove = value
}
@get:Bindable
var updateReady: Boolean
get() = item.updateInfo != null && !isRemoved && isEnabled
set(_) = notifyPropertyChanged(BR.updateReady)
val isSuspended =
(Info.isZygiskEnabled && item.isRiru) || (!Info.isZygiskEnabled && item.isZygisk)
@@ -80,11 +46,9 @@ class LocalModuleRvItem(
else R.string.suspend_text_zygisk.asText(R.string.zygisk.asText())
val isUpdated get() = item.updated
val isModified get() = isRemoved || isUpdated
fun delete(viewModel: ModuleViewModel) {
fun delete() {
isRemoved = !isRemoved
viewModel.updateActiveState()
}
override fun itemSameAs(other: LocalModuleRvItem): Boolean = item.id == other.item.id
@@ -1,63 +1,30 @@
package com.topjohnwu.magisk.ui.module
import androidx.databinding.Bindable
import androidx.databinding.ObservableArrayList
import androidx.lifecycle.viewModelScope
import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.arch.BaseViewModel
import com.topjohnwu.magisk.arch.Queryable
import com.topjohnwu.magisk.core.Info
import com.topjohnwu.magisk.core.model.module.LocalModule
import com.topjohnwu.magisk.core.model.module.OnlineModule
import com.topjohnwu.magisk.databinding.*
import com.topjohnwu.magisk.events.OpenReadmeEvent
import com.topjohnwu.magisk.databinding.RvItem
import com.topjohnwu.magisk.databinding.adapterOf
import com.topjohnwu.magisk.databinding.diffListOf
import com.topjohnwu.magisk.databinding.itemBindingOf
import com.topjohnwu.magisk.events.SelectModuleEvent
import com.topjohnwu.magisk.events.SnackbarEvent
import com.topjohnwu.magisk.events.dialog.ModuleInstallDialog
import com.topjohnwu.magisk.ktx.addOnListChangedCallback
import com.topjohnwu.magisk.ktx.reboot
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import me.tatarka.bindingcollectionadapter2.collections.MergeObservableList
class ModuleViewModel : BaseViewModel(), Queryable {
class ModuleViewModel : BaseViewModel() {
val bottomBarBarrierIds =
intArrayOf(R.id.module_info, R.id.module_remove)
val bottomBarBarrierIds = intArrayOf(R.id.module_update, R.id.module_remove)
override val queryDelay = 1000L
@get:Bindable
var isRemoteLoading = false
set(value) = set(value, field, { field = it }, BR.remoteLoading)
@get:Bindable
var query = ""
set(value) = set(value, field, { field = it }, BR.query) {
submitQuery()
// Yes we do lie about the search being loaded
searchLoading = true
}
@get:Bindable
var searchLoading = false
set(value) = set(value, field, { field = it }, BR.searchLoading)
val itemsSearch = diffListOf<AnyDiffRvItem>()
val itemSearchBinding = itemBindingOf<AnyDiffRvItem> {
it.bindExtra(BR.viewModel, this)
}
private val installSectionList = ObservableArrayList<RvItem>()
private val itemsInstalled = diffListOf<LocalModuleRvItem>()
private val sectionInstalled = SectionTitle(
R.string.module_installed,
R.string.reboot,
R.drawable.ic_restart
).also { it.hasButton = false }
val adapter = adapterOf<RvItem>()
val items = MergeObservableList<RvItem>()
@@ -65,34 +32,19 @@ class ModuleViewModel : BaseViewModel(), Queryable {
it.bindExtra(BR.viewModel, this)
}
// ---
init {
itemsInstalled.addOnListChangedCallback(
onItemRangeInserted = { _, _, _ ->
if (installSectionList.isEmpty())
installSectionList.add(sectionInstalled)
},
onItemRangeRemoved = { list, _, _ ->
if (list.isEmpty())
installSectionList.clear()
}
)
if (Info.env.isActive) {
items.insertItem(InstallModule)
.insertList(installSectionList)
.insertList(itemsInstalled)
}
}
// ---
override fun refresh(): Job {
return viewModelScope.launch {
state = State.LOADING
loadInstalled()
state = State.LOADED
loadUpdateInfo()
}
}
@@ -104,62 +56,21 @@ class ModuleViewModel : BaseViewModel(), Queryable {
itemsInstalled.update(installed, diff)
}
fun forceRefresh() {
itemsInstalled.clear()
refresh()
submitQuery()
private suspend fun loadUpdateInfo() {
withContext(Dispatchers.IO) {
itemsInstalled.forEach {
it.updateReady = it.item.load()
}
}
}
// ---
private suspend fun queryInternal(query: String): List<AnyDiffRvItem> {
return if (query.isBlank()) {
itemsSearch.clear()
listOf()
fun downloadPressed(item: OnlineModule?) =
if (item != null && isConnected.get()) {
withExternalRW { ModuleInstallDialog(item).publish() }
} else {
withContext(Dispatchers.Default) {
itemsInstalled.filter {
it.item.id.contains(query, true)
|| it.item.name.contains(query, true)
|| it.item.description.contains(query, true)
}
}
SnackbarEvent(R.string.no_connection).publish()
}
}
override fun query() {
viewModelScope.launch {
val searched = queryInternal(query)
val diff = withContext(Dispatchers.Default) {
itemsSearch.calculateDiff(searched)
}
searchLoading = false
itemsSearch.update(searched, diff)
}
}
// ---
fun updateActiveState() {
sectionInstalled.hasButton = itemsInstalled.any { it.isModified }
}
fun sectionPressed(item: SectionTitle) = when (item) {
sectionInstalled -> reboot()
else -> Unit
}
// The following methods are not used, but kept for future integration
fun downloadPressed(item: OnlineModule) =
if (isConnected.get()) withExternalRW { ModuleInstallDialog(item).publish() }
else { SnackbarEvent(R.string.no_connection).publish() }
fun installPressed() = withExternalRW { SelectModuleEvent().publish() }
fun infoPressed(item: OnlineModule) =
if (isConnected.get()) OpenReadmeEvent(item).publish()
else SnackbarEvent(R.string.no_connection).publish()
fun infoPressed(item: LocalModuleRvItem) { infoPressed(item.online ?: return) }
}