De-Rx ModuleViewModel

This commit is contained in:
topjohnwu 2020-07-08 01:26:45 -07:00
parent 86db0cd2cd
commit 01a43b03bd
6 changed files with 102 additions and 95 deletions

View File

@ -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()

View File

@ -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()

View File

@ -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()
}

View File

@ -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 {

View File

@ -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) {

View File

@ -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