Consolidate base viewmodel implementation

This commit is contained in:
topjohnwu 2020-01-13 03:56:03 +08:00
parent 3490ba0a56
commit 84f1e78660
26 changed files with 233 additions and 278 deletions

View File

@ -16,7 +16,6 @@ import androidx.databinding.ViewDataBinding
import com.topjohnwu.magisk.BR import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.Config import com.topjohnwu.magisk.Config
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.base.viewmodel.BaseViewModel
import com.topjohnwu.magisk.extensions.set import com.topjohnwu.magisk.extensions.set
import com.topjohnwu.magisk.model.events.EventHandler import com.topjohnwu.magisk.model.events.EventHandler
import com.topjohnwu.magisk.model.permissions.PermissionRequestBuilder import com.topjohnwu.magisk.model.permissions.PermissionRequestBuilder

View File

@ -8,7 +8,6 @@ import androidx.databinding.DataBindingUtil
import androidx.databinding.ViewDataBinding import androidx.databinding.ViewDataBinding
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import com.topjohnwu.magisk.BR import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.base.viewmodel.BaseViewModel
import com.topjohnwu.magisk.model.events.EventHandler import com.topjohnwu.magisk.model.events.EventHandler
import com.topjohnwu.magisk.model.events.ViewEvent import com.topjohnwu.magisk.model.events.ViewEvent

View File

@ -0,0 +1,201 @@
package com.topjohnwu.magisk.base
import androidx.annotation.CallSuper
import androidx.core.graphics.Insets
import androidx.databinding.Bindable
import androidx.databinding.PropertyChangeRegistry
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.Info
import com.topjohnwu.magisk.extensions.doOnSubscribeUi
import com.topjohnwu.magisk.model.events.*
import com.topjohnwu.magisk.model.observer.Observer
import com.topjohnwu.magisk.utils.KObservableField
import io.reactivex.*
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.disposables.Disposable
import io.reactivex.subjects.PublishSubject
import org.koin.core.KoinComponent
import androidx.databinding.Observable as BindingObservable
abstract class BaseViewModel(
initialState: State = State.LOADING
) : ViewModel(), BindingObservable, KoinComponent {
enum class State {
LOADED, LOADING, LOADING_FAILED
}
val loading @Bindable get() = state == State.LOADING
val loaded @Bindable get() = state == State.LOADED
val loadingFailed @Bindable get() = state == State.LOADING_FAILED
val isConnected = Observer(Info.isConnected) { Info.isConnected.value }
val viewEvents: LiveData<ViewEvent> get() = _viewEvents
val insets = KObservableField(Insets.NONE)
var state: State = initialState
set(value) {
field = value
notifyStateChanged()
}
private val disposables = CompositeDisposable()
private val _viewEvents = MutableLiveData<ViewEvent>()
private var runningTask: Disposable? = null
private val refreshCallback = object : androidx.databinding.Observable.OnPropertyChangedCallback() {
override fun onPropertyChanged(sender: androidx.databinding.Observable?, propertyId: Int) {
requestRefresh()
}
}
init {
isConnected.addOnPropertyChangedCallback(refreshCallback)
}
/** This should probably never be called manually, it's called manually via delegate. */
@Synchronized
fun requestRefresh() {
if (runningTask?.isDisposed?.not() == true) {
return
}
runningTask = refresh()
}
protected open fun refresh(): Disposable? = null
open fun notifyStateChanged() {
notifyPropertyChanged(BR.loading)
notifyPropertyChanged(BR.loaded)
notifyPropertyChanged(BR.loadingFailed)
}
@CallSuper
override fun onCleared() {
isConnected.removeOnPropertyChangedCallback(refreshCallback)
disposables.clear()
super.onCleared()
}
fun withView(action: BaseActivity<*, *>.() -> Unit) {
ViewActionEvent(action).publish()
}
fun withPermissions(vararg permissions: String): Observable<Boolean> {
val subject = PublishSubject.create<Boolean>()
return subject.doOnSubscribeUi { PermissionEvent(permissions.toList(), subject).publish() }
}
fun back() = BackPressEvent().publish()
fun <Event : ViewEvent> Event.publish() {
_viewEvents.postValue(this)
}
fun Int.publish() {
_viewEvents.postValue(SimpleViewEvent(this))
}
fun Disposable.add() {
disposables.add(this)
}
// The following is copied from androidx.databinding.BaseObservable
@Transient
private var callbacks: PropertyChangeRegistry? = null
@Synchronized
override fun addOnPropertyChangedCallback(callback: BindingObservable.OnPropertyChangedCallback) {
if (callbacks == null) {
callbacks = PropertyChangeRegistry()
}
callbacks?.add(callback)
}
@Synchronized
override fun removeOnPropertyChangedCallback(callback: BindingObservable.OnPropertyChangedCallback) {
callbacks?.remove(callback)
}
/**
* Notifies listeners that all properties of this instance have changed.
*/
@Synchronized
fun notifyChange() {
callbacks?.notifyCallbacks(this, 0, null)
}
/**
* Notifies listeners that a specific property has changed. The getter for the property
* that changes should be marked with [androidx.databinding.Bindable] to generate a field in
* `BR` to be used as `fieldId`.
*
* @param fieldId The generated BR id for the Bindable field.
*/
fun notifyPropertyChanged(fieldId: Int) {
callbacks?.notifyCallbacks(this, fieldId, null)
}
//region Rx
protected fun <T> Observable<T>.applyViewModel(viewModel: BaseViewModel, allowFinishing: Boolean = true) =
doOnSubscribe { viewModel.state =
State.LOADING
}
.doOnError { viewModel.state =
State.LOADING_FAILED
}
.doOnNext { if (allowFinishing) viewModel.state =
State.LOADED
}
protected fun <T> Single<T>.applyViewModel(viewModel: BaseViewModel, allowFinishing: Boolean = true) =
doOnSubscribe { viewModel.state =
State.LOADING
}
.doOnError { viewModel.state =
State.LOADING_FAILED
}
.doOnSuccess { if (allowFinishing) viewModel.state =
State.LOADED
}
protected fun <T> Maybe<T>.applyViewModel(viewModel: BaseViewModel, allowFinishing: Boolean = true) =
doOnSubscribe { viewModel.state =
State.LOADING
}
.doOnError { viewModel.state =
State.LOADING_FAILED
}
.doOnComplete { if (allowFinishing) viewModel.state =
State.LOADED
}
.doOnSuccess { if (allowFinishing) viewModel.state =
State.LOADED
}
protected fun <T> Flowable<T>.applyViewModel(viewModel: BaseViewModel, allowFinishing: Boolean = true) =
doOnSubscribe { viewModel.state =
State.LOADING
}
.doOnError { viewModel.state =
State.LOADING_FAILED
}
.doOnNext { if (allowFinishing) viewModel.state =
State.LOADED
}
protected fun Completable.applyViewModel(viewModel: BaseViewModel, allowFinishing: Boolean = true) =
doOnSubscribe { viewModel.state =
State.LOADING
}
.doOnError { viewModel.state =
State.LOADING_FAILED
}
.doOnComplete { if (allowFinishing) viewModel.state =
State.LOADED
}
//endregion
}

View File

@ -1,31 +0,0 @@
package com.topjohnwu.magisk.base.viewmodel
import com.topjohnwu.magisk.base.BaseActivity
import com.topjohnwu.magisk.extensions.doOnSubscribeUi
import com.topjohnwu.magisk.model.events.BackPressEvent
import com.topjohnwu.magisk.model.events.PermissionEvent
import com.topjohnwu.magisk.model.events.ViewActionEvent
import com.topjohnwu.magisk.model.observer.Observer
import io.reactivex.Observable
import io.reactivex.subjects.PublishSubject
import com.topjohnwu.magisk.Info.isConnected as gIsConnected
abstract class BaseViewModel(
initialState: State = State.LOADING
) : LoadingViewModel(initialState) {
val isConnected = Observer(gIsConnected) { gIsConnected.value }
fun withView(action: BaseActivity<*, *>.() -> Unit) {
ViewActionEvent(action).publish()
}
fun withPermissions(vararg permissions: String): Observable<Boolean> {
val subject = PublishSubject.create<Boolean>()
return subject.doOnSubscribeUi { PermissionEvent(permissions.toList(), subject).publish() }
}
fun back() = BackPressEvent().publish()
}

View File

@ -1,78 +0,0 @@
package com.topjohnwu.magisk.base.viewmodel
import androidx.databinding.Bindable
import com.topjohnwu.magisk.BR
import io.reactivex.*
abstract class LoadingViewModel(defaultState: State = State.LOADING) :
StatefulViewModel<LoadingViewModel.State>(defaultState) {
val loading @Bindable get() = state == State.LOADING
val loaded @Bindable get() = state == State.LOADED
val loadingFailed @Bindable get() = state == State.LOADING_FAILED
@Deprecated(
"Direct access is recommended since 0.2. This access method will be removed in 1.0",
ReplaceWith("state = State.LOADING", "com.topjohnwu.magisk.base.viewmodel.LoadingViewModel.State"),
DeprecationLevel.WARNING
)
fun setLoading() {
state = State.LOADING
}
@Deprecated(
"Direct access is recommended since 0.2. This access method will be removed in 1.0",
ReplaceWith("state = State.LOADED", "com.topjohnwu.magisk.base.viewmodel.LoadingViewModel.State"),
DeprecationLevel.WARNING
)
fun setLoaded() {
state = State.LOADED
}
@Deprecated(
"Direct access is recommended since 0.2. This access method will be removed in 1.0",
ReplaceWith("state = State.LOADING_FAILED", "com.topjohnwu.magisk.base.viewmodel.LoadingViewModel.State"),
DeprecationLevel.WARNING
)
fun setLoadingFailed() {
state = State.LOADING_FAILED
}
override fun notifyStateChanged() {
notifyPropertyChanged(BR.loading)
notifyPropertyChanged(BR.loaded)
notifyPropertyChanged(BR.loadingFailed)
}
enum class State {
LOADED, LOADING, LOADING_FAILED
}
//region Rx
protected fun <T> Observable<T>.applyViewModel(viewModel: LoadingViewModel, allowFinishing: Boolean = true) =
doOnSubscribe { viewModel.state = State.LOADING }
.doOnError { viewModel.state = State.LOADING_FAILED }
.doOnNext { if (allowFinishing) viewModel.state = State.LOADED }
protected fun <T> Single<T>.applyViewModel(viewModel: LoadingViewModel, allowFinishing: Boolean = true) =
doOnSubscribe { viewModel.state = State.LOADING }
.doOnError { viewModel.state = State.LOADING_FAILED }
.doOnSuccess { if (allowFinishing) viewModel.state = State.LOADED }
protected fun <T> Maybe<T>.applyViewModel(viewModel: LoadingViewModel, allowFinishing: Boolean = true) =
doOnSubscribe { viewModel.state = State.LOADING }
.doOnError { viewModel.state = State.LOADING_FAILED }
.doOnComplete { if (allowFinishing) viewModel.state = State.LOADED }
.doOnSuccess { if (allowFinishing) viewModel.state = State.LOADED }
protected fun <T> Flowable<T>.applyViewModel(viewModel: LoadingViewModel, allowFinishing: Boolean = true) =
doOnSubscribe { viewModel.state = State.LOADING }
.doOnError { viewModel.state = State.LOADING_FAILED }
.doOnNext { if (allowFinishing) viewModel.state = State.LOADED }
protected fun Completable.applyViewModel(viewModel: LoadingViewModel, allowFinishing: Boolean = true) =
doOnSubscribe { viewModel.state = State.LOADING }
.doOnError { viewModel.state = State.LOADING_FAILED }
.doOnComplete { if (allowFinishing) viewModel.state = State.LOADED }
//endregion
}

View File

@ -1,46 +0,0 @@
package com.topjohnwu.magisk.base.viewmodel
import androidx.databinding.Observable
import androidx.databinding.PropertyChangeRegistry
import androidx.lifecycle.ViewModel
/**
* Copy of [androidx.databinding.BaseObservable] which extends [ViewModel]
*/
abstract class ObservableViewModel : TeanityViewModel(), Observable {
@Transient
private var callbacks: PropertyChangeRegistry? = null
@Synchronized
override fun addOnPropertyChangedCallback(callback: Observable.OnPropertyChangedCallback) {
if (callbacks == null) {
callbacks = PropertyChangeRegistry()
}
callbacks?.add(callback)
}
@Synchronized
override fun removeOnPropertyChangedCallback(callback: Observable.OnPropertyChangedCallback) {
callbacks?.remove(callback)
}
/**
* Notifies listeners that all properties of this instance have changed.
*/
@Synchronized
fun notifyChange() {
callbacks?.notifyCallbacks(this, 0, null)
}
/**
* Notifies listeners that a specific property has changed. The getter for the property
* that changes should be marked with [androidx.databinding.Bindable] to generate a field in
* `BR` to be used as `fieldId`.
*
* @param fieldId The generated BR id for the Bindable field.
*/
fun notifyPropertyChanged(fieldId: Int) {
callbacks?.notifyCallbacks(this, fieldId, null)
}
}

View File

@ -1,15 +0,0 @@
package com.topjohnwu.magisk.base.viewmodel
abstract class StatefulViewModel<State : Enum<*>>(
val defaultState: State
) : ObservableViewModel() {
var state: State = defaultState
set(value) {
field = value
notifyStateChanged()
}
open fun notifyStateChanged() = Unit
}

View File

@ -1,33 +0,0 @@
package com.topjohnwu.magisk.base.viewmodel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.topjohnwu.magisk.model.events.SimpleViewEvent
import com.topjohnwu.magisk.model.events.ViewEvent
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.disposables.Disposable
abstract class TeanityViewModel : ViewModel() {
private val disposables = CompositeDisposable()
private val _viewEvents = MutableLiveData<ViewEvent>()
val viewEvents: LiveData<ViewEvent> get() = _viewEvents
override fun onCleared() {
super.onCleared()
disposables.clear()
}
fun <Event : ViewEvent> Event.publish() {
_viewEvents.postValue(this)
}
fun Int.publish() {
_viewEvents.postValue(SimpleViewEvent(this))
}
fun Disposable.add() {
disposables.add(this)
}
}

View File

@ -11,7 +11,7 @@ import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.Config import com.topjohnwu.magisk.Config
import com.topjohnwu.magisk.Const import com.topjohnwu.magisk.Const
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.base.viewmodel.BaseViewModel import com.topjohnwu.magisk.base.BaseViewModel
import com.topjohnwu.magisk.databinding.ComparableRvItem import com.topjohnwu.magisk.databinding.ComparableRvItem
import com.topjohnwu.magisk.extensions.* import com.topjohnwu.magisk.extensions.*
import com.topjohnwu.magisk.model.entity.recycler.ConsoleRvItem import com.topjohnwu.magisk.model.entity.recycler.ConsoleRvItem

View File

@ -10,7 +10,7 @@ import com.topjohnwu.magisk.BuildConfig
import com.topjohnwu.magisk.Config import com.topjohnwu.magisk.Config
import com.topjohnwu.magisk.Const import com.topjohnwu.magisk.Const
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.base.viewmodel.BaseViewModel import com.topjohnwu.magisk.base.BaseViewModel
import com.topjohnwu.magisk.data.database.PolicyDao import com.topjohnwu.magisk.data.database.PolicyDao
import com.topjohnwu.magisk.databinding.ComparableRvItem import com.topjohnwu.magisk.databinding.ComparableRvItem
import com.topjohnwu.magisk.extensions.now import com.topjohnwu.magisk.extensions.now

View File

@ -1,5 +1,5 @@
package com.topjohnwu.magisk.ui package com.topjohnwu.magisk.ui
import com.topjohnwu.magisk.ui.compat.CompatViewModel import com.topjohnwu.magisk.base.BaseViewModel
class MainViewModel : CompatViewModel() class MainViewModel : BaseViewModel()

View File

@ -11,6 +11,7 @@ import androidx.databinding.OnRebindCallback
import androidx.databinding.ViewDataBinding import androidx.databinding.ViewDataBinding
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import com.topjohnwu.magisk.base.BaseActivity import com.topjohnwu.magisk.base.BaseActivity
import com.topjohnwu.magisk.base.BaseViewModel
import com.topjohnwu.magisk.extensions.snackbar import com.topjohnwu.magisk.extensions.snackbar
import com.topjohnwu.magisk.extensions.startAnimations import com.topjohnwu.magisk.extensions.startAnimations
import com.topjohnwu.magisk.model.events.SnackbarEvent import com.topjohnwu.magisk.model.events.SnackbarEvent
@ -21,7 +22,7 @@ import com.topjohnwu.magisk.ui.theme.Theme
import kotlin.reflect.KClass import kotlin.reflect.KClass
abstract class CompatActivity<ViewModel : CompatViewModel, Binding : ViewDataBinding> : abstract class CompatActivity<ViewModel : BaseViewModel, Binding : ViewDataBinding> :
BaseActivity<ViewModel, Binding>(), CompatView<ViewModel>, Navigator { BaseActivity<ViewModel, Binding>(), CompatView<ViewModel>, Navigator {
override val themeRes = Theme.selected.themeRes override val themeRes = Theme.selected.themeRes

View File

@ -7,10 +7,11 @@ import androidx.databinding.OnRebindCallback
import androidx.databinding.ViewDataBinding import androidx.databinding.ViewDataBinding
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import com.topjohnwu.magisk.base.BaseFragment import com.topjohnwu.magisk.base.BaseFragment
import com.topjohnwu.magisk.base.BaseViewModel
import com.topjohnwu.magisk.extensions.startAnimations import com.topjohnwu.magisk.extensions.startAnimations
import com.topjohnwu.magisk.model.events.ViewEvent import com.topjohnwu.magisk.model.events.ViewEvent
abstract class CompatFragment<ViewModel : CompatViewModel, Binding : ViewDataBinding> abstract class CompatFragment<ViewModel : BaseViewModel, Binding : ViewDataBinding>
: BaseFragment<ViewModel, Binding>(), CompatView<ViewModel> { : BaseFragment<ViewModel, Binding>(), CompatView<ViewModel> {
override val viewRoot: View get() = binding.root override val viewRoot: View get() = binding.root

View File

@ -2,8 +2,9 @@ package com.topjohnwu.magisk.ui.compat
import android.view.View import android.view.View
import androidx.core.graphics.Insets import androidx.core.graphics.Insets
import com.topjohnwu.magisk.base.BaseViewModel
internal interface CompatView<ViewModel : CompatViewModel> { internal interface CompatView<ViewModel : BaseViewModel> {
val viewRoot: View val viewRoot: View
val viewModel: ViewModel val viewModel: ViewModel

View File

@ -1,45 +0,0 @@
package com.topjohnwu.magisk.ui.compat
import androidx.annotation.CallSuper
import androidx.core.graphics.Insets
import androidx.databinding.Observable
import com.topjohnwu.magisk.base.viewmodel.BaseViewModel
import com.topjohnwu.magisk.utils.KObservableField
import io.reactivex.disposables.Disposable
import org.koin.core.KoinComponent
abstract class CompatViewModel(
initialState: State = State.LOADING
) : BaseViewModel(initialState), KoinComponent {
val insets = KObservableField(Insets.NONE)
private var runningTask: Disposable? = null
private val refreshCallback = object : Observable.OnPropertyChangedCallback() {
override fun onPropertyChanged(sender: Observable?, propertyId: Int) {
requestRefresh()
}
}
init {
isConnected.addOnPropertyChangedCallback(refreshCallback)
}
/** This should probably never be called manually, it's called manually via delegate. */
@Synchronized
fun requestRefresh() {
if (runningTask?.isDisposed?.not() == true) {
return
}
runningTask = refresh()
}
protected open fun refresh(): Disposable? = null
@CallSuper
override fun onCleared() {
isConnected.removeOnPropertyChangedCallback(refreshCallback)
super.onCleared()
}
}

View File

@ -1,5 +1,5 @@
package com.topjohnwu.magisk.ui.flash package com.topjohnwu.magisk.ui.flash
import com.topjohnwu.magisk.ui.compat.CompatViewModel import com.topjohnwu.magisk.base.BaseViewModel
class FlashViewModel : CompatViewModel() class FlashViewModel : BaseViewModel()

View File

@ -12,7 +12,7 @@ import com.topjohnwu.magisk.model.entity.ProcessHideApp
import com.topjohnwu.magisk.model.entity.StatefulProcess import com.topjohnwu.magisk.model.entity.StatefulProcess
import com.topjohnwu.magisk.model.entity.recycler.HideItem import com.topjohnwu.magisk.model.entity.recycler.HideItem
import com.topjohnwu.magisk.model.entity.recycler.HideProcessItem import com.topjohnwu.magisk.model.entity.recycler.HideProcessItem
import com.topjohnwu.magisk.ui.compat.CompatViewModel import com.topjohnwu.magisk.base.BaseViewModel
import com.topjohnwu.magisk.ui.compat.Queryable import com.topjohnwu.magisk.ui.compat.Queryable
import com.topjohnwu.magisk.ui.compat.filterableListOf import com.topjohnwu.magisk.ui.compat.filterableListOf
import com.topjohnwu.magisk.ui.compat.itemBindingOf import com.topjohnwu.magisk.ui.compat.itemBindingOf
@ -21,7 +21,7 @@ import com.topjohnwu.magisk.utils.currentLocale
class HideViewModel( class HideViewModel(
private val magiskRepo: MagiskRepository private val magiskRepo: MagiskRepository
) : CompatViewModel(), Queryable by Queryable.impl(1000) { ) : BaseViewModel(), Queryable by Queryable.impl(1000) {
override val queryRunnable = Runnable { query() } override val queryRunnable = Runnable { query() }

View File

@ -22,7 +22,7 @@ import com.topjohnwu.magisk.model.events.dialog.ManagerInstallDialog
import com.topjohnwu.magisk.model.events.dialog.UninstallDialog import com.topjohnwu.magisk.model.events.dialog.UninstallDialog
import com.topjohnwu.magisk.model.navigation.Navigation import com.topjohnwu.magisk.model.navigation.Navigation
import com.topjohnwu.magisk.model.observer.Observer import com.topjohnwu.magisk.model.observer.Observer
import com.topjohnwu.magisk.ui.compat.CompatViewModel import com.topjohnwu.magisk.base.BaseViewModel
import com.topjohnwu.magisk.ui.compat.itemBindingOf import com.topjohnwu.magisk.ui.compat.itemBindingOf
import com.topjohnwu.magisk.utils.KObservableField import com.topjohnwu.magisk.utils.KObservableField
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
@ -35,7 +35,7 @@ enum class MagiskState {
class HomeViewModel( class HomeViewModel(
private val repoMagisk: MagiskRepository private val repoMagisk: MagiskRepository
) : CompatViewModel() { ) : BaseViewModel() {
val isNoticeVisible = KObservableField(Config.safetyNotice) val isNoticeVisible = KObservableField(Config.safetyNotice)

View File

@ -8,14 +8,14 @@ import com.topjohnwu.magisk.model.download.RemoteFileService
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.events.RequestFileEvent import com.topjohnwu.magisk.model.events.RequestFileEvent
import com.topjohnwu.magisk.ui.compat.CompatViewModel import com.topjohnwu.magisk.base.BaseViewModel
import com.topjohnwu.magisk.utils.KObservableField import com.topjohnwu.magisk.utils.KObservableField
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
import com.topjohnwu.superuser.ShellUtils import com.topjohnwu.superuser.ShellUtils
import org.koin.core.get import org.koin.core.get
import kotlin.math.roundToInt import kotlin.math.roundToInt
class InstallViewModel : CompatViewModel(State.LOADED) { class InstallViewModel : BaseViewModel(State.LOADED) {
val isRooted = Shell.rootAccess() val isRooted = Shell.rootAccess()
val isAB = isABDevice() val isAB = isABDevice()

View File

@ -12,7 +12,7 @@ import com.topjohnwu.magisk.model.entity.recycler.ConsoleItem
import com.topjohnwu.magisk.model.entity.recycler.LogItem import com.topjohnwu.magisk.model.entity.recycler.LogItem
import com.topjohnwu.magisk.model.entity.recycler.TextItem import com.topjohnwu.magisk.model.entity.recycler.TextItem
import com.topjohnwu.magisk.model.events.SnackbarEvent import com.topjohnwu.magisk.model.events.SnackbarEvent
import com.topjohnwu.magisk.ui.compat.CompatViewModel import com.topjohnwu.magisk.base.BaseViewModel
import com.topjohnwu.magisk.ui.compat.diffListOf import com.topjohnwu.magisk.ui.compat.diffListOf
import com.topjohnwu.magisk.ui.compat.itemBindingOf import com.topjohnwu.magisk.ui.compat.itemBindingOf
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
@ -26,7 +26,7 @@ import java.util.*
class LogViewModel( class LogViewModel(
private val repo: LogRepository private val repo: LogRepository
) : CompatViewModel() { ) : BaseViewModel() {
// --- empty view // --- empty view

View File

@ -6,6 +6,7 @@ import androidx.databinding.ObservableArrayList
import com.topjohnwu.magisk.BR import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.Config import com.topjohnwu.magisk.Config
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.base.BaseViewModel
import com.topjohnwu.magisk.data.database.RepoByNameDao import com.topjohnwu.magisk.data.database.RepoByNameDao
import com.topjohnwu.magisk.data.database.RepoByUpdatedDao import com.topjohnwu.magisk.data.database.RepoByUpdatedDao
import com.topjohnwu.magisk.databinding.ComparableRvItem import com.topjohnwu.magisk.databinding.ComparableRvItem
@ -37,7 +38,7 @@ 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
) : CompatViewModel(), Queryable by Queryable.impl(1000) { ) : BaseViewModel(), Queryable by Queryable.impl(1000) {
override val queryRunnable = Runnable { query() } override val queryRunnable = Runnable { query() }

View File

@ -1,5 +1,5 @@
package com.topjohnwu.magisk.ui.request package com.topjohnwu.magisk.ui.request
import com.topjohnwu.magisk.ui.compat.CompatViewModel import com.topjohnwu.magisk.base.BaseViewModel
class RequestViewModel : CompatViewModel() class RequestViewModel : BaseViewModel()

View File

@ -6,7 +6,7 @@ import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.extensions.subscribeK import com.topjohnwu.magisk.extensions.subscribeK
import com.topjohnwu.magisk.model.events.SafetyNetResult import com.topjohnwu.magisk.model.events.SafetyNetResult
import com.topjohnwu.magisk.model.events.UpdateSafetyNetEvent import com.topjohnwu.magisk.model.events.UpdateSafetyNetEvent
import com.topjohnwu.magisk.ui.compat.CompatViewModel import com.topjohnwu.magisk.base.BaseViewModel
import com.topjohnwu.magisk.ui.safetynet.SafetyNetState.* import com.topjohnwu.magisk.ui.safetynet.SafetyNetState.*
import com.topjohnwu.magisk.utils.KObservableField import com.topjohnwu.magisk.utils.KObservableField
import com.topjohnwu.magisk.utils.RxBus import com.topjohnwu.magisk.utils.RxBus
@ -18,7 +18,7 @@ enum class SafetyNetState {
class SafetynetViewModel( class SafetynetViewModel(
rxBus: RxBus rxBus: RxBus
) : CompatViewModel() { ) : BaseViewModel() {
private var currentState = IDLE private var currentState = IDLE
set(value) { set(value) {

View File

@ -15,7 +15,7 @@ import com.topjohnwu.magisk.model.events.PermissionEvent
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.model.navigation.Navigation import com.topjohnwu.magisk.model.navigation.Navigation
import com.topjohnwu.magisk.ui.compat.CompatViewModel import com.topjohnwu.magisk.base.BaseViewModel
import com.topjohnwu.magisk.ui.compat.adapterOf import com.topjohnwu.magisk.ui.compat.adapterOf
import com.topjohnwu.magisk.ui.compat.diffListOf import com.topjohnwu.magisk.ui.compat.diffListOf
import com.topjohnwu.magisk.ui.compat.itemBindingOf import com.topjohnwu.magisk.ui.compat.itemBindingOf
@ -28,7 +28,7 @@ import org.koin.core.get
class SettingsViewModel( class SettingsViewModel(
private val repositoryDao: RepoDao private val repositoryDao: RepoDao
) : CompatViewModel(), SettingsItem.Callback { ) : BaseViewModel(), SettingsItem.Callback {
val adapter = adapterOf<SettingsItem>() val adapter = adapterOf<SettingsItem>()
val itemBinding = itemBindingOf<SettingsItem> { it.bindExtra(BR.callback, this) } val itemBinding = itemBindingOf<SettingsItem> { it.bindExtra(BR.callback, this) }

View File

@ -19,7 +19,7 @@ import com.topjohnwu.magisk.model.events.SnackbarEvent
import com.topjohnwu.magisk.model.events.dialog.BiometricDialog import com.topjohnwu.magisk.model.events.dialog.BiometricDialog
import com.topjohnwu.magisk.model.events.dialog.SuperuserRevokeDialog import com.topjohnwu.magisk.model.events.dialog.SuperuserRevokeDialog
import com.topjohnwu.magisk.model.navigation.Navigation import com.topjohnwu.magisk.model.navigation.Navigation
import com.topjohnwu.magisk.ui.compat.CompatViewModel import com.topjohnwu.magisk.base.BaseViewModel
import com.topjohnwu.magisk.ui.compat.adapterOf import com.topjohnwu.magisk.ui.compat.adapterOf
import com.topjohnwu.magisk.ui.compat.diffListOf import com.topjohnwu.magisk.ui.compat.diffListOf
import com.topjohnwu.magisk.ui.compat.itemBindingOf import com.topjohnwu.magisk.ui.compat.itemBindingOf
@ -32,7 +32,7 @@ class SuperuserViewModel(
private val db: PolicyDao, private val db: PolicyDao,
private val packageManager: PackageManager, private val packageManager: PackageManager,
private val resources: Resources private val resources: Resources
) : CompatViewModel(), TappableHeadlineItem.Listener { ) : BaseViewModel(), TappableHeadlineItem.Listener {
private val itemNoData = TextItem(R.string.superuser_policy_none) private val itemNoData = TextItem(R.string.superuser_policy_none)

View File

@ -3,9 +3,9 @@ package com.topjohnwu.magisk.ui.theme
import com.topjohnwu.magisk.model.entity.recycler.TappableHeadlineItem import com.topjohnwu.magisk.model.entity.recycler.TappableHeadlineItem
import com.topjohnwu.magisk.model.events.RecreateEvent import com.topjohnwu.magisk.model.events.RecreateEvent
import com.topjohnwu.magisk.model.events.dialog.DarkThemeDialog import com.topjohnwu.magisk.model.events.dialog.DarkThemeDialog
import com.topjohnwu.magisk.ui.compat.CompatViewModel import com.topjohnwu.magisk.base.BaseViewModel
class ThemeViewModel : CompatViewModel(), TappableHeadlineItem.Listener { class ThemeViewModel : BaseViewModel(), TappableHeadlineItem.Listener {
val themeHeadline = TappableHeadlineItem.ThemeMode val themeHeadline = TappableHeadlineItem.ThemeMode