Skip to content

Commit 171b9fd

Browse files
author
Alex
committed
add RvmSavedStateDelegates
1 parent c229948 commit 171b9fd

File tree

2 files changed

+128
-1
lines changed

2 files changed

+128
-1
lines changed

reactiveviewmodel/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ afterEvaluate {
4444
release(MavenPublication) {
4545
from components.release
4646
groupId = 'com.alexdeww.reactiveviewmodel'
47-
version = '2.4.1'
47+
version = '2.4.2'
4848
}
4949
}
5050
}
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
package com.alexdeww.reactiveviewmodel.core
2+
3+
import androidx.lifecycle.SavedStateHandle
4+
import com.alexdeww.reactiveviewmodel.core.property.State
5+
import com.alexdeww.reactiveviewmodel.widget.BaseVisualControl
6+
import com.alexdeww.reactiveviewmodel.widget.FormatterAction
7+
import com.alexdeww.reactiveviewmodel.widget.InputControl
8+
import com.alexdeww.reactiveviewmodel.widget.RatingControl
9+
import kotlin.properties.ReadOnlyProperty
10+
import kotlin.properties.ReadWriteProperty
11+
import kotlin.reflect.KProperty
12+
13+
fun <T> SavedStateHandle.delegate(
14+
initValue: (thisRef: ReactiveViewModel, stateHandle: SavedStateHandle, key: String) -> T
15+
): ReadWriteProperty<ReactiveViewModel, T> = SavedStateProperty(this, initValue)
16+
17+
fun <T : Any> SavedStateHandle.value(
18+
initialValue: T? = null
19+
): ReadWriteProperty<ReactiveViewModel, T?> = delegate { _, stateHandle, key ->
20+
if (stateHandle.contains(key)) stateHandle[key]
21+
else initialValue
22+
}
23+
24+
fun <T : Any> SavedStateHandle.valueNonNull(
25+
defaultValue: T
26+
): ReadWriteProperty<ReactiveViewModel, T> = delegate { _, stateHandle, key ->
27+
stateHandle[key] ?: defaultValue
28+
}
29+
30+
fun <T : Any> SavedStateHandle.state(
31+
initialValue: T? = null,
32+
debounceInterval: Long? = null
33+
): ReadOnlyProperty<ReactiveViewModel, State<T>> = delegate { thisRef, stateHandle, key ->
34+
val state = State(stateHandle[key] ?: initialValue, debounceInterval)
35+
thisRef.run { state.viewFlowable.subscribe { stateHandle[key] = it }.disposeOnCleared() }
36+
state
37+
}
38+
39+
fun SavedStateHandle.inputControl(
40+
initialText: String = "",
41+
hideErrorOnUserInput: Boolean = true,
42+
formatter: FormatterAction? = null,
43+
initialEnabled: Boolean = true,
44+
initialVisibility: BaseVisualControl.Visibility = BaseVisualControl.Visibility.VISIBLE
45+
): ReadOnlyProperty<ReactiveViewModel, InputControl> = delegate { thisRef, stateHandle, key ->
46+
val textKey = "$key.text"
47+
val errorKey = "$key.error"
48+
val enabledKey = "$key.enabled"
49+
val visibilityKey = "$key.visibility"
50+
val control = InputControl(
51+
initialText = stateHandle[textKey] ?: initialText,
52+
hideErrorOnUserInput = hideErrorOnUserInput,
53+
formatter = formatter,
54+
initialEnabled = stateHandle[enabledKey] ?: initialEnabled,
55+
initialVisibility = stateHandle[visibilityKey] ?: initialVisibility
56+
)
57+
thisRef.run {
58+
control.value.viewFlowable
59+
.subscribe { stateHandle[textKey] = it }
60+
.disposeOnCleared()
61+
control.error.viewFlowable
62+
.subscribe { stateHandle[errorKey] = it }
63+
.disposeOnCleared()
64+
control.enabled.viewFlowable
65+
.subscribe { stateHandle[enabledKey] = it }
66+
.disposeOnCleared()
67+
control.visibility.viewFlowable
68+
.subscribe { stateHandle[visibilityKey] = it }
69+
.disposeOnCleared()
70+
}
71+
control
72+
}
73+
74+
fun SavedStateHandle.ratingControl(
75+
initialValue: Float = 0f,
76+
initialEnabled: Boolean = true,
77+
initialVisibility: BaseVisualControl.Visibility = BaseVisualControl.Visibility.VISIBLE
78+
): ReadOnlyProperty<ReactiveViewModel, RatingControl> = delegate { thisRef, stateHandle, key ->
79+
val ratingKey = "$key.rating"
80+
val enabledKey = "$key.enabled"
81+
val visibilityKey = "$key.visibility"
82+
val control = RatingControl(
83+
initialValue = stateHandle[ratingKey] ?: initialValue,
84+
initialEnabled = stateHandle[enabledKey] ?: initialEnabled,
85+
initialVisibility = stateHandle[visibilityKey] ?: initialVisibility
86+
)
87+
thisRef.run {
88+
control.value.viewFlowable
89+
.subscribe { stateHandle[ratingKey] = it }
90+
.disposeOnCleared()
91+
control.enabled.viewFlowable
92+
.subscribe { stateHandle[enabledKey] = it }
93+
.disposeOnCleared()
94+
control.visibility.viewFlowable
95+
.subscribe { stateHandle[visibilityKey] = it }
96+
.disposeOnCleared()
97+
}
98+
control
99+
}
100+
101+
@PublishedApi
102+
internal class SavedStateProperty<T>(
103+
private val savedStateHandle: SavedStateHandle,
104+
private val initValue: (thisRef: ReactiveViewModel, stateHandle: SavedStateHandle, key: String) -> T
105+
) : ReadWriteProperty<ReactiveViewModel, T> {
106+
107+
private object NoneValue
108+
109+
private var value: Any? = NoneValue
110+
111+
@Suppress("UNCHECKED_CAST")
112+
override fun getValue(thisRef: ReactiveViewModel, property: KProperty<*>): T {
113+
if (value === NoneValue) {
114+
value = initValue(thisRef, savedStateHandle, getStateKey(thisRef, property))
115+
}
116+
return value as T
117+
}
118+
119+
override fun setValue(thisRef: ReactiveViewModel, property: KProperty<*>, value: T) {
120+
this.value = value
121+
savedStateHandle[getStateKey(thisRef, property)] = value
122+
}
123+
124+
private fun getStateKey(thisRef: ReactiveViewModel, property: KProperty<*>): String =
125+
"${thisRef::class.java.simpleName}.${property.name}"
126+
127+
}

0 commit comments

Comments
 (0)