Commit 46d47083 authored by topjohnwu's avatar topjohnwu

Decouple state from BaseViewModel

parent aabc36f8
......@@ -4,17 +4,14 @@ import android.Manifest.permission.REQUEST_INSTALL_PACKAGES
import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
import android.annotation.SuppressLint
import android.os.Bundle
import androidx.databinding.Bindable
import androidx.databinding.PropertyChangeRegistry
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.navigation.NavDirections
import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.databinding.ObservableHost
import com.topjohnwu.magisk.databinding.set
import com.topjohnwu.magisk.events.BackPressEvent
import com.topjohnwu.magisk.events.NavigationEvent
import com.topjohnwu.magisk.events.PermissionEvent
......@@ -25,22 +22,8 @@ abstract class BaseViewModel : ViewModel(), ObservableHost {
override var callbacks: PropertyChangeRegistry? = null
enum class State {
LOADED, LOADING, LOADING_FAILED
}
@get:Bindable
val loading get() = state == State.LOADING
@get:Bindable
val loaded get() = state == State.LOADED
@get:Bindable
val loadFailed get() = state == State.LOADING_FAILED
val viewEvents: LiveData<ViewEvent> get() = _viewEvents
var state = State.LOADING
set(value) = set(value, field, { field = it }, BR.loading, BR.loaded, BR.loadFailed)
private val _viewEvents = MutableLiveData<ViewEvent>()
private var runningJob: Job? = null
......
......@@ -2,14 +2,15 @@ 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.core.di.AppContext
import com.topjohnwu.magisk.databinding.bindExtra
import com.topjohnwu.magisk.databinding.filterableListOf
import com.topjohnwu.magisk.databinding.set
import com.topjohnwu.magisk.ktx.concurrentMap
import com.topjohnwu.magisk.utils.Utils
import com.topjohnwu.superuser.Shell
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.asFlow
......@@ -43,13 +44,13 @@ class DenyListViewModel : BaseViewModel() {
it.put(BR.viewModel, this)
}
@get:Bindable
var loading = true
private set(value) = set(value, field, { field = it }, BR.loading)
@SuppressLint("InlinedApi")
override fun refresh() = viewModelScope.launch {
if (!Utils.showSuperUser()) {
state = State.LOADING_FAILED
return@launch
}
state = State.LOADING
loading = true
val (apps, diff) = withContext(Dispatchers.Default) {
val pm = AppContext.packageManager
val denyList = Shell.cmd("magisk --denylist ls").exec().out
......@@ -84,6 +85,6 @@ class DenyListViewModel : BaseViewModel() {
(it.isChecked || (filterSystem() && filterOS())) && filterQuery()
}
state = State.LOADED
loading = false
}
}
......@@ -35,7 +35,7 @@ class FlashFragment : BaseFragment<FragmentFlashMd2Binding>() {
setHasOptionsMenu(true)
activity?.setTitle(R.string.flash_screen_title)
viewModel.flashState.observe(this) {
viewModel.state.observe(this) {
activity?.supportActionBar?.setSubtitle(
when (it) {
FlashViewModel.State.FLASHING -> R.string.flashing
......
......@@ -32,9 +32,9 @@ class FlashViewModel : BaseViewModel() {
FLASHING, SUCCESS, FAILED
}
private val _flashState = MutableLiveData(State.FLASHING)
val flashState: LiveData<State> get() = _flashState
val flashing = Transformations.map(flashState) { it == State.FLASHING }
private val _state = MutableLiveData(State.FLASHING)
val state: LiveData<State> get() = _state
val flashing = Transformations.map(state) { it == State.FLASHING }
@get:Bindable
var showReboot = Info.isRooted
......@@ -89,7 +89,7 @@ class FlashViewModel : BaseViewModel() {
}
private fun onResult(success: Boolean) {
_flashState.value = if (success) State.SUCCESS else State.FAILED
_state.value = if (success) State.SUCCESS else State.FAILED
}
fun onMenuItemClicked(item: MenuItem): Boolean {
......
......@@ -26,14 +26,14 @@ import com.topjohnwu.superuser.Shell
import kotlinx.coroutines.launch
import kotlin.math.roundToInt
enum class MagiskState {
NOT_INSTALLED, UP_TO_DATE, OBSOLETE, LOADING
}
class HomeViewModel(
private val svc: NetworkService
) : BaseViewModel() {
enum class State {
LOADING, INVALID, OUTDATED, UP_TO_DATE
}
val magiskTitleBarrierIds =
intArrayOf(R.id.home_magisk_icon, R.id.home_magisk_title, R.id.home_magisk_button)
val appTitleBarrierIds =
......@@ -43,16 +43,16 @@ class HomeViewModel(
var isNoticeVisible = Config.safetyNotice
set(value) = set(value, field, { field = it }, BR.noticeVisible)
val stateMagisk
val magiskState
get() = when {
!Info.env.isActive -> MagiskState.NOT_INSTALLED
Info.env.versionCode < BuildConfig.VERSION_CODE -> MagiskState.OBSOLETE
else -> MagiskState.UP_TO_DATE
!Info.env.isActive -> State.INVALID
Info.env.versionCode < BuildConfig.VERSION_CODE -> State.OUTDATED
else -> State.UP_TO_DATE
}
@get:Bindable
var stateManager = MagiskState.LOADING
set(value) = set(value, field, { field = it }, BR.stateManager)
var appState = State.LOADING
set(value) = set(value, field, { field = it }, BR.appState)
val magiskInstalledVersion
get() = Info.env.run {
......@@ -84,13 +84,11 @@ class HomeViewModel(
}
override fun refresh() = viewModelScope.launch {
state = State.LOADING
appState = State.LOADING
Info.getRemote(svc)?.apply {
state = State.LOADED
stateManager = when {
BuildConfig.VERSION_CODE < magisk.versionCode -> MagiskState.OBSOLETE
else -> MagiskState.UP_TO_DATE
appState = when {
BuildConfig.VERSION_CODE < magisk.versionCode -> State.OUTDATED
else -> State.UP_TO_DATE
}
val isDebug = Config.updateChannel == Config.Value.DEBUG_CHANNEL
......@@ -98,7 +96,6 @@ class HomeViewModel(
("${magisk.version} (${magisk.versionCode}) (${stub.versionCode})" +
if (isDebug) " (D)" else "").asText()
} ?: run {
state = State.LOADING_FAILED
managerRemoteVersion = R.string.not_available.asText()
}
ensureEnv()
......@@ -119,14 +116,14 @@ class HomeViewModel(
fun onDeletePressed() = UninstallDialog().publish()
fun onManagerPressed() = when (state) {
State.LOADED -> withExternalRW {
fun onManagerPressed() = when (magiskState) {
State.LOADING -> SnackbarEvent(R.string.loading).publish()
State.INVALID -> SnackbarEvent(R.string.no_connection).publish()
else -> withExternalRW {
withInstallPermission {
ManagerInstallDialog().publish()
}
}
State.LOADING -> SnackbarEvent(R.string.loading).publish()
else -> SnackbarEvent(R.string.no_connection).publish()
}
fun onMagiskPressed() = withExternalRW {
......@@ -139,7 +136,7 @@ class HomeViewModel(
}
private suspend fun ensureEnv() {
if (MagiskState.NOT_INSTALLED == stateMagisk || checkedEnv) return
if (magiskState == State.INVALID || checkedEnv) return
val cmd = "env_check ${Info.env.versionString} ${Info.env.versionCode}"
if (!Shell.cmd(cmd).await().isSuccess) {
EnvFixDialog(this).publish()
......
......@@ -95,7 +95,6 @@ class InstallViewModel(
R.id.method_inactive_slot -> FlashFragment.flash(true).navigate(true)
else -> error("Unknown value")
}
state = State.LOADING
}
override fun onSaveState(state: Bundle) {
......
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
......@@ -10,10 +11,7 @@ import com.topjohnwu.magisk.core.Info
import com.topjohnwu.magisk.core.base.ContentResultCallback
import com.topjohnwu.magisk.core.model.module.LocalModule
import com.topjohnwu.magisk.core.model.module.OnlineModule
import com.topjohnwu.magisk.databinding.MergeObservableList
import com.topjohnwu.magisk.databinding.RvItem
import com.topjohnwu.magisk.databinding.bindExtra
import com.topjohnwu.magisk.databinding.diffListOf
import com.topjohnwu.magisk.databinding.*
import com.topjohnwu.magisk.events.GetContentEvent
import com.topjohnwu.magisk.events.SnackbarEvent
import com.topjohnwu.magisk.events.dialog.ModuleInstallDialog
......@@ -36,6 +34,10 @@ class ModuleViewModel : BaseViewModel() {
val data get() = uri
@get:Bindable
var loading = true
private set(value) = set(value, field, { field = it }, BR.loading)
init {
if (Info.env.isActive && LocalModule.loaded()) {
items.insertItem(InstallModule)
......@@ -45,9 +47,9 @@ class ModuleViewModel : BaseViewModel() {
override fun refresh(): Job {
return viewModelScope.launch {
state = State.LOADING
loading = true
loadInstalled()
state = State.LOADED
loading = false
loadUpdateInfo()
}
}
......
......@@ -4,6 +4,7 @@ import android.annotation.SuppressLint
import android.content.pm.PackageManager
import android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES
import android.os.Process
import androidx.databinding.Bindable
import androidx.databinding.ObservableArrayList
import androidx.lifecycle.viewModelScope
import com.topjohnwu.magisk.BR
......@@ -14,10 +15,7 @@ import com.topjohnwu.magisk.core.di.AppContext
import com.topjohnwu.magisk.core.model.su.SuPolicy
import com.topjohnwu.magisk.core.utils.BiometricHelper
import com.topjohnwu.magisk.core.utils.currentLocale
import com.topjohnwu.magisk.databinding.AnyDiffRvItem
import com.topjohnwu.magisk.databinding.MergeObservableList
import com.topjohnwu.magisk.databinding.bindExtra
import com.topjohnwu.magisk.databinding.diffListOf
import com.topjohnwu.magisk.databinding.*
import com.topjohnwu.magisk.events.SnackbarEvent
import com.topjohnwu.magisk.events.dialog.BiometricEvent
import com.topjohnwu.magisk.events.dialog.SuperuserRevokeDialog
......@@ -45,15 +43,17 @@ class SuperuserViewModel(
it.put(BR.listener, this)
}
// ---
@get:Bindable
var loading = true
private set(value) = set(value, field, { field = it }, BR.loading)
@SuppressLint("InlinedApi")
override fun refresh() = viewModelScope.launch {
if (!Utils.showSuperUser()) {
state = State.LOADING_FAILED
loading = false
return@launch
}
state = State.LOADING
loading = true
val (policies, diff) = withContext(Dispatchers.IO) {
db.deleteOutdated()
db.delete(AppContext.applicationInfo.uid)
......@@ -98,7 +98,7 @@ class SuperuserViewModel(
itemsHelpers.clear()
else if (itemsHelpers.isEmpty())
itemsHelpers.add(itemNoData)
state = State.LOADED
loading = false
}
// ---
......
......@@ -24,7 +24,7 @@
android:paddingTop="@dimen/internal_action_bar_size"
app:fitsSystemWindowsInsets="top|bottom"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:invisibleUnless="@{viewModel.loaded}"
app:invisible="@{viewModel.loading}"
app:items="@{viewModel.items}"
app:extraBindings="@{viewModel.extraBindings}"
tools:listitem="@layout/item_hide_md2"
......@@ -52,24 +52,6 @@
</LinearLayout>
<LinearLayout
goneUnless="@{viewModel.loadFailed}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:orientation="vertical"
tools:visibility="gone">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/not_available"
android:textAppearance="@style/AppearanceFoundation.Title"
android:textStyle="bold" />
</LinearLayout>
</FrameLayout>
</layout>
......@@ -7,8 +7,6 @@
<import type="com.topjohnwu.magisk.core.Info" />
<import type="com.topjohnwu.magisk.ui.home.MagiskState" />
<import type="com.topjohnwu.magisk.ui.home.DeveloperItem" />
<import type="com.topjohnwu.magisk.ui.home.IconLink" />
......
......@@ -19,7 +19,7 @@
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/superuser_list"
goneUnless="@{viewModel.loaded || !viewModel.items.empty}"
gone="@{viewModel.loading}"
app:items="@{viewModel.items}"
app:extraBindings="@{viewModel.extraBindings}"
android:layout_width="match_parent"
......@@ -33,7 +33,7 @@
tools:listitem="@layout/item_policy_md2" />
<LinearLayout
goneUnless="@{viewModel.loading &amp;&amp; viewModel.items.empty}"
goneUnless="@{viewModel.loading}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
......@@ -54,24 +54,6 @@
</LinearLayout>
<LinearLayout
goneUnless="@{viewModel.loadFailed}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:orientation="vertical"
tools:visibility="gone">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/not_available"
android:textAppearance="@style/AppearanceFoundation.Title"
android:textStyle="bold" />
</LinearLayout>
</FrameLayout>
</layout>
......@@ -7,7 +7,7 @@
<import type="com.topjohnwu.magisk.core.Info" />
<import type="com.topjohnwu.magisk.ui.home.MagiskState" />
<import type="com.topjohnwu.magisk.ui.home.HomeViewModel.State" />
<variable
name="viewModel"
......@@ -63,7 +63,7 @@
<Button
style="@style/WidgetFoundation.Button"
gone="@{viewModel.stateMagisk != MagiskState.OBSOLETE}"
gone="@{viewModel.magiskState != State.OUTDATED}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="@{() -> viewModel.onMagiskPressed()}"
......@@ -74,7 +74,7 @@
<Button
style="@style/WidgetFoundation.Button.Text"
gone="@{viewModel.stateMagisk == MagiskState.OBSOLETE}"
gone="@{viewModel.magiskState == State.OUTDATED}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
......
......@@ -7,7 +7,7 @@
<import type="com.topjohnwu.magisk.core.Info" />
<import type="com.topjohnwu.magisk.ui.home.MagiskState" />
<import type="com.topjohnwu.magisk.ui.home.HomeViewModel.State" />
<variable
name="viewModel"
......@@ -64,7 +64,7 @@
<Button
style="@style/WidgetFoundation.Button"
gone="@{viewModel.stateManager != MagiskState.OBSOLETE}"
gone="@{viewModel.appState != State.OUTDATED}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="@{() -> viewModel.onManagerPressed()}"
......@@ -75,7 +75,7 @@
<Button
style="@style/WidgetFoundation.Button.Text"
gone="@{viewModel.stateManager != MagiskState.UP_TO_DATE}"
gone="@{viewModel.appState != State.UP_TO_DATE}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment