Move coroutine job into its own class

This commit is contained in:
topjohnwu 2022-06-10 04:12:31 -07:00
parent 46d4708386
commit 515f81944c
9 changed files with 54 additions and 54 deletions

View File

@ -0,0 +1,22 @@
package com.topjohnwu.magisk.arch
import androidx.annotation.MainThread
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
abstract class AsyncLoadViewModel : BaseViewModel() {
private var loadingJob: Job? = null
@MainThread
fun startLoading() {
if (loadingJob?.isActive == true) {
// Prevent multiple jobs from running at the same time
return
}
loadingJob = viewModelScope.launch { doLoadWork() }
}
protected abstract suspend fun doLoadWork()
}

View File

@ -76,7 +76,10 @@ abstract class BaseFragment<Binding : ViewDataBinding> : Fragment(), ViewModelHo
override fun onResume() {
super.onResume()
viewModel.requestRefresh()
viewModel.let {
if (it is AsyncLoadViewModel)
it.startLoading()
}
}
protected open fun onPreBind(binding: Binding) {

View File

@ -16,31 +16,16 @@ import com.topjohnwu.magisk.events.BackPressEvent
import com.topjohnwu.magisk.events.NavigationEvent
import com.topjohnwu.magisk.events.PermissionEvent
import com.topjohnwu.magisk.events.SnackbarEvent
import kotlinx.coroutines.Job
abstract class BaseViewModel : ViewModel(), ObservableHost {
override var callbacks: PropertyChangeRegistry? = null
val viewEvents: LiveData<ViewEvent> get() = _viewEvents
private val _viewEvents = MutableLiveData<ViewEvent>()
private var runningJob: Job? = null
val viewEvents: LiveData<ViewEvent> get() = _viewEvents
open fun onSaveState(state: Bundle) {}
open fun onRestoreState(state: Bundle) {}
/** This should probably never be called manually, it's called manually via delegate. */
@Synchronized
fun requestRefresh() {
if (runningJob?.isActive == true) {
return
}
runningJob = refresh()
}
protected open fun refresh(): Job? = null
open fun onNetworkChanged(network: Boolean) {}
fun withPermission(permission: String, callback: (Boolean) -> Unit) {

View File

@ -89,7 +89,10 @@ abstract class UIActivity<Binding : ViewDataBinding> : BaseActivity(), ViewModel
override fun onResume() {
super.onResume()
viewModel.requestRefresh()
viewModel.let {
if (it is AsyncLoadViewModel)
it.startLoading()
}
}
override fun onEventDispatched(event: ViewEvent) = when (event) {

View File

@ -3,9 +3,8 @@ package com.topjohnwu.magisk.ui.deny
import android.annotation.SuppressLint
import android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES
import androidx.databinding.Bindable
import androidx.lifecycle.viewModelScope
import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.arch.BaseViewModel
import com.topjohnwu.magisk.arch.AsyncLoadViewModel
import com.topjohnwu.magisk.core.di.AppContext
import com.topjohnwu.magisk.databinding.bindExtra
import com.topjohnwu.magisk.databinding.filterableListOf
@ -16,10 +15,9 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.toCollection
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class DenyListViewModel : BaseViewModel() {
class DenyListViewModel : AsyncLoadViewModel() {
var isShowSystem = false
set(value) {
@ -49,7 +47,7 @@ class DenyListViewModel : BaseViewModel() {
private set(value) = set(value, field, { field = it }, BR.loading)
@SuppressLint("InlinedApi")
override fun refresh() = viewModelScope.launch {
override suspend fun doLoadWork() {
loading = true
val (apps, diff) = withContext(Dispatchers.Default) {
val pm = AppContext.packageManager

View File

@ -3,7 +3,6 @@ package com.topjohnwu.magisk.ui.home
import android.content.Context
import androidx.core.net.toUri
import androidx.databinding.Bindable
import androidx.lifecycle.viewModelScope
import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.BuildConfig
import com.topjohnwu.magisk.R
@ -23,12 +22,11 @@ import com.topjohnwu.magisk.ktx.await
import com.topjohnwu.magisk.utils.Utils
import com.topjohnwu.magisk.utils.asText
import com.topjohnwu.superuser.Shell
import kotlinx.coroutines.launch
import kotlin.math.roundToInt
class HomeViewModel(
private val svc: NetworkService
) : BaseViewModel() {
) : AsyncLoadViewModel() {
enum class State {
LOADING, INVALID, OUTDATED, UP_TO_DATE
@ -83,7 +81,7 @@ class HomeViewModel(
private var checkedEnv = false
}
override fun refresh() = viewModelScope.launch {
override suspend fun doLoadWork() {
appState = State.LOADING
Info.getRemote(svc)?.apply {
appState = when {
@ -101,9 +99,7 @@ class HomeViewModel(
ensureEnv()
}
override fun onNetworkChanged(network: Boolean) {
requestRefresh()
}
override fun onNetworkChanged(network: Boolean) = startLoading()
fun onProgressUpdate(progress: Float, subject: Subject) {
if (subject is App)

View File

@ -5,7 +5,7 @@ import androidx.lifecycle.viewModelScope
import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.BuildConfig
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.arch.BaseViewModel
import com.topjohnwu.magisk.arch.AsyncLoadViewModel
import com.topjohnwu.magisk.core.Info
import com.topjohnwu.magisk.core.repository.LogRepository
import com.topjohnwu.magisk.core.utils.MediaStoreUtils
@ -24,7 +24,7 @@ import java.io.FileInputStream
class LogViewModel(
private val repo: LogRepository
) : BaseViewModel() {
) : AsyncLoadViewModel() {
// --- empty view
@ -43,7 +43,7 @@ class LogViewModel(
var consoleText = " "
set(value) = set(value, field, { field = it }, BR.consoleText)
override fun refresh() = viewModelScope.launch {
override suspend fun doLoadWork() {
consoleText = repo.fetchMagiskLogs()
val (suLogs, diff) = withContext(Dispatchers.Default) {
val suLogs = repo.fetchSuLogs().map { LogRvItem(it) }
@ -89,12 +89,12 @@ class LogViewModel(
fun clearMagiskLog() = repo.clearMagiskLogs {
SnackbarEvent(R.string.logs_cleared).publish()
requestRefresh()
startLoading()
}
fun clearLog() = viewModelScope.launch {
repo.clearLogs()
SnackbarEvent(R.string.logs_cleared).publish()
requestRefresh()
startLoading()
}
}

View File

@ -3,10 +3,9 @@ package com.topjohnwu.magisk.ui.module
import android.net.Uri
import androidx.databinding.Bindable
import androidx.lifecycle.MutableLiveData
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.AsyncLoadViewModel
import com.topjohnwu.magisk.core.Info
import com.topjohnwu.magisk.core.base.ContentResultCallback
import com.topjohnwu.magisk.core.model.module.LocalModule
@ -16,12 +15,10 @@ import com.topjohnwu.magisk.events.GetContentEvent
import com.topjohnwu.magisk.events.SnackbarEvent
import com.topjohnwu.magisk.events.dialog.ModuleInstallDialog
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.parcelize.Parcelize
class ModuleViewModel : BaseViewModel() {
class ModuleViewModel : AsyncLoadViewModel() {
val bottomBarBarrierIds = intArrayOf(R.id.module_update, R.id.module_remove)
@ -45,18 +42,14 @@ class ModuleViewModel : BaseViewModel() {
}
}
override fun refresh(): Job {
return viewModelScope.launch {
loading = true
loadInstalled()
loading = false
loadUpdateInfo()
}
override suspend fun doLoadWork() {
loading = true
loadInstalled()
loading = false
loadUpdateInfo()
}
override fun onNetworkChanged(network: Boolean) {
requestRefresh()
}
override fun onNetworkChanged(network: Boolean) = startLoading()
private suspend fun loadInstalled() {
val installed = LocalModule.installed().map { LocalModuleRvItem(it) }

View File

@ -9,7 +9,7 @@ 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.AsyncLoadViewModel
import com.topjohnwu.magisk.core.data.magiskdb.PolicyDao
import com.topjohnwu.magisk.core.di.AppContext
import com.topjohnwu.magisk.core.model.su.SuPolicy
@ -29,7 +29,7 @@ import kotlinx.coroutines.withContext
class SuperuserViewModel(
private val db: PolicyDao
) : BaseViewModel() {
) : AsyncLoadViewModel() {
private val itemNoData = TextItem(R.string.superuser_policy_none)
@ -48,10 +48,10 @@ class SuperuserViewModel(
private set(value) = set(value, field, { field = it }, BR.loading)
@SuppressLint("InlinedApi")
override fun refresh() = viewModelScope.launch {
override suspend fun doLoadWork() {
if (!Utils.showSuperUser()) {
loading = false
return@launch
return
}
loading = true
val (policies, diff) = withContext(Dispatchers.IO) {