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 @file:JvmName("SavedStateHandleSupport")
18 
19 package androidx.lifecycle
20 
21 import androidx.annotation.MainThread
22 import androidx.lifecycle.ViewModelProvider.Companion.VIEW_MODEL_KEY
23 import androidx.lifecycle.viewmodel.CreationExtras
24 import androidx.savedstate.SavedState
25 import androidx.savedstate.SavedStateRegistry
26 import androidx.savedstate.SavedStateRegistryOwner
27 import androidx.savedstate.read
28 import androidx.savedstate.savedState
29 import androidx.savedstate.write
30 import kotlin.jvm.JvmField
31 import kotlin.jvm.JvmName
32 import kotlin.reflect.KClass
33 
34 internal const val VIEWMODEL_KEY = "androidx.lifecycle.internal.SavedStateHandlesVM"
35 internal const val SAVED_STATE_KEY = "androidx.lifecycle.internal.SavedStateHandlesProvider"
36 
37 /**
38  * Enables the support of [SavedStateHandle] in a component.
39  *
40  * After this method, [createSavedStateHandle] can be called on [CreationExtras] containing this
41  * [SavedStateRegistryOwner] / [ViewModelStoreOwner].
42  *
43  * Must be called while component is in `INITIALIZED` or `CREATED` state and before a [ViewModel]
44  * with [SavedStateHandle] is requested.
45  */
46 @MainThread
47 public fun <T> T.enableSavedStateHandles() where
48 T : SavedStateRegistryOwner,
49 T : ViewModelStoreOwner {
50     val currentState = lifecycle.currentState
51     require(currentState == Lifecycle.State.INITIALIZED || currentState == Lifecycle.State.CREATED)
52 
53     // Add the SavedStateProvider used to save SavedStateHandles
54     // if we haven't already registered the provider
55     if (savedStateRegistry.getSavedStateProvider(SAVED_STATE_KEY) == null) {
56         val provider = SavedStateHandlesProvider(savedStateRegistry, this)
57         savedStateRegistry.registerSavedStateProvider(SAVED_STATE_KEY, provider)
58         lifecycle.addObserver(SavedStateHandleAttacher(provider))
59     }
60 }
61 
createSavedStateHandlenull62 private fun createSavedStateHandle(
63     savedStateRegistryOwner: SavedStateRegistryOwner,
64     viewModelStoreOwner: ViewModelStoreOwner,
65     key: String,
66     defaultArgs: SavedState?
67 ): SavedStateHandle {
68     val provider = savedStateRegistryOwner.savedStateHandlesProvider
69     val viewModel = viewModelStoreOwner.savedStateHandlesVM
70     // If we already have a reference to a previously created SavedStateHandle
71     // for a given key stored in our ViewModel, use that. Otherwise, create
72     // a new SavedStateHandle, providing it any restored state we might have saved
73     return viewModel.handles[key]
74         ?: SavedStateHandle.createHandle(provider.consumeRestoredStateForKey(key), defaultArgs)
75             .also { viewModel.handles[key] = it }
76 }
77 
78 /**
79  * Creates `SavedStateHandle` that can be used in your ViewModels
80  *
81  * This function requires [enableSavedStateHandles] call during the component initialization. Latest
82  * versions of androidx components like `ComponentActivity`, `Fragment`, `NavBackStackEntry` makes
83  * this call automatically.
84  *
85  * This [CreationExtras] must contain [SAVED_STATE_REGISTRY_OWNER_KEY], [VIEW_MODEL_STORE_OWNER_KEY]
86  * and [VIEW_MODEL_KEY].
87  *
88  * @throws IllegalArgumentException if this `CreationExtras` are missing required keys:
89  *   `ViewModelStoreOwnerKey`, `SavedStateRegistryOwnerKey`, `VIEW_MODEL_KEY`
90  */
91 @MainThread
createSavedStateHandlenull92 public fun CreationExtras.createSavedStateHandle(): SavedStateHandle {
93     val savedStateRegistryOwner =
94         this[SAVED_STATE_REGISTRY_OWNER_KEY]
95             ?: throw IllegalArgumentException(
96                 "CreationExtras must have a value by `SAVED_STATE_REGISTRY_OWNER_KEY`"
97             )
98     val viewModelStateRegistryOwner =
99         this[VIEW_MODEL_STORE_OWNER_KEY]
100             ?: throw IllegalArgumentException(
101                 "CreationExtras must have a value by `VIEW_MODEL_STORE_OWNER_KEY`"
102             )
103 
104     val defaultArgs = this[DEFAULT_ARGS_KEY]
105     val key =
106         this[VIEW_MODEL_KEY]
107             ?: throw IllegalArgumentException(
108                 "CreationExtras must have a value by `VIEW_MODEL_KEY`"
109             )
110     return createSavedStateHandle(
111         savedStateRegistryOwner,
112         viewModelStateRegistryOwner,
113         key,
114         defaultArgs
115     )
116 }
117 
118 internal val ViewModelStoreOwner.savedStateHandlesVM: SavedStateHandlesVM
119     get() =
120         ViewModelProvider.create(
121                 owner = this,
122                 factory =
123                     object : ViewModelProvider.Factory {
createnull124                         override fun <T : ViewModel> create(
125                             modelClass: KClass<T>,
126                             extras: CreationExtras
127                         ): T {
128                             @Suppress("UNCHECKED_CAST") return SavedStateHandlesVM() as T
129                         }
130                     }
131             )[VIEWMODEL_KEY, SavedStateHandlesVM::class]
132 
133 internal val SavedStateRegistryOwner.savedStateHandlesProvider: SavedStateHandlesProvider
134     get() =
135         savedStateRegistry.getSavedStateProvider(SAVED_STATE_KEY) as? SavedStateHandlesProvider
136             ?: throw IllegalStateException(
137                 "enableSavedStateHandles() wasn't called " +
138                     "prior to createSavedStateHandle() call"
139             )
140 
141 internal class SavedStateHandlesVM : ViewModel() {
142     val handles = mutableMapOf<String, SavedStateHandle>()
143 }
144 
145 /**
146  * This single SavedStateProvider is responsible for saving the state of every SavedStateHandle
147  * associated with the SavedState/ViewModel pair.
148  */
149 internal class SavedStateHandlesProvider(
150     private val savedStateRegistry: SavedStateRegistry,
151     viewModelStoreOwner: ViewModelStoreOwner
152 ) : SavedStateRegistry.SavedStateProvider {
153     private var restored = false
154     private var restoredState: SavedState? = null
155 
<lambda>null156     private val viewModel by lazy { viewModelStoreOwner.savedStateHandlesVM }
157 
saveStatenull158     override fun saveState(): SavedState {
159         return savedState {
160             // Ensure that even if ViewModels aren't recreated after process death and
161             // recreation
162             // that we keep their state until they are recreated
163             restoredState?.let { putAll(it) }
164             // But if we do have ViewModels, prefer their state over what we may
165             // have restored
166             viewModel.handles.forEach { (key, handle) ->
167                 val savedState = handle.savedStateProvider().saveState()
168                 if (savedState.read { !isEmpty() }) {
169                     putSavedState(key, savedState)
170                 }
171             }
172 
173             // After we've saved the state, allow restoring a second time
174             restored = false
175         }
176     }
177 
178     /** Restore the state from the SavedStateRegistry if it hasn't already been restored. */
performRestorenull179     fun performRestore() {
180         if (!restored) {
181             val newState = savedStateRegistry.consumeRestoredStateForKey(SAVED_STATE_KEY)
182             restoredState = savedState {
183                 restoredState?.let { putAll(it) }
184                 newState?.let { putAll(it) }
185             }
186             restored = true
187             // Grab a reference to the ViewModel for later usage when we saveState()
188             // This ensures that even if saveState() is called after the Lifecycle is
189             // DESTROYED, we can still save the state
190             viewModel
191         }
192     }
193 
194     /** Restore the state associated with a particular SavedStateHandle, identified by its [key] */
consumeRestoredStateForKeynull195     fun consumeRestoredStateForKey(key: String): SavedState? {
196         performRestore()
197         val state = restoredState ?: return null
198         if (state.read { !contains(key) }) return null
199 
200         val result = state.read { getSavedStateOrNull(key) ?: savedState() }
201         state.write { remove(key) }
202         if (state.read { isEmpty() }) {
203             this.restoredState = null
204         }
205 
206         return result
207     }
208 }
209 
210 // it reconnects existent SavedStateHandles to SavedStateRegistryOwner when it is recreated
211 internal class SavedStateHandleAttacher(private val provider: SavedStateHandlesProvider) :
212     LifecycleEventObserver {
213 
onStateChangednull214     override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
215         check(event == Lifecycle.Event.ON_CREATE) { "Next event must be ON_CREATE, it was $event" }
216         source.lifecycle.removeObserver(this)
217         // onRecreated() is called after the Lifecycle reaches CREATED, so we
218         // eagerly restore the state as part of this call to ensure it consumed
219         // even if no ViewModels are actually created during this cycle of the Lifecycle
220         provider.performRestore()
221     }
222 }
223 
224 /**
225  * A key for [SavedStateRegistryOwner] that corresponds to [ViewModelStoreOwner] of a [ViewModel]
226  * that is being created.
227  */
228 @JvmField
229 public val SAVED_STATE_REGISTRY_OWNER_KEY: CreationExtras.Key<SavedStateRegistryOwner> =
230     CreationExtras.Key<SavedStateRegistryOwner>()
231 
232 /** A key for [ViewModelStoreOwner] that is an owner of a [ViewModel] that is being created. */
233 @JvmField
234 public val VIEW_MODEL_STORE_OWNER_KEY: CreationExtras.Key<ViewModelStoreOwner> =
235     CreationExtras.Key<ViewModelStoreOwner>()
236 
237 /** A key for default arguments that should be passed to [SavedStateHandle] if needed. */
238 @JvmField
239 public val DEFAULT_ARGS_KEY: CreationExtras.Key<SavedState> = CreationExtras.Key<SavedState>()
240