De-Rx ModuleViewModel
This commit is contained in:
parent
86db0cd2cd
commit
01a43b03bd
@ -131,7 +131,7 @@ class OpenChangelogEvent(val item: Repo) : ViewEvent(), ContextExecutor {
|
||||
}
|
||||
}
|
||||
|
||||
class PermissionEvent(
|
||||
class RxPermissionEvent(
|
||||
val permissions: List<String>,
|
||||
val callback: PublishSubject<Boolean>
|
||||
) : ViewEvent(), ActivityExecutor {
|
||||
@ -148,6 +148,22 @@ class PermissionEvent(
|
||||
}
|
||||
}
|
||||
|
||||
class PermissionEvent(
|
||||
private val permissions: List<String>,
|
||||
private val callback: (Boolean) -> Unit
|
||||
) : ViewEvent(), ActivityExecutor {
|
||||
|
||||
override fun invoke(activity: BaseActivity) =
|
||||
activity.withPermissions(*permissions.toTypedArray()) {
|
||||
onSuccess {
|
||||
callback(true)
|
||||
}
|
||||
onFailure {
|
||||
callback(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BackPressEvent : ViewEvent(), ActivityExecutor {
|
||||
override fun invoke(activity: BaseActivity) {
|
||||
activity.onBackPressed()
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.topjohnwu.magisk.ui.base
|
||||
|
||||
import android.Manifest
|
||||
import androidx.annotation.CallSuper
|
||||
import androidx.core.graphics.Insets
|
||||
import androidx.databinding.Bindable
|
||||
@ -88,7 +89,15 @@ abstract class BaseViewModel(
|
||||
|
||||
fun withPermissions(vararg permissions: String): Observable<Boolean> {
|
||||
val subject = PublishSubject.create<Boolean>()
|
||||
return subject.doOnSubscribeUi { PermissionEvent(permissions.toList(), subject).publish() }
|
||||
return subject.doOnSubscribeUi { RxPermissionEvent(permissions.toList(), subject).publish() }
|
||||
}
|
||||
|
||||
fun withPermissions(vararg permissions: String, callback: (Boolean) -> Unit) {
|
||||
PermissionEvent(permissions.toList(), callback).publish()
|
||||
}
|
||||
|
||||
fun withExternalRW(callback: (Boolean) -> Unit) {
|
||||
withPermissions(Manifest.permission.WRITE_EXTERNAL_STORAGE, callback = callback)
|
||||
}
|
||||
|
||||
fun back() = BackPressEvent().publish()
|
||||
|
@ -1,23 +1,17 @@
|
||||
package com.topjohnwu.magisk.ui.base
|
||||
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import androidx.core.os.postDelayed
|
||||
import com.topjohnwu.superuser.internal.UiThreadHandler
|
||||
|
||||
interface Queryable {
|
||||
|
||||
val queryDelay: Long
|
||||
val queryHandler: Handler
|
||||
val queryRunnable: Runnable
|
||||
val queryHandler: Handler get() = UiThreadHandler.handler
|
||||
|
||||
fun submitQuery()
|
||||
|
||||
companion object {
|
||||
fun impl(delay: Long = 1000L) = object : Queryable {
|
||||
override val queryDelay = delay
|
||||
override val queryHandler = Handler(Looper.getMainLooper())
|
||||
override val queryRunnable = Runnable { TODO() }
|
||||
|
||||
override fun submitQuery() {}
|
||||
}
|
||||
fun submitQuery() {
|
||||
queryHandler.postDelayed(queryDelay) { query() }
|
||||
}
|
||||
|
||||
fun query()
|
||||
}
|
||||
|
@ -21,16 +21,16 @@ import com.topjohnwu.magisk.utils.KObservableField
|
||||
|
||||
class HideViewModel(
|
||||
private val magiskRepo: MagiskRepository
|
||||
) : BaseViewModel(), Queryable by Queryable.impl(1000) {
|
||||
) : BaseViewModel(), Queryable {
|
||||
|
||||
override val queryRunnable = Runnable { query() }
|
||||
override val queryDelay = 1000L
|
||||
|
||||
var isShowSystem = false
|
||||
@Bindable get
|
||||
set(value) {
|
||||
field = value
|
||||
notifyPropertyChanged(BR.showSystem)
|
||||
query()
|
||||
submitQuery()
|
||||
}
|
||||
|
||||
var query = ""
|
||||
@ -81,17 +81,9 @@ class HideViewModel(
|
||||
|
||||
// ---
|
||||
|
||||
override fun submitQuery() {
|
||||
queryHandler.removeCallbacks(queryRunnable)
|
||||
queryHandler.postDelayed(queryRunnable, queryDelay)
|
||||
}
|
||||
|
||||
private fun query(
|
||||
query: String = this.query,
|
||||
showSystem: Boolean = isShowSystem
|
||||
) = items.filter {
|
||||
override fun query() = items.filter {
|
||||
fun filterSystem(): Boolean {
|
||||
return showSystem || it.item.info.info.flags and ApplicationInfo.FLAG_SYSTEM == 0
|
||||
return isShowSystem || it.item.info.info.flags and ApplicationInfo.FLAG_SYSTEM == 0
|
||||
}
|
||||
|
||||
fun filterQuery(): Boolean {
|
||||
|
@ -1,6 +1,5 @@
|
||||
package com.topjohnwu.magisk.ui.module
|
||||
|
||||
import android.Manifest
|
||||
import androidx.databinding.Bindable
|
||||
import androidx.databinding.ObservableArrayList
|
||||
import androidx.lifecycle.viewModelScope
|
||||
@ -16,7 +15,6 @@ import com.topjohnwu.magisk.data.database.RepoByUpdatedDao
|
||||
import com.topjohnwu.magisk.databinding.ComparableRvItem
|
||||
import com.topjohnwu.magisk.extensions.addOnListChangedCallback
|
||||
import com.topjohnwu.magisk.extensions.reboot
|
||||
import com.topjohnwu.magisk.extensions.subscribeK
|
||||
import com.topjohnwu.magisk.model.entity.internal.DownloadSubject
|
||||
import com.topjohnwu.magisk.model.entity.recycler.*
|
||||
import com.topjohnwu.magisk.model.events.InstallExternalModuleEvent
|
||||
@ -26,13 +24,9 @@ import com.topjohnwu.magisk.model.events.dialog.ModuleInstallDialog
|
||||
import com.topjohnwu.magisk.ui.base.*
|
||||
import com.topjohnwu.magisk.utils.EndlessRecyclerScrollListener
|
||||
import com.topjohnwu.magisk.utils.KObservableField
|
||||
import com.topjohnwu.superuser.internal.UiThreadHandler
|
||||
import io.reactivex.Single
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import kotlinx.coroutines.*
|
||||
import me.tatarka.bindingcollectionadapter2.collections.MergeObservableList
|
||||
import java.lang.Runnable
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
/*
|
||||
@ -53,9 +47,11 @@ class ModuleViewModel(
|
||||
private val repoName: RepoByNameDao,
|
||||
private val repoUpdated: RepoByUpdatedDao,
|
||||
private val repoUpdater: RepoUpdater
|
||||
) : BaseViewModel(), Queryable by Queryable.impl(1000) {
|
||||
) : BaseViewModel(), Queryable {
|
||||
|
||||
override val queryRunnable = Runnable { query() }
|
||||
override val queryDelay = 1000L
|
||||
private var queryJob: Job? = null
|
||||
private var remoteJob: Job? = null
|
||||
|
||||
var query = ""
|
||||
@Bindable get
|
||||
@ -68,7 +64,6 @@ class ModuleViewModel(
|
||||
searchLoading.value = true
|
||||
}
|
||||
|
||||
private var queryJob: Disposable? = null
|
||||
val searchLoading = KObservableField(false)
|
||||
val itemsSearch = diffListOf<RepoItem>()
|
||||
val itemSearchBinding = itemBindingOf<RepoItem> {
|
||||
@ -213,16 +208,15 @@ class ModuleViewModel(
|
||||
state = State.LOADED
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun loadRemote() {
|
||||
// check for existing jobs
|
||||
if (isRemoteLoading)
|
||||
if (remoteJob?.isActive == true)
|
||||
return
|
||||
if (itemsRemote.isEmpty()) {
|
||||
EndlessRecyclerScrollListener.ResetState().publish()
|
||||
}
|
||||
|
||||
viewModelScope.launch {
|
||||
if (itemsRemote.isEmpty())
|
||||
EndlessRecyclerScrollListener.ResetState().publish()
|
||||
|
||||
remoteJob = viewModelScope.launch {
|
||||
suspend fun loadRemoteDB(offset: Int) = withContext(Dispatchers.IO) {
|
||||
dao.getRepos(offset).map { RepoItem.Remote(it) }
|
||||
}
|
||||
@ -236,7 +230,7 @@ class ModuleViewModel(
|
||||
}
|
||||
isRemoteLoading = false
|
||||
refetch = false
|
||||
UiThreadHandler.handler.post { itemsRemote.addAll(repos) }
|
||||
queryHandler.post { itemsRemote.addAll(repos) }
|
||||
}
|
||||
}
|
||||
|
||||
@ -250,44 +244,44 @@ class ModuleViewModel(
|
||||
|
||||
// ---
|
||||
|
||||
override fun submitQuery() {
|
||||
queryHandler.removeCallbacks(queryRunnable)
|
||||
queryHandler.postDelayed(queryRunnable, queryDelay)
|
||||
}
|
||||
|
||||
private fun queryInternal(query: String, offset: Int): Single<List<RepoItem>> {
|
||||
if (query.isBlank()) {
|
||||
return Single.just(listOf<RepoItem>())
|
||||
.doOnSubscribe { itemsSearch.clear() }
|
||||
.subscribeOn(AndroidSchedulers.mainThread())
|
||||
private suspend fun queryInternal(query: String, offset: Int): List<RepoItem> {
|
||||
return if (query.isBlank()) {
|
||||
itemsSearch.clear()
|
||||
listOf()
|
||||
} else {
|
||||
withContext(Dispatchers.IO) {
|
||||
dao.searchRepos(query, offset).map { RepoItem.Remote(it) }
|
||||
}
|
||||
}
|
||||
return Single.fromCallable { dao.searchRepos(query, offset) }
|
||||
.map { it.map { RepoItem.Remote(it) } }
|
||||
}
|
||||
|
||||
private fun query(query: String = this.query, offset: Int = 0) {
|
||||
queryJob?.dispose()
|
||||
queryJob = queryInternal(query, offset)
|
||||
.map { it to itemsSearch.calculateDiff(it) }
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doOnSuccess { searchLoading.value = false }
|
||||
.subscribeK { itemsSearch.update(it.first, it.second) }
|
||||
override fun query() {
|
||||
queryJob = viewModelScope.launch {
|
||||
val searched = queryInternal(query, 0)
|
||||
val diff = withContext(Dispatchers.Default) {
|
||||
itemsSearch.calculateDiff(searched)
|
||||
}
|
||||
searchLoading.value = false
|
||||
itemsSearch.update(searched, diff)
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun loadMoreQuery() {
|
||||
if (queryJob?.isDisposed == false) return
|
||||
queryJob = queryInternal(query, itemsSearch.size)
|
||||
.subscribeK { itemsSearch.addAll(it) }
|
||||
if (queryJob?.isActive == true) return
|
||||
queryJob = viewModelScope.launch {
|
||||
val searched = queryInternal(query, itemsSearch.size)
|
||||
queryHandler.post { itemsSearch.addAll(searched) }
|
||||
}
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
private fun update(repo: Repo, progress: Int) =
|
||||
Single.fromCallable { itemsRemote + itemsSearch }
|
||||
.map { it.first { it.item.id == repo.id } }
|
||||
.subscribeK { it.progress.value = progress }
|
||||
.add()
|
||||
private fun update(repo: Repo, progress: Int) = viewModelScope.launch {
|
||||
val item = withContext(Dispatchers.Default) {
|
||||
(itemsRemote + itemsSearch).first { it.item.id == repo.id }
|
||||
}
|
||||
item.progress.value = progress
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
@ -305,12 +299,14 @@ class ModuleViewModel(
|
||||
|
||||
// ---
|
||||
|
||||
fun updateActiveState() = Single.fromCallable { itemsInstalled.any { it.isModified } }
|
||||
.subscribeK { sectionActive.hasButton = it }
|
||||
.add()
|
||||
fun updateActiveState() = viewModelScope.launch {
|
||||
sectionActive.hasButton = withContext(Dispatchers.Default) {
|
||||
itemsInstalled.any { it.isModified }
|
||||
}
|
||||
}
|
||||
|
||||
fun sectionPressed(item: SectionTitle) = when (item) {
|
||||
sectionActive -> reboot() //TODO add reboot picker, regular reboot is not always preferred
|
||||
sectionActive -> reboot() // TODO add reboot picker, regular reboot is not always preferred
|
||||
sectionRemote -> {
|
||||
Config.repoOrder = when (Config.repoOrder) {
|
||||
Config.Value.ORDER_NAME -> Config.Value.ORDER_DATE
|
||||
@ -318,28 +314,28 @@ class ModuleViewModel(
|
||||
else -> Config.Value.ORDER_NAME
|
||||
}
|
||||
updateOrderIcon()
|
||||
Single.fromCallable { itemsRemote }
|
||||
.subscribeK {
|
||||
itemsRemote.removeAll(it)
|
||||
loadRemote()
|
||||
}.add()
|
||||
queryHandler.post {
|
||||
itemsRemote.clear()
|
||||
loadRemote()
|
||||
}
|
||||
Unit
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
|
||||
fun downloadPressed(item: RepoItem) = withPermissions(
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE,
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
).any { it }.subscribeK(onError = { permissionDenied() }) {
|
||||
ModuleInstallDialog(item.item).publish()
|
||||
}.add()
|
||||
fun downloadPressed(item: RepoItem) = withExternalRW {
|
||||
if (it)
|
||||
ModuleInstallDialog(item.item).publish()
|
||||
else
|
||||
permissionDenied()
|
||||
}
|
||||
|
||||
fun installPressed() = withPermissions(
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE,
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
).any { it }.subscribeK(onError = { permissionDenied() }) {
|
||||
InstallExternalModuleEvent().publish()
|
||||
}.add()
|
||||
fun installPressed() = withExternalRW {
|
||||
if (it)
|
||||
InstallExternalModuleEvent().publish()
|
||||
else
|
||||
permissionDenied()
|
||||
}
|
||||
|
||||
fun infoPressed(item: RepoItem) = OpenChangelogEvent(item.item).publish()
|
||||
fun infoPressed(item: ModuleItem) {
|
||||
|
@ -16,7 +16,7 @@ import com.topjohnwu.magisk.extensions.subscribeK
|
||||
import com.topjohnwu.magisk.model.entity.internal.Configuration
|
||||
import com.topjohnwu.magisk.model.entity.internal.DownloadSubject
|
||||
import com.topjohnwu.magisk.model.entity.recycler.SettingsItem
|
||||
import com.topjohnwu.magisk.model.events.PermissionEvent
|
||||
import com.topjohnwu.magisk.model.events.RxPermissionEvent
|
||||
import com.topjohnwu.magisk.model.events.RecreateEvent
|
||||
import com.topjohnwu.magisk.model.events.dialog.BiometricDialog
|
||||
import com.topjohnwu.magisk.ui.base.BaseViewModel
|
||||
@ -137,7 +137,7 @@ class SettingsViewModel(
|
||||
private fun requireRWPermission() {
|
||||
val callback = PublishSubject.create<Boolean>()
|
||||
callback.subscribeK { if (!it) requireRWPermission() }
|
||||
PermissionEvent(
|
||||
RxPermissionEvent(
|
||||
listOf(
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE,
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
|
Loading…
Reference in New Issue
Block a user