From 171b9fd684c167a02cde017c74dbad3933518bd5 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 10 Mar 2022 21:15:15 +0700 Subject: [PATCH] add RvmSavedStateDelegates --- reactiveviewmodel/build.gradle | 2 +- .../core/RvmSavedStateDelegates.kt | 127 ++++++++++++++++++ 2 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 reactiveviewmodel/src/main/java/com/alexdeww/reactiveviewmodel/core/RvmSavedStateDelegates.kt diff --git a/reactiveviewmodel/build.gradle b/reactiveviewmodel/build.gradle index 255f8dc..8c2f122 100644 --- a/reactiveviewmodel/build.gradle +++ b/reactiveviewmodel/build.gradle @@ -44,7 +44,7 @@ afterEvaluate { release(MavenPublication) { from components.release groupId = 'com.alexdeww.reactiveviewmodel' - version = '2.4.1' + version = '2.4.2' } } } diff --git a/reactiveviewmodel/src/main/java/com/alexdeww/reactiveviewmodel/core/RvmSavedStateDelegates.kt b/reactiveviewmodel/src/main/java/com/alexdeww/reactiveviewmodel/core/RvmSavedStateDelegates.kt new file mode 100644 index 0000000..473304f --- /dev/null +++ b/reactiveviewmodel/src/main/java/com/alexdeww/reactiveviewmodel/core/RvmSavedStateDelegates.kt @@ -0,0 +1,127 @@ +package com.alexdeww.reactiveviewmodel.core + +import androidx.lifecycle.SavedStateHandle +import com.alexdeww.reactiveviewmodel.core.property.State +import com.alexdeww.reactiveviewmodel.widget.BaseVisualControl +import com.alexdeww.reactiveviewmodel.widget.FormatterAction +import com.alexdeww.reactiveviewmodel.widget.InputControl +import com.alexdeww.reactiveviewmodel.widget.RatingControl +import kotlin.properties.ReadOnlyProperty +import kotlin.properties.ReadWriteProperty +import kotlin.reflect.KProperty + +fun SavedStateHandle.delegate( + initValue: (thisRef: ReactiveViewModel, stateHandle: SavedStateHandle, key: String) -> T +): ReadWriteProperty = SavedStateProperty(this, initValue) + +fun SavedStateHandle.value( + initialValue: T? = null +): ReadWriteProperty = delegate { _, stateHandle, key -> + if (stateHandle.contains(key)) stateHandle[key] + else initialValue +} + +fun SavedStateHandle.valueNonNull( + defaultValue: T +): ReadWriteProperty = delegate { _, stateHandle, key -> + stateHandle[key] ?: defaultValue +} + +fun SavedStateHandle.state( + initialValue: T? = null, + debounceInterval: Long? = null +): ReadOnlyProperty> = delegate { thisRef, stateHandle, key -> + val state = State(stateHandle[key] ?: initialValue, debounceInterval) + thisRef.run { state.viewFlowable.subscribe { stateHandle[key] = it }.disposeOnCleared() } + state +} + +fun SavedStateHandle.inputControl( + initialText: String = "", + hideErrorOnUserInput: Boolean = true, + formatter: FormatterAction? = null, + initialEnabled: Boolean = true, + initialVisibility: BaseVisualControl.Visibility = BaseVisualControl.Visibility.VISIBLE +): ReadOnlyProperty = delegate { thisRef, stateHandle, key -> + val textKey = "$key.text" + val errorKey = "$key.error" + val enabledKey = "$key.enabled" + val visibilityKey = "$key.visibility" + val control = InputControl( + initialText = stateHandle[textKey] ?: initialText, + hideErrorOnUserInput = hideErrorOnUserInput, + formatter = formatter, + initialEnabled = stateHandle[enabledKey] ?: initialEnabled, + initialVisibility = stateHandle[visibilityKey] ?: initialVisibility + ) + thisRef.run { + control.value.viewFlowable + .subscribe { stateHandle[textKey] = it } + .disposeOnCleared() + control.error.viewFlowable + .subscribe { stateHandle[errorKey] = it } + .disposeOnCleared() + control.enabled.viewFlowable + .subscribe { stateHandle[enabledKey] = it } + .disposeOnCleared() + control.visibility.viewFlowable + .subscribe { stateHandle[visibilityKey] = it } + .disposeOnCleared() + } + control +} + +fun SavedStateHandle.ratingControl( + initialValue: Float = 0f, + initialEnabled: Boolean = true, + initialVisibility: BaseVisualControl.Visibility = BaseVisualControl.Visibility.VISIBLE +): ReadOnlyProperty = delegate { thisRef, stateHandle, key -> + val ratingKey = "$key.rating" + val enabledKey = "$key.enabled" + val visibilityKey = "$key.visibility" + val control = RatingControl( + initialValue = stateHandle[ratingKey] ?: initialValue, + initialEnabled = stateHandle[enabledKey] ?: initialEnabled, + initialVisibility = stateHandle[visibilityKey] ?: initialVisibility + ) + thisRef.run { + control.value.viewFlowable + .subscribe { stateHandle[ratingKey] = it } + .disposeOnCleared() + control.enabled.viewFlowable + .subscribe { stateHandle[enabledKey] = it } + .disposeOnCleared() + control.visibility.viewFlowable + .subscribe { stateHandle[visibilityKey] = it } + .disposeOnCleared() + } + control +} + +@PublishedApi +internal class SavedStateProperty( + private val savedStateHandle: SavedStateHandle, + private val initValue: (thisRef: ReactiveViewModel, stateHandle: SavedStateHandle, key: String) -> T +) : ReadWriteProperty { + + private object NoneValue + + private var value: Any? = NoneValue + + @Suppress("UNCHECKED_CAST") + override fun getValue(thisRef: ReactiveViewModel, property: KProperty<*>): T { + if (value === NoneValue) { + value = initValue(thisRef, savedStateHandle, getStateKey(thisRef, property)) + } + return value as T + } + + override fun setValue(thisRef: ReactiveViewModel, property: KProperty<*>, value: T) { + this.value = value + savedStateHandle[getStateKey(thisRef, property)] = value + } + + private fun getStateKey(thisRef: ReactiveViewModel, property: KProperty<*>): String = + "${thisRef::class.java.simpleName}.${property.name}" + +}