1 /*
<lambda>null2  * Copyright 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.compose.runtime.saveable
18 
19 import androidx.compose.runtime.Composable
20 import androidx.compose.runtime.MutableState
21 import androidx.compose.runtime.RememberObserver
22 import androidx.compose.runtime.SideEffect
23 import androidx.compose.runtime.SnapshotMutationPolicy
24 import androidx.compose.runtime.currentCompositeKeyHashCode
25 import androidx.compose.runtime.mutableStateOf
26 import androidx.compose.runtime.neverEqualPolicy
27 import androidx.compose.runtime.referentialEqualityPolicy
28 import androidx.compose.runtime.remember
29 import androidx.compose.runtime.snapshots.SnapshotMutableState
30 import androidx.compose.runtime.structuralEqualityPolicy
31 import androidx.compose.runtime.toString
32 
33 /**
34  * Remember the value produced by [init].
35  *
36  * It behaves similarly to [remember], but the stored value will survive the activity or process
37  * recreation using the saved instance state mechanism (for example it happens when the screen is
38  * rotated in the Android application).
39  *
40  * @sample androidx.compose.runtime.saveable.samples.RememberSaveable
41  *
42  * If you use it with types which can be stored inside the Bundle then it will be saved and restored
43  * automatically using [autoSaver], otherwise you will need to provide a custom [Saver]
44  * implementation via the [saver] param.
45  *
46  * @sample androidx.compose.runtime.saveable.samples.RememberSaveableCustomSaver
47  *
48  * You can use it with a value stored inside [androidx.compose.runtime.mutableStateOf].
49  *
50  * @sample androidx.compose.runtime.saveable.samples.RememberSaveableWithMutableState
51  *
52  * If the value inside the MutableState can be stored inside the Bundle it would be saved and
53  * restored automatically, otherwise you will need to provide a custom [Saver] implementation via an
54  * overload with which has `stateSaver` param.
55  *
56  * @sample androidx.compose.runtime.saveable.samples.RememberSaveableWithMutableStateAndCustomSaver
57  * @param inputs A set of inputs such that, when any of them have changed, will cause the state to
58  *   reset and [init] to be rerun. Note that state restoration DOES NOT validate against inputs
59  *   provided before value was saved.
60  * @param saver The [Saver] object which defines how the state is saved and restored.
61  * @param key An optional key to be used as a key for the saved value. If not provided we use the
62  *   automatically generated by the Compose runtime which is unique for the every exact code
63  *   location in the composition tree
64  * @param init A factory function to create the initial value of this state
65  */
66 @Composable
67 fun <T : Any> rememberSaveable(
68     vararg inputs: Any?,
69     saver: Saver<T, out Any> = autoSaver(),
70     key: String? = null,
71     init: () -> T
72 ): T {
73     val compositeKey = currentCompositeKeyHashCode
74     // key is the one provided by the user or the one generated by the compose runtime
75     val finalKey =
76         if (!key.isNullOrEmpty()) {
77             key
78         } else {
79             compositeKey.toString(MaxSupportedRadix)
80         }
81     @Suppress("UNCHECKED_CAST") (saver as Saver<T, Any>)
82 
83     val registry = LocalSaveableStateRegistry.current
84 
85     val holder = remember {
86         // value is restored using the registry or created via [init] lambda
87         val restored = registry?.consumeRestored(finalKey)?.let { saver.restore(it) }
88         val finalValue = restored ?: init()
89         SaveableHolder(saver, registry, finalKey, finalValue, inputs)
90     }
91 
92     val value = holder.getValueIfInputsDidntChange(inputs) ?: init()
93     SideEffect { holder.update(saver, registry, finalKey, value, inputs) }
94 
95     return value
96 }
97 
98 /**
99  * Remember the value produced by [init].
100  *
101  * It behaves similarly to [remember], but the stored value will survive the activity or process
102  * recreation using the saved instance state mechanism (for example it happens when the screen is
103  * rotated in the Android application).
104  *
105  * Use this overload if you remember a mutable state with a type which can't be stored in the Bundle
106  * so you have to provide a custom saver object.
107  *
108  * @sample androidx.compose.runtime.saveable.samples.RememberSaveableWithMutableStateAndCustomSaver
109  * @param inputs A set of inputs such that, when any of them have changed, will cause the state to
110  *   reset and [init] to be rerun. Note that state restoration DOES NOT validate against inputs
111  *   provided before value was saved.
112  * @param stateSaver The [Saver] object which defines how the value inside the MutableState is saved
113  *   and restored.
114  * @param key An optional key to be used as a key for the saved value. If not provided we use the
115  *   automatically generated by the Compose runtime which is unique for the every exact code
116  *   location in the composition tree
117  * @param init A factory function to create the initial value of this state
118  */
119 @Composable
rememberSaveablenull120 fun <T> rememberSaveable(
121     vararg inputs: Any?,
122     stateSaver: Saver<T, out Any>,
123     key: String? = null,
124     init: () -> MutableState<T>
125 ): MutableState<T> =
126     rememberSaveable(*inputs, saver = mutableStateSaver(stateSaver), key = key, init = init)
127 
128 private class SaveableHolder<T>(
129     private var saver: Saver<T, Any>,
130     private var registry: SaveableStateRegistry?,
131     private var key: String,
132     private var value: T,
133     private var inputs: Array<out Any?>
134 ) : SaverScope, RememberObserver {
135     private var entry: SaveableStateRegistry.Entry? = null
136     /** Value provider called by the registry. */
137     private val valueProvider = {
138         with(saver) { save(requireNotNull(value) { "Value should be initialized" }) }
139     }
140 
141     fun update(
142         saver: Saver<T, Any>,
143         registry: SaveableStateRegistry?,
144         key: String,
145         value: T,
146         inputs: Array<out Any?>
147     ) {
148         var entryIsOutdated = false
149         if (this.registry !== registry) {
150             this.registry = registry
151             entryIsOutdated = true
152         }
153         if (this.key != key) {
154             this.key = key
155             entryIsOutdated = true
156         }
157         this.saver = saver
158         this.value = value
159         this.inputs = inputs
160         if (entry != null && entryIsOutdated) {
161             entry?.unregister()
162             entry = null
163             register()
164         }
165     }
166 
167     private fun register() {
168         val registry = registry
169         require(entry == null) { "entry($entry) is not null" }
170         if (registry != null) {
171             registry.requireCanBeSaved(valueProvider())
172             entry = registry.registerProvider(key, valueProvider)
173         }
174     }
175 
176     override fun canBeSaved(value: Any): Boolean {
177         val registry = registry
178         return registry == null || registry.canBeSaved(value)
179     }
180 
181     override fun onRemembered() {
182         register()
183     }
184 
185     override fun onForgotten() {
186         entry?.unregister()
187     }
188 
189     override fun onAbandoned() {
190         entry?.unregister()
191     }
192 
193     fun getValueIfInputsDidntChange(inputs: Array<out Any?>): T? {
194         return if (inputs.contentEquals(this.inputs)) {
195             value
196         } else {
197             null
198         }
199     }
200 }
201 
202 @Suppress("UNCHECKED_CAST")
mutableStateSavernull203 private fun <T> mutableStateSaver(inner: Saver<T, out Any>) =
204     with(inner as Saver<T, Any>) {
205         Saver<MutableState<T>, MutableState<Any?>>(
206             save = { state ->
207                 require(state is SnapshotMutableState<T>) {
208                     "If you use a custom MutableState implementation you have to write a custom " +
209                         "Saver and pass it as a saver param to rememberSaveable()"
210                 }
211                 val saved = save(state.value)
212                 if (saved != null) {
213                     mutableStateOf(saved, state.policy as SnapshotMutationPolicy<Any?>)
214                 } else {
215                     // if the inner saver returned null we need to return null as well so the
216                     // user's init lambda will be used instead of restoring mutableStateOf(null)
217                     null
218                 }
219             },
220             restore =
221                 @Suppress("UNCHECKED_CAST", "ExceptionMessage") {
222                     require(it is SnapshotMutableState<Any?>)
223                     mutableStateOf(
224                         if (it.value != null) restore(it.value!!) else null,
225                         it.policy as SnapshotMutationPolicy<T?>
226                     )
227                         as MutableState<T>
228                 }
229         )
230     }
231 
requireCanBeSavednull232 private fun SaveableStateRegistry.requireCanBeSaved(value: Any?) {
233     if (value != null && !canBeSaved(value)) {
234         throw IllegalArgumentException(
235             if (value is SnapshotMutableState<*>) {
236                 if (
237                     value.policy !== neverEqualPolicy<Any?>() &&
238                         value.policy !== structuralEqualityPolicy<Any?>() &&
239                         value.policy !== referentialEqualityPolicy<Any?>()
240                 ) {
241                     "If you use a custom SnapshotMutationPolicy for your MutableState you have to" +
242                         " write a custom Saver"
243                 } else {
244                     "MutableState containing ${value.value} cannot be saved using the current " +
245                         "SaveableStateRegistry. The default implementation only supports types " +
246                         "which can be stored inside the Bundle. Please consider implementing a " +
247                         "custom Saver for this class and pass it as a stateSaver parameter to " +
248                         "rememberSaveable()."
249                 }
250             } else {
251                 generateCannotBeSavedErrorMessage(value)
252             }
253         )
254     }
255 }
256 
generateCannotBeSavedErrorMessagenull257 internal fun generateCannotBeSavedErrorMessage(value: Any): String =
258     "$value cannot be saved using the current SaveableStateRegistry. The default " +
259         "implementation only supports types which can be stored inside the Bundle" +
260         ". Please consider implementing a custom Saver for this class and pass it" +
261         " to rememberSaveable()."
262 
263 /** The maximum radix available for conversion to and from strings. */
264 private val MaxSupportedRadix = 36
265