You've already forked Magisk
mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-09-06 06:36:58 +00:00
Support modules update
This commit is contained in:
@@ -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) }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user