Reduce usage of delegation

This commit is contained in:
topjohnwu 2020-07-15 01:21:57 -07:00
parent ba31c6b625
commit 6c6368fd81
15 changed files with 226 additions and 213 deletions

View File

@ -7,17 +7,21 @@ import com.topjohnwu.magisk.databinding.ObservableItem
import com.topjohnwu.magisk.ktx.timeDateFormat import com.topjohnwu.magisk.ktx.timeDateFormat
import com.topjohnwu.magisk.ktx.toTime import com.topjohnwu.magisk.ktx.toTime
import com.topjohnwu.magisk.model.entity.MagiskLog import com.topjohnwu.magisk.model.entity.MagiskLog
import com.topjohnwu.magisk.utils.observable import com.topjohnwu.magisk.utils.set
class LogItem(val item: MagiskLog) : ObservableItem<LogItem>() { class LogItem(val item: MagiskLog) : ObservableItem<LogItem>() {
override val layoutRes = R.layout.item_log_access_md2 override val layoutRes = R.layout.item_log_access_md2
val date = item.time.toTime(timeDateFormat) val date = item.time.toTime(timeDateFormat)
@get:Bindable @get:Bindable
var isTop by observable(false, BR.top) var isTop = false
set(value) = set(value, field, { field = it }, BR.top)
@get:Bindable @get:Bindable
var isBottom by observable(false, BR.bottom) var isBottom = false
set(value) = set(value, field, { field = it }, BR.bottom)
override fun itemSameAs(other: LogItem) = item.appName == other.item.appName override fun itemSameAs(other: LogItem) = item.appName == other.item.appName

View File

@ -11,7 +11,7 @@ import com.topjohnwu.magisk.core.model.module.Repo
import com.topjohnwu.magisk.databinding.ComparableRvItem import com.topjohnwu.magisk.databinding.ComparableRvItem
import com.topjohnwu.magisk.databinding.ObservableItem import com.topjohnwu.magisk.databinding.ObservableItem
import com.topjohnwu.magisk.ui.module.ModuleViewModel import com.topjohnwu.magisk.ui.module.ModuleViewModel
import com.topjohnwu.magisk.utils.observable import com.topjohnwu.magisk.utils.set
object InstallModule : ComparableRvItem<InstallModule>() { object InstallModule : ComparableRvItem<InstallModule>() {
override val layoutRes = R.layout.item_module_download override val layoutRes = R.layout.item_module_download
@ -34,11 +34,16 @@ class SectionTitle(
override val layoutRes = R.layout.item_section_md2 override val layoutRes = R.layout.item_section_md2
@get:Bindable @get:Bindable
var button by observable(_button, BR.button) var button = _button
set(value) = set(value, field, { field = it }, BR.button)
@get:Bindable @get:Bindable
var icon by observable(_icon, BR.icon) var icon = _icon
set(value) = set(value, field, { field = it }, BR.icon)
@get:Bindable @get:Bindable
var hasButton by observable(_button != 0 && _icon != 0, BR.hasButton) var hasButton = _button != 0 && _icon != 0
set(value) = set(value, field, { field = it }, BR.hasButton)
override fun onBindingBound(binding: ViewDataBinding) { override fun onBindingBound(binding: ViewDataBinding) {
super.onBindingBound(binding) super.onBindingBound(binding)
@ -54,9 +59,13 @@ sealed class RepoItem(val item: Repo) : ObservableItem<RepoItem>() {
override val layoutRes: Int = R.layout.item_repo_md2 override val layoutRes: Int = R.layout.item_repo_md2
@get:Bindable @get:Bindable
var progress by observable(0, BR.progress) var progress = 0
set(value) = set(value, field, { field = it }, BR.progress)
@get:Bindable @get:Bindable
var isUpdate by observable(false, BR.update) var isUpdate = false
set(value) = set(value, field, { field = it }, BR.update)
override fun contentSameAs(other: RepoItem): Boolean = item == other.item override fun contentSameAs(other: RepoItem): Boolean = item == other.item
override fun itemSameAs(other: RepoItem): Boolean = item.id == other.item.id override fun itemSameAs(other: RepoItem): Boolean = item.id == other.item.id
@ -75,7 +84,8 @@ class ModuleItem(val item: Module) : ObservableItem<ModuleItem>(), Observable {
override val layoutRes = R.layout.item_module_md2 override val layoutRes = R.layout.item_module_md2
@get:Bindable @get:Bindable
var repo: Repo? by observable(null, BR.repo) var repo: Repo? = null
set(value) = set(value, field, { field = it }, BR.repo)
@get:Bindable @get:Bindable
var isEnabled var isEnabled

View File

@ -13,7 +13,7 @@ import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.databinding.ObservableItem import com.topjohnwu.magisk.databinding.ObservableItem
import com.topjohnwu.magisk.utils.TransitiveText import com.topjohnwu.magisk.utils.TransitiveText
import com.topjohnwu.magisk.utils.observable import com.topjohnwu.magisk.utils.set
import com.topjohnwu.magisk.view.MagiskDialog import com.topjohnwu.magisk.view.MagiskDialog
import org.koin.core.KoinComponent import org.koin.core.KoinComponent
import org.koin.core.get import org.koin.core.get
@ -27,7 +27,8 @@ sealed class SettingsItem : ObservableItem<SettingsItem>() {
open val description: TransitiveText get() = TransitiveText.EMPTY open val description: TransitiveText get() = TransitiveText.EMPTY
@get:Bindable @get:Bindable
var isEnabled by observable(true, BR.enabled) var isEnabled = true
set(value) = set(value, field, { field = it }, BR.enabled)
protected open val isFullSpan get() = false protected open val isFullSpan get() = false
@ -63,12 +64,15 @@ sealed class SettingsItem : ObservableItem<SettingsItem>() {
@get:Bindable @get:Bindable
abstract var value: T abstract var value: T
protected inline fun <reified T> value( protected inline fun <reified T> setV(
initialValue: T, new: T, old: T, setter: (T) -> Unit, vararg fieldIds: Int) {
vararg fieldIds: Int, set(new, old, setter, BR.value, *fieldIds)
crossinline setter: (T) -> Unit = {} }
) = observable(initialValue, BR.value, *fieldIds, afterChanged = setter)
protected inline fun <reified T> setV(
new: T, old: T, setter: (T) -> Unit, afterChanged: (T) -> Unit = {}) {
set(new, old, setter, BR.value, afterChanged = afterChanged)
}
} }
abstract class Toggle : Value<Boolean>() { abstract class Toggle : Value<Boolean>() {
@ -142,10 +146,10 @@ sealed class SettingsItem : ObservableItem<SettingsItem>() {
val selectedEntry val selectedEntry
get() = entries.getOrNull(value) get() = entries.getOrNull(value)
/* override */ protected inline fun value( protected inline fun <reified T> setS(
initialValue: Int, new: T, old: T, setter: (T) -> Unit, afterChanged: (T) -> Unit = {}) {
crossinline setter: (Int) -> Unit set(new, old, setter, BR.value, BR.selectedEntry, BR.description, afterChanged = afterChanged)
) = observable(initialValue, BR.value, BR.selectedEntry, BR.description, afterChanged = setter) }
private fun Resources.getArrayOrEmpty(id: Int): Array<String> = private fun Resources.getArrayOrEmpty(id: Int): Array<String> =
runCatching { getStringArray(id) }.getOrDefault(emptyArray()) runCatching { getStringArray(id) }.getOrDefault(emptyArray())

View File

@ -17,7 +17,7 @@ import com.topjohnwu.magisk.core.base.BaseActivity
import com.topjohnwu.magisk.model.events.* import com.topjohnwu.magisk.model.events.*
import com.topjohnwu.magisk.model.navigation.NavigationWrapper import com.topjohnwu.magisk.model.navigation.NavigationWrapper
import com.topjohnwu.magisk.utils.ObservableHost import com.topjohnwu.magisk.utils.ObservableHost
import com.topjohnwu.magisk.utils.observable import com.topjohnwu.magisk.utils.set
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import org.koin.core.KoinComponent import org.koin.core.KoinComponent
@ -42,9 +42,11 @@ abstract class BaseViewModel(
val viewEvents: LiveData<ViewEvent> get() = _viewEvents val viewEvents: LiveData<ViewEvent> get() = _viewEvents
@get:Bindable @get:Bindable
var insets by observable(Insets.NONE, BR.insets) var insets = Insets.NONE
set(value) = set(value, field, { field = it }, BR.insets)
var state by observable(initialState, BR.loading, BR.loaded, BR.loadFailed) var state= initialState
set(value) = set(value, field, { field = it }, BR.loading, BR.loaded, BR.loadFailed)
private val _viewEvents = MutableLiveData<ViewEvent>() private val _viewEvents = MutableLiveData<ViewEvent>()
private var runningJob: Job? = null private var runningJob: Job? = null

View File

@ -20,7 +20,7 @@ import com.topjohnwu.magisk.model.events.SnackbarEvent
import com.topjohnwu.magisk.ui.base.BaseViewModel import com.topjohnwu.magisk.ui.base.BaseViewModel
import com.topjohnwu.magisk.ui.base.diffListOf import com.topjohnwu.magisk.ui.base.diffListOf
import com.topjohnwu.magisk.ui.base.itemBindingOf import com.topjohnwu.magisk.ui.base.itemBindingOf
import com.topjohnwu.magisk.utils.observable import com.topjohnwu.magisk.utils.set
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -34,9 +34,12 @@ class FlashViewModel(
) : BaseViewModel() { ) : BaseViewModel() {
@get:Bindable @get:Bindable
var showReboot by observable(Shell.rootAccess(), BR.showReboot) var showReboot = Shell.rootAccess()
set(value) = set(value, field, { field = it }, BR.showReboot)
@get:Bindable @get:Bindable
var behaviorText by observable(resources.getString(R.string.flashing), BR.behaviorText) var behaviorText = resources.getString(R.string.flashing)
set(value) = set(value, field, { field = it }, BR.behaviorText)
val adapter = BindingAdapter<ConsoleItem>() val adapter = BindingAdapter<ConsoleItem>()
val items = diffListOf<ConsoleItem>() val items = diffListOf<ConsoleItem>()

View File

@ -17,7 +17,7 @@ import com.topjohnwu.magisk.ui.base.BaseViewModel
import com.topjohnwu.magisk.ui.base.Queryable import com.topjohnwu.magisk.ui.base.Queryable
import com.topjohnwu.magisk.ui.base.filterableListOf import com.topjohnwu.magisk.ui.base.filterableListOf
import com.topjohnwu.magisk.ui.base.itemBindingOf import com.topjohnwu.magisk.ui.base.itemBindingOf
import com.topjohnwu.magisk.utils.observable import com.topjohnwu.magisk.utils.set
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@ -29,12 +29,14 @@ class HideViewModel(
override val queryDelay = 1000L override val queryDelay = 1000L
@get:Bindable @get:Bindable
var isShowSystem by observable(false, BR.showSystem) { var isShowSystem = false
set(value) = set(value, field, { field = it }, BR.showSystem){
submitQuery() submitQuery()
} }
@get:Bindable @get:Bindable
var query by observable("", BR.query) { var query = ""
set(value) = set(value, field, { field = it }, BR.query){
submitQuery() submitQuery()
} }

View File

@ -27,7 +27,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.ui.base.BaseViewModel import com.topjohnwu.magisk.ui.base.BaseViewModel
import com.topjohnwu.magisk.ui.base.itemBindingOf import com.topjohnwu.magisk.ui.base.itemBindingOf
import com.topjohnwu.magisk.utils.observable import com.topjohnwu.magisk.utils.set
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import me.tatarka.bindingcollectionadapter2.BR import me.tatarka.bindingcollectionadapter2.BR
@ -42,25 +42,37 @@ class HomeViewModel(
) : BaseViewModel() { ) : BaseViewModel() {
@get:Bindable @get:Bindable
var isNoticeVisible by observable(Config.safetyNotice, BR.noticeVisible) var isNoticeVisible = Config.safetyNotice
set(value) = set(value, field, { field = it }, BR.noticeVisible)
@get:Bindable @get:Bindable
var stateMagisk by observable(MagiskState.LOADING, BR.stateMagisk) var stateMagisk = MagiskState.LOADING
set(value) = set(value, field, { field = it }, BR.stateMagisk)
@get:Bindable @get:Bindable
var stateManager by observable(MagiskState.LOADING, BR.stateManager) var stateManager = MagiskState.LOADING
set(value) = set(value, field, { field = it }, BR.stateManager)
@get:Bindable @get:Bindable
var magiskRemoteVersion by observable(R.string.loading.res(), BR.magiskRemoteVersion) var magiskRemoteVersion = R.string.loading.res()
set(value) = set(value, field, { field = it }, BR.magiskRemoteVersion)
val magiskInstalledVersion get() = val magiskInstalledVersion get() =
"${Info.env.magiskVersionString} (${Info.env.magiskVersionCode})" "${Info.env.magiskVersionString} (${Info.env.magiskVersionCode})"
val magiskMode get() = R.string.home_status_normal.res() val magiskMode get() = R.string.home_status_normal.res()
@get:Bindable @get:Bindable
var managerRemoteVersion by observable(R.string.loading.res(), BR.managerRemoteVersion) var managerRemoteVersion = R.string.loading.res()
set(value) = set(value, field, { field = it }, BR.managerRemoteVersion)
val managerInstalledVersion = Info.stub?.let { val managerInstalledVersion = Info.stub?.let {
"${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE}) (${it.version})" "${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE}) (${it.version})"
} ?: "${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})" } ?: "${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})"
val statePackageName = packageName val statePackageName = packageName
@get:Bindable @get:Bindable
var stateManagerProgress by observable(0, BR.stateManagerProgress) var stateManagerProgress = 0
set(value) = set(value, field, { field = it }, BR.stateManagerProgress)
val items = listOf(DeveloperItem.Mainline, DeveloperItem.App, DeveloperItem.Project) val items = listOf(DeveloperItem.Mainline, DeveloperItem.App, DeveloperItem.Project)
val itemBinding = itemBindingOf<HomeItem> { val itemBinding = itemBindingOf<HomeItem> {

View File

@ -16,7 +16,7 @@ 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.model.events.dialog.SecondSlotWarningDialog import com.topjohnwu.magisk.model.events.dialog.SecondSlotWarningDialog
import com.topjohnwu.magisk.ui.base.BaseViewModel import com.topjohnwu.magisk.ui.base.BaseViewModel
import com.topjohnwu.magisk.utils.observable import com.topjohnwu.magisk.utils.set
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.koin.core.get import org.koin.core.get
@ -30,9 +30,12 @@ class InstallViewModel(
val isAB get() = Info.isAB val isAB get() = Info.isAB
@get:Bindable @get:Bindable
var step by observable(0, BR.step) var step = 0
set(value) = set(value, field, { field = it }, BR.step)
@get:Bindable @get:Bindable
var method by observable(-1, BR.method) { var method = -1
set(value) = set(value, field, { field = it }, BR.method) {
when (it) { when (it) {
R.id.method_patch -> { R.id.method_patch -> {
Utils.toast(R.string.patch_file_msg, Toast.LENGTH_LONG) Utils.toast(R.string.patch_file_msg, Toast.LENGTH_LONG)
@ -43,12 +46,18 @@ class InstallViewModel(
} }
} }
} }
@get:Bindable @get:Bindable
var progress by observable(0, BR.progress) var progress = 0
set(value) = set(value, field, { field = it }, BR.progress)
@get:Bindable @get:Bindable
var data by observable(null as Uri?, BR.data) var data: Uri? = null
set(value) = set(value, field, { field = it }, BR.data)
@get:Bindable @get:Bindable
var notes by observable("", BR.notes) var notes = ""
set(value) = set(value, field, { field = it }, BR.notes)
init { init {
RemoteFileService.reset() RemoteFileService.reset()

View File

@ -13,7 +13,7 @@ import com.topjohnwu.magisk.model.events.SnackbarEvent
import com.topjohnwu.magisk.ui.base.BaseViewModel import com.topjohnwu.magisk.ui.base.BaseViewModel
import com.topjohnwu.magisk.ui.base.diffListOf import com.topjohnwu.magisk.ui.base.diffListOf
import com.topjohnwu.magisk.ui.base.itemBindingOf import com.topjohnwu.magisk.ui.base.itemBindingOf
import com.topjohnwu.magisk.utils.observable import com.topjohnwu.magisk.utils.set
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -41,7 +41,8 @@ class LogViewModel(
// --- magisk log // --- magisk log
@get:Bindable @get:Bindable
var consoleText by observable(" ", BR.consoleText) var consoleText= " "
set(value) = set(value, field, { field = it }, BR.consoleText)
override fun refresh() = viewModelScope.launch { override fun refresh() = viewModelScope.launch {
consoleText = repo.fetchMagiskLogs() consoleText = repo.fetchMagiskLogs()

View File

@ -23,7 +23,7 @@ import com.topjohnwu.magisk.model.events.SnackbarEvent
import com.topjohnwu.magisk.model.events.dialog.ModuleInstallDialog 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.observable import com.topjohnwu.magisk.utils.set
import kotlinx.coroutines.* import kotlinx.coroutines.*
import me.tatarka.bindingcollectionadapter2.collections.MergeObservableList import me.tatarka.bindingcollectionadapter2.collections.MergeObservableList
import kotlin.math.roundToInt import kotlin.math.roundToInt
@ -53,17 +53,21 @@ class ModuleViewModel(
private var remoteJob: Job? = null private var remoteJob: Job? = null
@get:Bindable @get:Bindable
var isRemoteLoading by observable(false, BR.remoteLoading) var isRemoteLoading = false
set(value) = set(value, field, { field = it }, BR.remoteLoading)
@get:Bindable @get:Bindable
var query by observable("", BR.query) { var query = ""
set(value) = set(value, field, { field = it }, BR.query) {
submitQuery() submitQuery()
// Yes we do lie about the search being loaded // Yes we do lie about the search being loaded
searchLoading = true searchLoading = true
} }
@get:Bindable @get:Bindable
var searchLoading by observable(false, BR.searchLoading) var searchLoading = false
set(value) = set(value, field, { field = it }, BR.searchLoading)
val itemsSearch = diffListOf<RepoItem>() val itemsSearch = diffListOf<RepoItem>()
val itemSearchBinding = itemBindingOf<RepoItem> { val itemSearchBinding = itemBindingOf<RepoItem> {
it.bindExtra(BR.viewModel, this) it.bindExtra(BR.viewModel, this)

View File

@ -6,7 +6,7 @@ import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.model.events.CheckSafetyNetEvent import com.topjohnwu.magisk.model.events.CheckSafetyNetEvent
import com.topjohnwu.magisk.ui.base.BaseViewModel import com.topjohnwu.magisk.ui.base.BaseViewModel
import com.topjohnwu.magisk.ui.safetynet.SafetyNetState.* import com.topjohnwu.magisk.ui.safetynet.SafetyNetState.*
import com.topjohnwu.magisk.utils.observable import com.topjohnwu.magisk.utils.set
import org.json.JSONObject import org.json.JSONObject
enum class SafetyNetState { enum class SafetyNetState {
@ -21,13 +21,20 @@ data class SafetyNetResult(
class SafetynetViewModel : BaseViewModel() { class SafetynetViewModel : BaseViewModel() {
@get:Bindable @get:Bindable
var safetyNetTitle by observable(R.string.empty, BR.safetyNetTitle) var safetyNetTitle = R.string.empty
set(value) = set(value, field, { field = it }, BR.safetyNetTitle)
@get:Bindable @get:Bindable
var ctsState by observable(false, BR.ctsState) var ctsState = false
set(value) = set(value, field, { field = it }, BR.ctsState)
@get:Bindable @get:Bindable
var basicIntegrityState by observable(false, BR.basicIntegrityState) var basicIntegrityState = false
set(value) = set(value, field, { field = it }, BR.basicIntegrityState)
@get:Bindable @get:Bindable
var evalType by observable("") var evalType = ""
set(value) = set(value, field, { field = it }, BR.evalType)
@get:Bindable @get:Bindable
val isChecking get() = currentState == LOADING val isChecking get() = currentState == LOADING
@ -36,7 +43,8 @@ class SafetynetViewModel : BaseViewModel() {
@get:Bindable @get:Bindable
val isSuccess get() = currentState == PASS val isSuccess get() = currentState == PASS
private var currentState by observable(IDLE, BR.checking, BR.failed, BR.success) private var currentState = IDLE
set(value) = set(value, field, { field = it }, BR.checking, BR.failed, BR.success)
init { init {
cachedResult?.also { cachedResult?.also {

View File

@ -21,7 +21,7 @@ import com.topjohnwu.magisk.databinding.DialogSettingsUpdateChannelBinding
import com.topjohnwu.magisk.ktx.get import com.topjohnwu.magisk.ktx.get
import com.topjohnwu.magisk.model.entity.recycler.SettingsItem import com.topjohnwu.magisk.model.entity.recycler.SettingsItem
import com.topjohnwu.magisk.utils.asTransitive import com.topjohnwu.magisk.utils.asTransitive
import com.topjohnwu.magisk.utils.observable import com.topjohnwu.magisk.utils.set
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -34,7 +34,8 @@ object Customization : SettingsItem.Section() {
} }
object Language : SettingsItem.Selector() { object Language : SettingsItem.Selector() {
override var value by value(-1) { override var value = -1
set(value) = setS(value, field, { field = it }) {
Config.locale = entryValues[it] Config.locale = entryValues[it]
} }
@ -49,7 +50,6 @@ object Language : SettingsItem.Selector() {
entryValues = values entryValues = values
val selectedLocale = currentLocale.getDisplayName(currentLocale) val selectedLocale = currentLocale.getDisplayName(currentLocale)
value = names.indexOfFirst { it == selectedLocale }.let { if (it == -1) 0 else it } value = names.indexOfFirst { it == selectedLocale }.let { if (it == -1) 0 else it }
notifyPropertyChanged(BR.selectedEntry)
} }
} }
} }
@ -79,7 +79,8 @@ object Hide : SettingsItem.Input() {
override val title = R.string.settings_hide_manager_title.asTransitive() override val title = R.string.settings_hide_manager_title.asTransitive()
override val description = R.string.settings_hide_manager_summary.asTransitive() override val description = R.string.settings_hide_manager_summary.asTransitive()
override val showStrip = false override val showStrip = false
override var value by value(resources.getString(R.string.re_app_name), BR.error) override var value = resources.getString(R.string.re_app_name)
set(value) = setV(value, field, { field = it }, BR.error)
@get:Bindable @get:Bindable
val isError get() = value.length > 14 || value.isBlank() val isError get() = value.length > 14 || value.isBlank()
@ -101,13 +102,15 @@ fun HideOrRestore() =
if (get<Context>().packageName == BuildConfig.APPLICATION_ID) Hide else Restore if (get<Context>().packageName == BuildConfig.APPLICATION_ID) Hide else Restore
object DownloadPath : SettingsItem.Input() { object DownloadPath : SettingsItem.Input() {
override var value: String by value(Config.downloadPath) { Config.downloadPath = it } override var value = Config.downloadPath
set(value) = setV(value, field, { field = it }) { Config.downloadPath = it }
override val title = R.string.settings_download_path_title.asTransitive() override val title = R.string.settings_download_path_title.asTransitive()
override val intermediate: String? override val intermediate: String?
get() = if (Utils.ensureDownloadPath(result) != null) result else null get() = if (Utils.ensureDownloadPath(result) != null) result else null
@get:Bindable @get:Bindable
var result by observable(value, BR.result, BR.path) var result = value
set(value) = set(value, field, { field = it }, BR.result, BR.path)
@get:Bindable @get:Bindable
val path get() = File(Environment.getExternalStorageDirectory(), result).absolutePath.orEmpty() val path get() = File(Environment.getExternalStorageDirectory(), result).absolutePath.orEmpty()
@ -117,7 +120,8 @@ object DownloadPath : SettingsItem.Input() {
} }
object UpdateChannel : SettingsItem.Selector() { object UpdateChannel : SettingsItem.Selector() {
override var value by value(Config.updateChannel) { Config.updateChannel = it } override var value = Config.updateChannel
set(value) = setS(value, field, { field = it }) { Config.updateChannel = it }
override val title = R.string.settings_update_channel_title.asTransitive() override val title = R.string.settings_update_channel_title.asTransitive()
override val entries get() = resources.getStringArray(R.array.update_channel).let { override val entries get() = resources.getStringArray(R.array.update_channel).let {
@ -128,11 +132,13 @@ object UpdateChannel : SettingsItem.Selector() {
object UpdateChannelUrl : SettingsItem.Input() { object UpdateChannelUrl : SettingsItem.Input() {
override val title = R.string.settings_update_custom.asTransitive() override val title = R.string.settings_update_custom.asTransitive()
override var value by value(Config.customChannelUrl) { Config.customChannelUrl = it } override var value = Config.customChannelUrl
set(value) = setV(value, field, { field = it }) { Config.customChannelUrl = it }
override val intermediate: String? get() = result override val intermediate: String? get() = result
@get:Bindable @get:Bindable
var result by observable(value, BR.result) var result = value
set(value) = set(value, field, { field = it }, BR.result)
override fun refresh() { override fun refresh() {
isEnabled = UpdateChannel.value == Config.Value.CUSTOM_CHANNEL isEnabled = UpdateChannel.value == Config.Value.CUSTOM_CHANNEL
@ -145,7 +151,8 @@ object UpdateChannelUrl : SettingsItem.Input() {
object UpdateChecker : SettingsItem.Toggle() { object UpdateChecker : SettingsItem.Toggle() {
override val title = R.string.settings_check_update_title.asTransitive() override val title = R.string.settings_check_update_title.asTransitive()
override val description = R.string.settings_check_update_summary.asTransitive() override val description = R.string.settings_check_update_summary.asTransitive()
override var value by value(Config.checkUpdate) { override var value = Config.checkUpdate
set(value) = setV(value, field, { field = it }) {
Config.checkUpdate = it Config.checkUpdate = it
Utils.scheduleUpdateCheck(get()) Utils.scheduleUpdateCheck(get())
} }
@ -159,7 +166,8 @@ object SystemlessHosts : SettingsItem.Blank() {
object Biometrics : SettingsItem.Toggle() { object Biometrics : SettingsItem.Toggle() {
override val title = R.string.settings_su_biometric_title.asTransitive() override val title = R.string.settings_su_biometric_title.asTransitive()
override var value by value(Config.suBiometric) { Config.suBiometric = it } override var value = Config.suBiometric
set(value) = setV(value, field, { field = it }) { Config.suBiometric = it }
override var description = R.string.settings_su_biometric_summary.asTransitive() override var description = R.string.settings_su_biometric_summary.asTransitive()
override fun refresh() { override fun refresh() {
@ -174,7 +182,8 @@ object Biometrics : SettingsItem.Toggle() {
object Reauthenticate : SettingsItem.Toggle() { object Reauthenticate : SettingsItem.Toggle() {
override val title = R.string.settings_su_reauth_title.asTransitive() override val title = R.string.settings_su_reauth_title.asTransitive()
override val description = R.string.settings_su_reauth_summary.asTransitive() override val description = R.string.settings_su_reauth_summary.asTransitive()
override var value by value(Config.suReAuth) { Config.suReAuth = it } override var value = Config.suReAuth
set(value) = setV(value, field, { field = it }) { Config.suReAuth = it }
override fun refresh() { override fun refresh() {
isEnabled = Build.VERSION.SDK_INT < Build.VERSION_CODES.O && Utils.showSuperUser() isEnabled = Build.VERSION.SDK_INT < Build.VERSION_CODES.O && Utils.showSuperUser()
@ -190,7 +199,8 @@ object Magisk : SettingsItem.Section() {
object MagiskHide : SettingsItem.Toggle() { object MagiskHide : SettingsItem.Toggle() {
override val title = R.string.magiskhide.asTransitive() override val title = R.string.magiskhide.asTransitive()
override val description = R.string.settings_magiskhide_summary.asTransitive() override val description = R.string.settings_magiskhide_summary.asTransitive()
override var value by value(Config.magiskHide) { override var value = Config.magiskHide
set(value) = setV(value, field, { field = it }) {
Config.magiskHide = it Config.magiskHide = it
when { when {
it -> Shell.su("magiskhide --enable").submit() it -> Shell.su("magiskhide --enable").submit()
@ -210,7 +220,8 @@ object AccessMode : SettingsItem.Selector() {
override val entryRes = R.array.su_access override val entryRes = R.array.su_access
override val entryValRes = R.array.value_array override val entryValRes = R.array.value_array
override var value by value(Config.rootMode) { override var value = Config.rootMode
set(value) = setS(value, field, { field = it }) {
Config.rootMode = entryValues[it].toInt() Config.rootMode = entryValues[it].toInt()
} }
} }
@ -220,7 +231,8 @@ object MultiuserMode : SettingsItem.Selector() {
override val entryRes = R.array.multiuser_mode override val entryRes = R.array.multiuser_mode
override val entryValRes = R.array.value_array override val entryValRes = R.array.value_array
override var value by value(Config.suMultiuserMode) { override var value = Config.suMultiuserMode
set(value) = setS(value, field, { field = it }) {
Config.suMultiuserMode = entryValues[it].toInt() Config.suMultiuserMode = entryValues[it].toInt()
} }
@ -237,7 +249,8 @@ object MountNamespaceMode : SettingsItem.Selector() {
override val entryRes = R.array.namespace override val entryRes = R.array.namespace
override val entryValRes = R.array.value_array override val entryValRes = R.array.value_array
override var value by value(Config.suMntNamespaceMode) { override var value = Config.suMntNamespaceMode
set(value) = setS(value, field, { field = it }) {
Config.suMntNamespaceMode = entryValues[it].toInt() Config.suMntNamespaceMode = entryValues[it].toInt()
} }
@ -250,7 +263,8 @@ object AutomaticResponse : SettingsItem.Selector() {
override val entryRes = R.array.auto_response override val entryRes = R.array.auto_response
override val entryValRes = R.array.value_array override val entryValRes = R.array.value_array
override var value by value(Config.suAutoReponse) { override var value = Config.suAutoReponse
set(value) = setS(value, field, { field = it }) {
Config.suAutoReponse = entryValues[it].toInt() Config.suAutoReponse = entryValues[it].toInt()
} }
} }
@ -260,7 +274,8 @@ object RequestTimeout : SettingsItem.Selector() {
override val entryRes = R.array.request_timeout override val entryRes = R.array.request_timeout
override val entryValRes = R.array.request_timeout_value override val entryValRes = R.array.request_timeout_value
override var value by value(selected) { override var value = selected
set(value) = setS(value, field, { field = it }) {
Config.suDefaultTimeout = entryValues[it].toInt() Config.suDefaultTimeout = entryValues[it].toInt()
} }
@ -273,7 +288,8 @@ object SUNotification : SettingsItem.Selector() {
override val entryRes = R.array.su_notification override val entryRes = R.array.su_notification
override val entryValRes = R.array.value_array override val entryValRes = R.array.value_array
override var value by value(Config.suNotification) { override var value = Config.suNotification
set(value) = setS(value, field, { field = it }) {
Config.suNotification = entryValues[it].toInt() Config.suNotification = entryValues[it].toInt()
} }
} }

View File

@ -19,7 +19,7 @@ import com.topjohnwu.magisk.core.utils.BiometricHelper
import com.topjohnwu.magisk.model.entity.recycler.SpinnerRvItem import com.topjohnwu.magisk.model.entity.recycler.SpinnerRvItem
import com.topjohnwu.magisk.model.events.DieEvent import com.topjohnwu.magisk.model.events.DieEvent
import com.topjohnwu.magisk.ui.base.BaseViewModel import com.topjohnwu.magisk.ui.base.BaseViewModel
import com.topjohnwu.magisk.utils.observable import com.topjohnwu.magisk.utils.set
import com.topjohnwu.superuser.internal.UiThreadHandler import com.topjohnwu.superuser.internal.UiThreadHandler
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import me.tatarka.bindingcollectionadapter2.BindingListViewAdapter import me.tatarka.bindingcollectionadapter2.BindingListViewAdapter
@ -34,19 +34,32 @@ class SuRequestViewModel(
) : BaseViewModel() { ) : BaseViewModel() {
@get:Bindable @get:Bindable
var icon by observable(null as Drawable?, BR.icon) var icon: Drawable? = null
set(value) = set(value, field, { field = it }, BR.icon)
@get:Bindable @get:Bindable
var title by observable("", BR.title) var title = ""
set(value) = set(value, field, { field = it }, BR.title)
@get:Bindable @get:Bindable
var packageName by observable("", BR.packageName) var packageName = ""
set(value) = set(value, field, { field = it }, BR.packageName)
@get:Bindable @get:Bindable
var denyText by observable(res.getString(R.string.deny), BR.denyText) var denyText = res.getString(R.string.deny)
set(value) = set(value, field, { field = it }, BR.denyText)
@get:Bindable @get:Bindable
var warningText by observable(res.getString(R.string.su_warning), BR.warningText) var warningText = res.getString(R.string.su_warning)
set(value) = set(value, field, { field = it }, BR.warningText)
@get:Bindable @get:Bindable
var selectedItemPosition by observable(0, BR.selectedItemPosition) var selectedItemPosition = 0
set(value) = set(value, field, { field = it }, BR.selectedItemPosition)
@get:Bindable @get:Bindable
var grantEnabled by observable(false, BR.grantEnabled) var grantEnabled = false
set(value) = set(value, field, { field = it }, BR.grantEnabled)
private val items = res.getStringArray(R.array.allow_timeout).map { SpinnerRvItem(it) } private val items = res.getStringArray(R.array.allow_timeout).map { SpinnerRvItem(it) }
val adapter = BindingListViewAdapter<SpinnerRvItem>(1).apply { val adapter = BindingListViewAdapter<SpinnerRvItem>(1).apply {

View File

@ -2,8 +2,6 @@ package com.topjohnwu.magisk.utils
import androidx.databinding.Observable import androidx.databinding.Observable
import androidx.databinding.PropertyChangeRegistry import androidx.databinding.PropertyChangeRegistry
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
/** /**
* Modified from https://github.com/skoumalcz/teanity/blob/1.2/core/src/main/java/com/skoumal/teanity/observable/Notifyable.kt * Modified from https://github.com/skoumalcz/teanity/blob/1.2/core/src/main/java/com/skoumal/teanity/observable/Notifyable.kt
@ -52,105 +50,32 @@ interface ObservableHost : Observable {
} }
/** /**
* Declares delegated property in [ObservableHost] parent. This property is available for DataBinding * Injects boilerplate implementation for {@literal @}[androidx.databinding.Bindable] field setters.
* to be observed as usual. The only caveat is that in order for binding to generate the [fieldId]
* it has to be annotated accordingly.
*
* The annotation however give very strict control over your internal fields and overall reduce
* overhead in notifying observers. (In comparison to [androidx.databinding.ObservableField])
* It helps the kotlin code to feel more,... _native_, while respecting the original functionality.
* *
* # Examples: * # Examples:
*
* ## The most basic usage would probably be:
* ```kotlin * ```kotlin
* @get:Bindable * @get:Bindable
* var myField by observable(defaultValue, BR.myField) * var myField = defaultValue
* private set * set(value) = set(value, field, { field = it }, BR.myField) {
* ``` * doSomething(it)
*
* ## You can use the field as public read/write, of course:
* ```kotlin
* @get:Bindable
* var myField by observable(defaultValue, BR.myField)
* ```
*
* ## Please beware that delegated property instantiates one class per property
* We discourage using simple getters via delegated properties. Instead you can do something like
* this:
*
* ```kotlin
* @get:Bindable
* var myField by observable(defaultValue, BR.myField, BR.myTransformedField)
*
* var myTransformedField
* @Bindable get() {
* return myField.transform()
* }
* set(value) {
* myField = value.transform()
* } * }
* ``` * ```
*
* */ * */
// Optimize for the most common use case inline fun <reified T> ObservableHost.set(
// Generic type is reified to optimize primitive types new: T, old: T, setter: (T) -> Unit, fieldId: Int, afterChanged: (T) -> Unit = {}) {
inline fun <reified T> ObservableHost.observable( if (old != new) {
initialValue: T, setter(new)
fieldId: Int
) = object : ReadWriteProperty<ObservableHost, T> {
private var field = initialValue
override fun getValue(thisRef: ObservableHost, property: KProperty<*>): T {
return field
}
@Synchronized
override fun setValue(thisRef: ObservableHost, property: KProperty<*>, value: T) {
if (field != value) {
field = value
notifyPropertyChanged(fieldId) notifyPropertyChanged(fieldId)
} afterChanged(new)
} }
} }
inline fun <reified T> ObservableHost.observable( inline fun <reified T> ObservableHost.set(
initialValue: T, new: T, old: T, setter: (T) -> Unit, vararg fieldIds: Int, afterChanged: (T) -> Unit = {}) {
vararg fieldIds: Int if (old != new) {
) = object : ReadWriteProperty<ObservableHost, T> { setter(new)
private var field = initialValue
override fun getValue(thisRef: ObservableHost, property: KProperty<*>): T {
return field
}
@Synchronized
override fun setValue(thisRef: ObservableHost, property: KProperty<*>, value: T) {
if (field != value) {
field = value
fieldIds.forEach { notifyPropertyChanged(it) } fieldIds.forEach { notifyPropertyChanged(it) }
} afterChanged(new)
}
}
inline fun <reified T> ObservableHost.observable(
initialValue: T,
vararg fieldIds: Int,
crossinline afterChanged: (T) -> Unit
) = object : ReadWriteProperty<ObservableHost, T> {
private var field = initialValue
override fun getValue(thisRef: ObservableHost, property: KProperty<*>): T {
return field
}
@Synchronized
override fun setValue(thisRef: ObservableHost, property: KProperty<*>, value: T) {
if (field != value) {
field = value
fieldIds.forEach { notifyPropertyChanged(it) }
afterChanged(value)
}
} }
} }

View File

@ -16,7 +16,7 @@ buildscript {
} }
dependencies { dependencies {
classpath("com.android.tools.build:gradle:4.0.0") classpath("com.android.tools.build:gradle:4.0.1")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.72") classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.72")
classpath("androidx.navigation:navigation-safe-args-gradle-plugin:${Deps.vNav}") classpath("androidx.navigation:navigation-safe-args-gradle-plugin:${Deps.vNav}")