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