mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-04-21 21:11:27 +00:00
Remove online section in modules fragment
This commit is contained in:
parent
f98c68a280
commit
f5c982355a
@ -232,7 +232,7 @@ dependencies {
|
|||||||
implementation("androidx.navigation:navigation-ui-ktx:${vNav}")
|
implementation("androidx.navigation:navigation-ui-ktx:${vNav}")
|
||||||
|
|
||||||
implementation("androidx.biometric:biometric:1.1.0")
|
implementation("androidx.biometric:biometric:1.1.0")
|
||||||
implementation("androidx.constraintlayout:constraintlayout:2.0.4")
|
implementation("androidx.constraintlayout:constraintlayout:2.1.0")
|
||||||
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
||||||
implementation("androidx.browser:browser:1.3.0")
|
implementation("androidx.browser:browser:1.3.0")
|
||||||
implementation("androidx.preference:preference:1.1.1")
|
implementation("androidx.preference:preference:1.1.1")
|
||||||
|
@ -19,7 +19,6 @@ import com.topjohnwu.magisk.ktx.deviceProtectedContext
|
|||||||
import com.topjohnwu.magisk.ui.home.HomeViewModel
|
import com.topjohnwu.magisk.ui.home.HomeViewModel
|
||||||
import com.topjohnwu.magisk.ui.install.InstallViewModel
|
import com.topjohnwu.magisk.ui.install.InstallViewModel
|
||||||
import com.topjohnwu.magisk.ui.log.LogViewModel
|
import com.topjohnwu.magisk.ui.log.LogViewModel
|
||||||
import com.topjohnwu.magisk.ui.module.ModuleViewModel
|
|
||||||
import com.topjohnwu.magisk.ui.settings.SettingsViewModel
|
import com.topjohnwu.magisk.ui.settings.SettingsViewModel
|
||||||
import com.topjohnwu.magisk.ui.superuser.SuperuserViewModel
|
import com.topjohnwu.magisk.ui.superuser.SuperuserViewModel
|
||||||
import com.topjohnwu.magisk.ui.surequest.SuRequestViewModel
|
import com.topjohnwu.magisk.ui.surequest.SuRequestViewModel
|
||||||
@ -61,7 +60,6 @@ object ServiceLocator {
|
|||||||
return when (clz) {
|
return when (clz) {
|
||||||
HomeViewModel::class.java -> HomeViewModel(networkService)
|
HomeViewModel::class.java -> HomeViewModel(networkService)
|
||||||
LogViewModel::class.java -> LogViewModel(logRepo)
|
LogViewModel::class.java -> LogViewModel(logRepo)
|
||||||
ModuleViewModel::class.java -> ModuleViewModel(repoDB, repoUpdater)
|
|
||||||
SettingsViewModel::class.java -> SettingsViewModel(repoDB)
|
SettingsViewModel::class.java -> SettingsViewModel(repoDB)
|
||||||
SuperuserViewModel::class.java -> SuperuserViewModel(policyDB)
|
SuperuserViewModel::class.java -> SuperuserViewModel(policyDB)
|
||||||
InstallViewModel::class.java -> InstallViewModel(networkService)
|
InstallViewModel::class.java -> InstallViewModel(networkService)
|
||||||
|
@ -10,8 +10,6 @@ import androidx.recyclerview.widget.RecyclerView
|
|||||||
import com.topjohnwu.magisk.R
|
import com.topjohnwu.magisk.R
|
||||||
import com.topjohnwu.magisk.arch.BaseUIFragment
|
import com.topjohnwu.magisk.arch.BaseUIFragment
|
||||||
import com.topjohnwu.magisk.arch.ReselectionTarget
|
import com.topjohnwu.magisk.arch.ReselectionTarget
|
||||||
import com.topjohnwu.magisk.arch.ViewEvent
|
|
||||||
import com.topjohnwu.magisk.core.download.BaseDownloader
|
|
||||||
import com.topjohnwu.magisk.databinding.FragmentModuleMd2Binding
|
import com.topjohnwu.magisk.databinding.FragmentModuleMd2Binding
|
||||||
import com.topjohnwu.magisk.di.viewModel
|
import com.topjohnwu.magisk.di.viewModel
|
||||||
import com.topjohnwu.magisk.ktx.*
|
import com.topjohnwu.magisk.ktx.*
|
||||||
@ -42,13 +40,10 @@ class ModuleFragment : BaseUIFragment<ModuleViewModel, FragmentModuleMd2Binding>
|
|||||||
super.onStart()
|
super.onStart()
|
||||||
setHasOptionsMenu(true)
|
setHasOptionsMenu(true)
|
||||||
activity.title = resources.getString(R.string.modules)
|
activity.title = resources.getString(R.string.modules)
|
||||||
BaseDownloader.observeProgress(this, viewModel::onProgressUpdate)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
setEndlessScroller()
|
|
||||||
setEndlessSearch()
|
|
||||||
|
|
||||||
binding.moduleFilterToggle.setOnClickListener {
|
binding.moduleFilterToggle.setOnClickListener {
|
||||||
isFilterVisible = true
|
isFilterVisible = true
|
||||||
@ -116,13 +111,6 @@ class ModuleFragment : BaseUIFragment<ModuleViewModel, FragmentModuleMd2Binding>
|
|||||||
|
|
||||||
// ---
|
// ---
|
||||||
|
|
||||||
override fun onEventDispatched(event: ViewEvent) = when (event) {
|
|
||||||
is EndlessRecyclerScrollListener.ResetState -> listeners.forEach { it.resetState() }
|
|
||||||
else -> super.onEventDispatched(event)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---
|
|
||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||||
inflater.inflate(R.menu.menu_module_md2, menu)
|
inflater.inflate(R.menu.menu_module_md2, menu)
|
||||||
}
|
}
|
||||||
@ -137,32 +125,12 @@ class ModuleFragment : BaseUIFragment<ModuleViewModel, FragmentModuleMd2Binding>
|
|||||||
// ---
|
// ---
|
||||||
|
|
||||||
override fun onReselected() {
|
override fun onReselected() {
|
||||||
binding.moduleList
|
binding.moduleList.scrollToPosition(10)
|
||||||
.also { it.scrollToPosition(10) }
|
binding.moduleList.also { it.post { it.smoothScrollToPosition(0) } }
|
||||||
.let { binding.moduleList }
|
|
||||||
.also { it.post { it.smoothScrollToPosition(0) } }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---
|
// ---
|
||||||
|
|
||||||
override fun onPreBind(binding: FragmentModuleMd2Binding) = Unit
|
override fun onPreBind(binding: FragmentModuleMd2Binding) = Unit
|
||||||
|
|
||||||
private fun setEndlessScroller() {
|
|
||||||
val lama = binding.moduleList.layoutManager ?: return
|
|
||||||
lama.isAutoMeasureEnabled = false
|
|
||||||
|
|
||||||
val listener = EndlessRecyclerScrollListener(lama, viewModel::loadRemote)
|
|
||||||
binding.moduleList.addOnScrollListener(listener)
|
|
||||||
listeners.add(listener)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setEndlessSearch() {
|
|
||||||
val lama = binding.moduleFilterInclude.moduleFilterList.layoutManager ?: return
|
|
||||||
lama.isAutoMeasureEnabled = false
|
|
||||||
|
|
||||||
val listener = EndlessRecyclerScrollListener(lama, viewModel::loadMoreQuery)
|
|
||||||
binding.moduleFilterInclude.moduleFilterList.addOnScrollListener(listener)
|
|
||||||
listeners.add(listener)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -39,34 +39,27 @@ class SectionTitle(
|
|||||||
override fun contentSameAs(other: SectionTitle): Boolean = this === other
|
override fun contentSameAs(other: SectionTitle): Boolean = this === other
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class RepoItem(val item: OnlineModule) : ObservableItem<RepoItem>() {
|
class OnlineModuleRvItem(val item: OnlineModule) : ObservableItem<OnlineModuleRvItem>() {
|
||||||
override val layoutRes: Int = R.layout.item_repo_md2
|
override val layoutRes: Int = R.layout.item_repo_md2
|
||||||
|
|
||||||
@get:Bindable
|
@get:Bindable
|
||||||
var progress = 0
|
var progress = 0
|
||||||
set(value) = set(value, field, { field = it }, BR.progress)
|
set(value) = set(value, field, { field = it }, BR.progress)
|
||||||
|
|
||||||
abstract val isUpdate: Boolean
|
var hasUpdate = false
|
||||||
|
|
||||||
override fun contentSameAs(other: RepoItem): Boolean = item == other.item
|
override fun contentSameAs(other: OnlineModuleRvItem): Boolean = item == other.item
|
||||||
override fun itemSameAs(other: RepoItem): Boolean = item.id == other.item.id
|
override fun itemSameAs(other: OnlineModuleRvItem): Boolean = item.id == other.item.id
|
||||||
|
|
||||||
class Update(item: OnlineModule) : RepoItem(item) {
|
|
||||||
override val isUpdate get() = true
|
|
||||||
}
|
|
||||||
|
|
||||||
class Remote(item: OnlineModule) : RepoItem(item) {
|
|
||||||
override val isUpdate get() = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class ModuleItem(val item: LocalModule) : ObservableItem<ModuleItem>() {
|
class LocalModuleRvItem(val item: LocalModule) : ObservableItem<LocalModuleRvItem>() {
|
||||||
|
|
||||||
override val layoutRes = R.layout.item_module_md2
|
override val layoutRes = R.layout.item_module_md2
|
||||||
|
|
||||||
@get:Bindable
|
@get:Bindable
|
||||||
var repo: OnlineModule? = null
|
var online: OnlineModule? = null
|
||||||
set(value) = set(value, field, { field = it }, BR.repo)
|
set(value) = set(value, field, { field = it }, BR.online)
|
||||||
|
|
||||||
@get:Bindable
|
@get:Bindable
|
||||||
var isEnabled = item.enable
|
var isEnabled = item.enable
|
||||||
@ -88,11 +81,10 @@ class ModuleItem(val item: LocalModule) : ObservableItem<ModuleItem>() {
|
|||||||
viewModel.updateActiveState()
|
viewModel.updateActiveState()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun contentSameAs(other: ModuleItem): Boolean = item.version == other.item.version
|
override fun contentSameAs(other: LocalModuleRvItem): Boolean = item.version == other.item.version
|
||||||
&& item.versionCode == other.item.versionCode
|
&& item.versionCode == other.item.versionCode
|
||||||
&& item.description == other.item.description
|
&& item.description == other.item.description
|
||||||
&& item.name == other.item.name
|
&& item.name == other.item.name
|
||||||
|
|
||||||
override fun itemSameAs(other: ModuleItem): Boolean = item.id == other.item.id
|
override fun itemSameAs(other: LocalModuleRvItem): Boolean = item.id == other.item.id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,12 +6,10 @@ import androidx.lifecycle.viewModelScope
|
|||||||
import com.topjohnwu.magisk.BR
|
import com.topjohnwu.magisk.BR
|
||||||
import com.topjohnwu.magisk.R
|
import com.topjohnwu.magisk.R
|
||||||
import com.topjohnwu.magisk.arch.*
|
import com.topjohnwu.magisk.arch.*
|
||||||
import com.topjohnwu.magisk.core.Config
|
|
||||||
import com.topjohnwu.magisk.core.Info
|
import com.topjohnwu.magisk.core.Info
|
||||||
import com.topjohnwu.magisk.core.download.Subject
|
|
||||||
import com.topjohnwu.magisk.core.model.module.LocalModule
|
import com.topjohnwu.magisk.core.model.module.LocalModule
|
||||||
import com.topjohnwu.magisk.core.tasks.RepoUpdater
|
import com.topjohnwu.magisk.core.model.module.OnlineModule
|
||||||
import com.topjohnwu.magisk.data.database.RepoDao
|
import com.topjohnwu.magisk.databinding.ComparableRvItem
|
||||||
import com.topjohnwu.magisk.databinding.RvItem
|
import com.topjohnwu.magisk.databinding.RvItem
|
||||||
import com.topjohnwu.magisk.events.OpenReadmeEvent
|
import com.topjohnwu.magisk.events.OpenReadmeEvent
|
||||||
import com.topjohnwu.magisk.events.SelectModuleEvent
|
import com.topjohnwu.magisk.events.SelectModuleEvent
|
||||||
@ -19,37 +17,16 @@ import com.topjohnwu.magisk.events.SnackbarEvent
|
|||||||
import com.topjohnwu.magisk.events.dialog.ModuleInstallDialog
|
import com.topjohnwu.magisk.events.dialog.ModuleInstallDialog
|
||||||
import com.topjohnwu.magisk.ktx.addOnListChangedCallback
|
import com.topjohnwu.magisk.ktx.addOnListChangedCallback
|
||||||
import com.topjohnwu.magisk.ktx.reboot
|
import com.topjohnwu.magisk.ktx.reboot
|
||||||
import com.topjohnwu.magisk.utils.EndlessRecyclerScrollListener
|
|
||||||
import com.topjohnwu.magisk.utils.set
|
import com.topjohnwu.magisk.utils.set
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import me.tatarka.bindingcollectionadapter2.collections.MergeObservableList
|
import me.tatarka.bindingcollectionadapter2.collections.MergeObservableList
|
||||||
import kotlin.math.roundToInt
|
|
||||||
|
|
||||||
/*
|
class ModuleViewModel : BaseViewModel(), Queryable {
|
||||||
* The repo fetching behavior should follow these rules:
|
|
||||||
*
|
|
||||||
* For the first time the repo list is queried in the app, it should ALWAYS fetch for
|
|
||||||
* updates. However, this particular fetch should go through RepoUpdater.invoke(false),
|
|
||||||
* which internally will set ETAGs when doing GET requests to GitHub's API and will
|
|
||||||
* only update repo DB only if the GitHub API shows that something is changed remotely.
|
|
||||||
*
|
|
||||||
* When a user explicitly requests a full DB refresh, it should ALWAYS do a full force
|
|
||||||
* refresh, which in code can be done with RepoUpdater.invoke(true). This will update
|
|
||||||
* every single repo's information regardless whether GitHub's API shows if there is
|
|
||||||
* anything changed or not.
|
|
||||||
* */
|
|
||||||
|
|
||||||
class ModuleViewModel(
|
|
||||||
private val repoDB: RepoDao,
|
|
||||||
private val repoUpdater: RepoUpdater
|
|
||||||
) : BaseViewModel(), Queryable {
|
|
||||||
|
|
||||||
override val queryDelay = 1000L
|
override val queryDelay = 1000L
|
||||||
private var queryJob: Job? = null
|
|
||||||
private var remoteJob: Job? = null
|
|
||||||
|
|
||||||
@get:Bindable
|
@get:Bindable
|
||||||
var isRemoteLoading = false
|
var isRemoteLoading = false
|
||||||
@ -67,57 +44,27 @@ class ModuleViewModel(
|
|||||||
var searchLoading = false
|
var searchLoading = false
|
||||||
set(value) = set(value, field, { field = it }, BR.searchLoading)
|
set(value) = set(value, field, { field = it }, BR.searchLoading)
|
||||||
|
|
||||||
val itemsSearch = diffListOf<RepoItem>()
|
val itemsSearch = diffListOf<ComparableRvItem<*>>()
|
||||||
val itemSearchBinding = itemBindingOf<RepoItem> {
|
val itemSearchBinding = itemBindingOf<ComparableRvItem<*>> {
|
||||||
it.bindExtra(BR.viewModel, this)
|
it.bindExtra(BR.viewModel, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val installSectionList = ObservableArrayList<RvItem>()
|
private val installSectionList = ObservableArrayList<RvItem>()
|
||||||
private val updatableSectionList = ObservableArrayList<RvItem>()
|
private val itemsInstalled = diffListOf<LocalModuleRvItem>()
|
||||||
|
|
||||||
private val itemsInstalled = diffListOf<ModuleItem>()
|
|
||||||
private val itemsUpdatable = diffListOf<RepoItem.Update>()
|
|
||||||
private val itemsOnline = diffListOf<RepoItem.Remote>()
|
|
||||||
|
|
||||||
private val sectionUpdate = SectionTitle(
|
|
||||||
R.string.module_section_pending,
|
|
||||||
R.string.module_section_pending_action,
|
|
||||||
R.drawable.ic_update_md2
|
|
||||||
// enable with implementation of https://github.com/topjohnwu/Magisk/issues/2036
|
|
||||||
).also { it.hasButton = false }
|
|
||||||
|
|
||||||
private val sectionInstalled = SectionTitle(
|
private val sectionInstalled = SectionTitle(
|
||||||
R.string.module_installed,
|
R.string.module_installed,
|
||||||
R.string.reboot,
|
R.string.reboot,
|
||||||
R.drawable.ic_restart
|
R.drawable.ic_restart
|
||||||
).also { it.hasButton = false }
|
).also { it.hasButton = false }
|
||||||
|
|
||||||
private val sectionOnline = SectionTitle(
|
|
||||||
R.string.module_section_online,
|
|
||||||
R.string.sorting_order
|
|
||||||
).apply { updateOrderIcon() }
|
|
||||||
|
|
||||||
val adapter = adapterOf<RvItem>()
|
val adapter = adapterOf<RvItem>()
|
||||||
val items = MergeObservableList<RvItem>()
|
val items = MergeObservableList<RvItem>()
|
||||||
.also { if (Info.env.isActive) {
|
|
||||||
it.insertItem(InstallModule)
|
|
||||||
.insertList(updatableSectionList)
|
|
||||||
.insertList(itemsUpdatable)
|
|
||||||
.insertList(installSectionList)
|
|
||||||
.insertList(itemsInstalled)
|
|
||||||
} }
|
|
||||||
.insertItem(sectionOnline)
|
|
||||||
.insertList(itemsOnline)
|
|
||||||
val itemBinding = itemBindingOf<RvItem> {
|
val itemBinding = itemBindingOf<RvItem> {
|
||||||
it.bindExtra(BR.viewModel, this)
|
it.bindExtra(BR.viewModel, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---
|
// ---
|
||||||
|
|
||||||
private var refetch = false
|
|
||||||
|
|
||||||
// ---
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
itemsInstalled.addOnListChangedCallback(
|
itemsInstalled.addOnListChangedCallback(
|
||||||
onItemRangeInserted = { _, _, _ ->
|
onItemRangeInserted = { _, _, _ ->
|
||||||
@ -129,131 +76,58 @@ class ModuleViewModel(
|
|||||||
installSectionList.clear()
|
installSectionList.clear()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
itemsUpdatable.addOnListChangedCallback(
|
|
||||||
onItemRangeInserted = { _, _, _ ->
|
if (Info.env.isActive) {
|
||||||
if (updatableSectionList.isEmpty())
|
items.insertItem(InstallModule)
|
||||||
updatableSectionList.add(sectionUpdate)
|
.insertList(installSectionList)
|
||||||
},
|
.insertList(itemsInstalled)
|
||||||
onItemRangeRemoved = { list, _, _ ->
|
|
||||||
if (list.isEmpty())
|
|
||||||
updatableSectionList.clear()
|
|
||||||
}
|
}
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---
|
// ---
|
||||||
|
|
||||||
fun onProgressUpdate(progress: Float, subject: Subject) {
|
|
||||||
if (subject !is Subject.Module)
|
|
||||||
return
|
|
||||||
|
|
||||||
viewModelScope.launch {
|
|
||||||
val items = withContext(Dispatchers.Default) {
|
|
||||||
val predicate = { it: RepoItem -> it.item.id == subject.module.id }
|
|
||||||
itemsUpdatable.filter(predicate) +
|
|
||||||
itemsOnline.filter(predicate) +
|
|
||||||
itemsSearch.filter(predicate)
|
|
||||||
}
|
|
||||||
items.forEach { it.progress = progress.times(100).roundToInt() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun refresh(): Job {
|
override fun refresh(): Job {
|
||||||
return viewModelScope.launch {
|
return viewModelScope.launch {
|
||||||
state = State.LOADING
|
state = State.LOADING
|
||||||
loadInstalled()
|
loadInstalled()
|
||||||
if (itemsOnline.isEmpty())
|
|
||||||
loadRemote()
|
|
||||||
state = State.LOADED
|
state = State.LOADED
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun SectionTitle.updateOrderIcon() {
|
|
||||||
hasButton = true
|
|
||||||
icon = when (Config.repoOrder) {
|
|
||||||
Config.Value.ORDER_NAME -> R.drawable.ic_order_name
|
|
||||||
Config.Value.ORDER_DATE -> R.drawable.ic_order_date
|
|
||||||
else -> return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun loadInstalled() {
|
private suspend fun loadInstalled() {
|
||||||
val installed = LocalModule.installed().map { ModuleItem(it) }
|
val installed = LocalModule.installed().map { LocalModuleRvItem(it) }
|
||||||
val diff = withContext(Dispatchers.Default) {
|
val diff = withContext(Dispatchers.Default) {
|
||||||
itemsInstalled.calculateDiff(installed)
|
itemsInstalled.calculateDiff(installed)
|
||||||
}
|
}
|
||||||
itemsInstalled.update(installed, diff)
|
itemsInstalled.update(installed, diff)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun loadUpdatable() {
|
|
||||||
val (updates, diff) = withContext(Dispatchers.IO) {
|
|
||||||
itemsInstalled.forEach {
|
|
||||||
launch {
|
|
||||||
it.repo = repoDB.getModule(it.item.id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val updates = itemsInstalled
|
|
||||||
.mapNotNull { repoDB.getUpdatableModule(it.item.id, it.item.versionCode) }
|
|
||||||
.map { RepoItem.Update(it) }
|
|
||||||
val diff = itemsUpdatable.calculateDiff(updates)
|
|
||||||
return@withContext updates to diff
|
|
||||||
}
|
|
||||||
itemsUpdatable.update(updates, diff)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun loadRemote() {
|
|
||||||
// check for existing jobs
|
|
||||||
if (remoteJob?.isActive == true)
|
|
||||||
return
|
|
||||||
|
|
||||||
if (itemsOnline.isEmpty())
|
|
||||||
EndlessRecyclerScrollListener.ResetState().publish()
|
|
||||||
|
|
||||||
remoteJob = viewModelScope.launch {
|
|
||||||
suspend fun loadRemoteDB(offset: Int) = withContext(Dispatchers.IO) {
|
|
||||||
repoDB.getModules(offset).map { RepoItem.Remote(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
isRemoteLoading = true
|
|
||||||
val repos = if (itemsOnline.isEmpty()) {
|
|
||||||
repoUpdater.run(refetch)
|
|
||||||
loadUpdatable()
|
|
||||||
loadRemoteDB(0)
|
|
||||||
} else {
|
|
||||||
loadRemoteDB(itemsOnline.size)
|
|
||||||
}
|
|
||||||
isRemoteLoading = false
|
|
||||||
refetch = false
|
|
||||||
queryHandler.post { itemsOnline.addAll(repos) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun forceRefresh() {
|
fun forceRefresh() {
|
||||||
itemsOnline.clear()
|
itemsInstalled.clear()
|
||||||
itemsUpdatable.clear()
|
|
||||||
itemsSearch.clear()
|
|
||||||
refetch = true
|
|
||||||
refresh()
|
refresh()
|
||||||
submitQuery()
|
submitQuery()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---
|
// ---
|
||||||
|
|
||||||
private suspend fun queryInternal(query: String, offset: Int): List<RepoItem> {
|
private suspend fun queryInternal(query: String): List<ComparableRvItem<*>> {
|
||||||
return if (query.isBlank()) {
|
return if (query.isBlank()) {
|
||||||
itemsSearch.clear()
|
itemsSearch.clear()
|
||||||
listOf()
|
listOf()
|
||||||
} else {
|
} else {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.Default) {
|
||||||
repoDB.searchModules(query, offset).map { RepoItem.Remote(it) }
|
itemsInstalled.filter {
|
||||||
|
it.item.id.contains(query, true)
|
||||||
|
|| it.item.name.contains(query, true)
|
||||||
|
|| it.item.description.contains(query, true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun query() {
|
override fun query() {
|
||||||
EndlessRecyclerScrollListener.ResetState().publish()
|
viewModelScope.launch {
|
||||||
queryJob = viewModelScope.launch {
|
val searched = queryInternal(query)
|
||||||
val searched = queryInternal(query, 0)
|
|
||||||
val diff = withContext(Dispatchers.Default) {
|
val diff = withContext(Dispatchers.Default) {
|
||||||
itemsSearch.calculateDiff(searched)
|
itemsSearch.calculateDiff(searched)
|
||||||
}
|
}
|
||||||
@ -262,59 +136,28 @@ class ModuleViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadMoreQuery() {
|
|
||||||
if (queryJob?.isActive == true) return
|
|
||||||
queryJob = viewModelScope.launch {
|
|
||||||
val searched = queryInternal(query, itemsSearch.size)
|
|
||||||
queryHandler.post { itemsSearch.addAll(searched) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---
|
// ---
|
||||||
|
|
||||||
fun updateActiveState() = viewModelScope.launch {
|
fun updateActiveState() {
|
||||||
sectionInstalled.hasButton = withContext(Dispatchers.Default) {
|
sectionInstalled.hasButton = itemsInstalled.any { it.isModified }
|
||||||
itemsInstalled.any { it.isModified }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun sectionPressed(item: SectionTitle) = when (item) {
|
fun sectionPressed(item: SectionTitle) = when (item) {
|
||||||
sectionInstalled -> reboot() // TODO add reboot picker, regular reboot is not always preferred
|
sectionInstalled -> reboot()
|
||||||
sectionOnline -> {
|
|
||||||
Config.repoOrder = when (Config.repoOrder) {
|
|
||||||
Config.Value.ORDER_NAME -> Config.Value.ORDER_DATE
|
|
||||||
Config.Value.ORDER_DATE -> Config.Value.ORDER_NAME
|
|
||||||
else -> Config.Value.ORDER_NAME
|
|
||||||
}
|
|
||||||
sectionOnline.updateOrderIcon()
|
|
||||||
queryHandler.post {
|
|
||||||
itemsOnline.clear()
|
|
||||||
loadRemote()
|
|
||||||
}
|
|
||||||
Unit
|
|
||||||
}
|
|
||||||
else -> Unit
|
else -> Unit
|
||||||
}
|
}
|
||||||
|
|
||||||
fun downloadPressed(item: RepoItem) = if (isConnected.get()) withExternalRW {
|
// The following methods are not used, but kept for future integration
|
||||||
ModuleInstallDialog(item.item).publish()
|
|
||||||
} else {
|
|
||||||
SnackbarEvent(R.string.no_connection).publish()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun installPressed() = withExternalRW {
|
fun downloadPressed(item: OnlineModule) =
|
||||||
SelectModuleEvent().publish()
|
if (isConnected.get()) withExternalRW { ModuleInstallDialog(item).publish() }
|
||||||
}
|
else { SnackbarEvent(R.string.no_connection).publish() }
|
||||||
|
|
||||||
fun infoPressed(item: RepoItem) =
|
fun installPressed() = withExternalRW { SelectModuleEvent().publish() }
|
||||||
if (isConnected.get()) OpenReadmeEvent(item.item).publish()
|
|
||||||
|
fun infoPressed(item: OnlineModule) =
|
||||||
|
if (isConnected.get()) OpenReadmeEvent(item).publish()
|
||||||
else SnackbarEvent(R.string.no_connection).publish()
|
else SnackbarEvent(R.string.no_connection).publish()
|
||||||
|
|
||||||
|
fun infoPressed(item: LocalModuleRvItem) { infoPressed(item.online ?: return) }
|
||||||
fun infoPressed(item: ModuleItem) {
|
|
||||||
item.repo?.also {
|
|
||||||
if (isConnected.get()) OpenReadmeEvent(it).publish()
|
|
||||||
else SnackbarEvent(R.string.no_connection).publish()
|
|
||||||
} ?: return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
<variable
|
<variable
|
||||||
name="item"
|
name="item"
|
||||||
type="com.topjohnwu.magisk.ui.module.ModuleItem" />
|
type="com.topjohnwu.magisk.ui.module.LocalModuleRvItem" />
|
||||||
|
|
||||||
<variable
|
<variable
|
||||||
name="viewModel"
|
name="viewModel"
|
||||||
@ -120,7 +120,7 @@
|
|||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/module_info"
|
android:id="@+id/module_info"
|
||||||
style="@style/WidgetFoundation.Icon"
|
style="@style/WidgetFoundation.Icon"
|
||||||
gone="@{item.repo == null}"
|
gone="@{item.online == null}"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:alpha=".5"
|
android:alpha=".5"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
<variable
|
<variable
|
||||||
name="item"
|
name="item"
|
||||||
type="com.topjohnwu.magisk.ui.module.RepoItem" />
|
type="com.topjohnwu.magisk.ui.module.OnlineModuleRvItem" />
|
||||||
|
|
||||||
<variable
|
<variable
|
||||||
name="viewModel"
|
name="viewModel"
|
||||||
@ -25,7 +25,6 @@
|
|||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:nextFocusRight="@id/module_info"
|
android:nextFocusRight="@id/module_info"
|
||||||
android:onClick="@{() -> viewModel.downloadPressed(item)}"
|
|
||||||
tools:layout_gravity="center"
|
tools:layout_gravity="center"
|
||||||
tools:layout_marginBottom="@dimen/l1"
|
tools:layout_marginBottom="@dimen/l1"
|
||||||
tools:layout_marginEnd="@dimen/l1">
|
tools:layout_marginEnd="@dimen/l1">
|
||||||
@ -102,7 +101,7 @@
|
|||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:alpha=".5"
|
android:alpha=".5"
|
||||||
android:nextFocusLeft="@id/module_card"
|
android:nextFocusLeft="@id/module_card"
|
||||||
android:onClick="@{() -> viewModel.infoPressed(item)}"
|
android:onClick="@{() -> viewModel.infoPressed(item.item)}"
|
||||||
android:paddingEnd="@dimen/l_50"
|
android:paddingEnd="@dimen/l_50"
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/module_download"
|
app:layout_constraintBottom_toBottomOf="@+id/module_download"
|
||||||
app:layout_constraintEnd_toStartOf="@+id/module_download"
|
app:layout_constraintEnd_toStartOf="@+id/module_download"
|
||||||
@ -113,11 +112,11 @@
|
|||||||
android:id="@+id/module_download"
|
android:id="@+id/module_download"
|
||||||
style="@style/WidgetFoundation.Icon.Primary"
|
style="@style/WidgetFoundation.Icon.Primary"
|
||||||
isEnabled="@{!(item.progress == -100 || (item.progress > 0 && item.progress < 100))}"
|
isEnabled="@{!(item.progress == -100 || (item.progress > 0 && item.progress < 100))}"
|
||||||
srcCompat="@{item.isUpdate ? R.drawable.ic_update_md2 : R.drawable.ic_download_md2}"
|
srcCompat="@{item.hasUpdate ? R.drawable.ic_update_md2 : R.drawable.ic_download_md2}"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:contentDescription="@string/download"
|
android:contentDescription="@string/download"
|
||||||
android:nextFocusLeft="@id/module_info"
|
android:nextFocusLeft="@id/module_info"
|
||||||
android:onClick="@{() -> viewModel.downloadPressed(item)}"
|
android:onClick="@{() -> viewModel.downloadPressed(item.item)}"
|
||||||
android:paddingStart="@dimen/l_50"
|
android:paddingStart="@dimen/l_50"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
@ -154,4 +153,3 @@
|
|||||||
</com.google.android.material.card.MaterialCardView>
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
</layout>
|
</layout>
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user