1 /*
<lambda>null2  * Copyright 2020 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.collection.mutableScatterMapOf
20 import androidx.compose.runtime.Composable
21 import androidx.compose.runtime.CompositionLocalProvider
22 import androidx.compose.runtime.DisposableEffect
23 import androidx.compose.runtime.ReusableContent
24 import androidx.compose.runtime.remember
25 
26 /**
27  * Allows to save the state defined with [rememberSaveable] for the subtree before disposing it to
28  * make it possible to compose it back next time with the restored state. It allows different
29  * navigation patterns to keep the ui state like scroll position for the currently not composed
30  * screens from the backstack.
31  *
32  * @sample androidx.compose.runtime.saveable.samples.SimpleNavigationWithSaveableStateSample
33  *
34  * The content should be composed using [SaveableStateProvider] while providing a key representing
35  * this content. Next time [SaveableStateProvider] will be used with the same key its state will be
36  * restored.
37  */
38 interface SaveableStateHolder {
39     /**
40      * Put your content associated with a [key] inside the [content]. This will automatically save
41      * all the states defined with [rememberSaveable] before disposing the content and will restore
42      * the states when you compose with this key again.
43      *
44      * @param key to be used for saving and restoring the states for the subtree. Note that on
45      *   Android you can only use types which can be stored inside the Bundle.
46      * @param content the content for which [key] is associated.
47      */
48     @Composable fun SaveableStateProvider(key: Any, content: @Composable () -> Unit)
49 
50     /** Removes the saved state associated with the passed [key]. */
51     fun removeState(key: Any)
52 }
53 
54 /** Creates and remembers the instance of [SaveableStateHolder]. */
55 @Composable
rememberSaveableStateHoldernull56 fun rememberSaveableStateHolder(): SaveableStateHolder =
57     rememberSaveable(saver = SaveableStateHolderImpl.Saver) { SaveableStateHolderImpl() }
<lambda>null58         .apply { parentSaveableStateRegistry = LocalSaveableStateRegistry.current }
59 
60 private class SaveableStateHolderImpl(
61     private val savedStates: MutableMap<Any, Map<String, List<Any?>>> = mutableMapOf()
62 ) : SaveableStateHolder {
63     private val registries = mutableScatterMapOf<Any, SaveableStateRegistry>()
64     var parentSaveableStateRegistry: SaveableStateRegistry? = null
<lambda>null65     private val canBeSaved: (Any) -> Boolean = {
66         parentSaveableStateRegistry?.canBeSaved(it) ?: true
67     }
68 
69     @Composable
70     override fun SaveableStateProvider(key: Any, content: @Composable () -> Unit) {
<lambda>null71         ReusableContent(key) {
72             val registry = remember {
73                 require(canBeSaved(key)) {
74                     "Type of the key $key is not supported. On Android you can only use types " +
75                         "which can be stored inside the Bundle."
76                 }
77                 SaveableStateRegistry(savedStates[key], canBeSaved)
78             }
79             CompositionLocalProvider(
80                 LocalSaveableStateRegistry provides registry,
81                 content = content
82             )
83             DisposableEffect(Unit) {
84                 require(key !in registries) { "Key $key was used multiple times " }
85                 savedStates -= key
86                 registries[key] = registry
87                 onDispose {
88                     if (registries.remove(key) === registry) {
89                         registry.saveTo(savedStates, key)
90                     }
91                 }
92             }
93         }
94     }
95 
saveAllnull96     private fun saveAll(): MutableMap<Any, Map<String, List<Any?>>>? {
97         val map = savedStates
98         registries.forEach { key, registry -> registry.saveTo(map, key) }
99         return map.ifEmpty { null }
100     }
101 
removeStatenull102     override fun removeState(key: Any) {
103         if (registries.remove(key) == null) {
104             savedStates -= key
105         }
106     }
107 
SaveableStateRegistrynull108     private fun SaveableStateRegistry.saveTo(
109         map: MutableMap<Any, Map<String, List<Any?>>>,
110         key: Any
111     ) {
112         val savedData = performSave()
113         if (savedData.isEmpty()) {
114             map -= key
115         } else {
116             map[key] = savedData
117         }
118     }
119 
120     companion object {
121         val Saver: Saver<SaveableStateHolderImpl, *> =
<lambda>null122             Saver(save = { it.saveAll() }, restore = { SaveableStateHolderImpl(it) })
123     }
124 }
125