1 /*
2  * Copyright 2024 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 @file:JvmName("ViewModelProvider")
17 
18 package androidx.lifecycle
19 
20 import android.app.Application
21 import androidx.annotation.MainThread
22 import androidx.annotation.RestrictTo
23 import androidx.lifecycle.viewmodel.CreationExtras
24 import androidx.lifecycle.viewmodel.CreationExtras.Key
25 import androidx.lifecycle.viewmodel.InitializerViewModelFactory
26 import androidx.lifecycle.viewmodel.ViewModelInitializer
27 import androidx.lifecycle.viewmodel.ViewModelProviderImpl
28 import androidx.lifecycle.viewmodel.internal.JvmViewModelProviders
29 import androidx.lifecycle.viewmodel.internal.ViewModelProviders
30 import java.lang.reflect.InvocationTargetException
31 import kotlin.reflect.KClass
32 
33 public actual open class ViewModelProvider
34 private constructor(
35     private val impl: ViewModelProviderImpl,
36 ) {
37 
38     /**
39      * Creates a [ViewModelProvider]. This provider generates [ViewModel] instances using the
40      * specified [Factory] and stores them within the [ViewModelStore] of the provided
41      * [ViewModelStoreOwner].
42      *
43      * @param store `ViewModelStore` where ViewModels will be stored.
44      * @param factory The [Factory] responsible for creating new [ViewModel] instances.
45      * @param defaultCreationExtras Additional data to be passed to the [Factory] during [ViewModel]
46      *   creation.
47      */
48     @JvmOverloads
49     public constructor(
50         store: ViewModelStore,
51         factory: Factory,
52         defaultCreationExtras: CreationExtras = CreationExtras.Empty,
53     ) : this(ViewModelProviderImpl(store, factory, defaultCreationExtras))
54 
55     /**
56      * Creates [ViewModelProvider]. This will create [ViewModel] instances and retain them in the
57      * [ViewModelStore] of the given [ViewModelStoreOwner].
58      *
59      * This method will use the
60      * [default factory][HasDefaultViewModelProviderFactory.defaultViewModelProviderFactory] if the
61      * owner implements [HasDefaultViewModelProviderFactory]. Otherwise, a [NewInstanceFactory] will
62      * be used.
63      */
64     public constructor(
65         owner: ViewModelStoreOwner,
66     ) : this(
67         store = owner.viewModelStore,
68         factory = ViewModelProviders.getDefaultFactory(owner),
69         defaultCreationExtras = ViewModelProviders.getDefaultCreationExtras(owner)
70     )
71 
72     /**
73      * Creates a [ViewModelProvider]. This provider generates [ViewModel] instances using the
74      * specified [Factory] and stores them within the [ViewModelStore] of the provided
75      * [ViewModelStoreOwner].
76      *
77      * @param owner The [ViewModelStoreOwner] that will manage the lifecycle of the created
78      *   [ViewModel] instances.
79      * @param factory The [Factory] responsible for creating new [ViewModel] instances.
80      */
81     public constructor(
82         owner: ViewModelStoreOwner,
83         factory: Factory,
84     ) : this(
85         store = owner.viewModelStore,
86         factory = factory,
87         defaultCreationExtras = ViewModelProviders.getDefaultCreationExtras(owner)
88     )
89 
90     @MainThread
getnull91     public actual operator fun <T : ViewModel> get(modelClass: KClass<T>): T =
92         impl.getViewModel(modelClass)
93 
94     /**
95      * Returns an existing ViewModel or creates a new one in the scope (usually, a fragment or an
96      * activity), associated with this `ViewModelProvider`.
97      *
98      * The created ViewModel is associated with the given scope and will be retained as long as the
99      * scope is alive (e.g. if it is an activity, until it is finished or process is killed).
100      *
101      * @param modelClass The class of the ViewModel to create an instance of it if it is not
102      *   present.
103      * @return A ViewModel that is an instance of the given type `T`.
104      * @throws IllegalArgumentException if the given [modelClass] is local or anonymous class.
105      */
106     public open operator fun <T : ViewModel> get(modelClass: Class<T>): T = get(modelClass.kotlin)
107 
108     @MainThread
109     public actual operator fun <T : ViewModel> get(key: String, modelClass: KClass<T>): T =
110         impl.getViewModel(modelClass, key)
111 
112     /**
113      * Returns an existing ViewModel or creates a new one in the scope (usually, a fragment or an
114      * activity), associated with this `ViewModelProvider`.
115      *
116      * The created ViewModel is associated with the given scope and will be retained as long as the
117      * scope is alive (e.g. if it is an activity, until it is finished or process is killed).
118      *
119      * @param key The key to use to identify the ViewModel.
120      * @param modelClass The class of the ViewModel to create an instance of it if it is not
121      *   present.
122      * @return A ViewModel that is an instance of the given type `T`.
123      */
124     public open operator fun <T : ViewModel> get(key: String, modelClass: Class<T>): T =
125         impl.getViewModel(modelClass.kotlin, key)
126 
127     public actual interface Factory {
128 
129         /**
130          * Creates a new instance of the given `Class`.
131          *
132          * Default implementation throws [UnsupportedOperationException]. ˆ
133          *
134          * @param modelClass a `Class` whose instance is requested
135          * @return a newly created ViewModel
136          */
137         public fun <T : ViewModel> create(modelClass: Class<T>): T =
138             ViewModelProviders.unsupportedCreateViewModel()
139 
140         /**
141          * Creates a new instance of the given `Class`.
142          *
143          * @param modelClass a `Class` whose instance is requested
144          * @param extras an additional information for this creation request
145          * @return a newly created ViewModel
146          */
147         public fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T =
148             create(modelClass)
149 
150         public actual fun <T : ViewModel> create(
151             modelClass: KClass<T>,
152             extras: CreationExtras,
153         ): T = create(modelClass.java, extras)
154 
155         public companion object {
156             /**
157              * Creates an [InitializerViewModelFactory] using the given initializers.
158              *
159              * @param initializers the class initializer pairs used for the factory to create simple
160              *   view models
161              * @see [InitializerViewModelFactory]
162              */
163             @JvmStatic
164             public fun from(vararg initializers: ViewModelInitializer<*>): Factory =
165                 ViewModelProviders.createInitializerFactory(*initializers)
166         }
167     }
168 
169     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
170     public actual open class OnRequeryFactory {
onRequerynull171         public actual open fun onRequery(viewModel: ViewModel) {}
172     }
173 
174     /** Simple factory, which calls empty constructor on the give class. */
175     public open class NewInstanceFactory
176     /**
177      * Construct a new [NewInstanceFactory] instance.
178      *
179      * Use [NewInstanceFactory.instance] to get a default instance of [NewInstanceFactory].
180      */
181     @Suppress("SingletonConstructor")
182     constructor() : Factory {
183 
createnull184         public override fun <T : ViewModel> create(modelClass: Class<T>): T =
185             JvmViewModelProviders.createViewModel(modelClass)
186 
187         public override fun <T : ViewModel> create(
188             modelClass: Class<T>,
189             extras: CreationExtras,
190         ): T = create(modelClass)
191 
192         public override fun <T : ViewModel> create(
193             modelClass: KClass<T>,
194             extras: CreationExtras,
195         ): T = create(modelClass.java, extras)
196 
197         public companion object {
198             private var _instance: NewInstanceFactory? = null
199 
200             /**
201              * Retrieve a singleton instance of NewInstanceFactory.
202              *
203              * @return A valid [NewInstanceFactory]
204              */
205             @JvmStatic
206             public val instance: NewInstanceFactory
207                 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
208                 get() {
209                     if (_instance == null) {
210                         _instance = NewInstanceFactory()
211                     }
212                     return _instance!!
213                 }
214 
215             /**
216              * A [CreationExtras.Key] used to retrieve the key associated with a requested
217              * [ViewModel].
218              *
219              * The [ViewModelProvider] automatically includes the key in the [CreationExtras] passed
220              * to [ViewModelProvider.Factory]. This applies to keys generated by either of these
221              * usage patterns:
222              * - `ViewModelProvider.get(key, MyViewModel::class)`: provided `key` is used.
223              * - `ViewModelProvider.get(MyViewModel::class)`: generates a `key` from given `class`.
224              *
225              * @see ViewModelProvider.VIEW_MODEL_KEY
226              */
227             @JvmField public val VIEW_MODEL_KEY: Key<String> = ViewModelProvider.VIEW_MODEL_KEY
228         }
229     }
230 
231     /**
232      * [Factory] which may create [AndroidViewModel] and [ViewModel], which have an empty
233      * constructor.
234      *
235      * @param application an application to pass in [AndroidViewModel]
236      */
237     public open class AndroidViewModelFactory
238     private constructor(
239         private val application: Application?,
240         // parameter to avoid clash between constructors with nullable and non-nullable
241         // Application
242         @Suppress("UNUSED_PARAMETER") unused: Int,
243     ) : NewInstanceFactory() {
244 
245         /**
246          * Constructs this factory. When a factory is constructed this way, a component for which
247          * [ViewModel] is created must provide an [Application] by [APPLICATION_KEY] in
248          * [CreationExtras], otherwise [IllegalArgumentException] will be thrown from [create]
249          * method.
250          */
251         @Suppress("SingletonConstructor")
252         public constructor() : this(application = null, unused = 0)
253 
254         /**
255          * Constructs this factory.
256          *
257          * @param application an application to pass in [AndroidViewModel]
258          */
259         @Suppress("SingletonConstructor")
260         public constructor(application: Application) : this(application, unused = 0)
261 
262         @Suppress("DocumentExceptions")
createnull263         override fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T {
264             return if (application != null) {
265                 create(modelClass)
266             } else {
267                 val application = extras[APPLICATION_KEY]
268                 if (application != null) {
269                     create(modelClass, application)
270                 } else {
271                     // For AndroidViewModels, CreationExtras must have an application set
272                     if (AndroidViewModel::class.java.isAssignableFrom(modelClass)) {
273                         throw IllegalArgumentException(
274                             "CreationExtras must have an application by `APPLICATION_KEY`"
275                         )
276                     }
277                     super.create(modelClass)
278                 }
279             }
280         }
281 
282         @Suppress("DocumentExceptions")
createnull283         override fun <T : ViewModel> create(modelClass: Class<T>): T {
284             return if (application == null) {
285                 throw UnsupportedOperationException(
286                     "AndroidViewModelFactory constructed " +
287                         "with empty constructor works only with " +
288                         "create(modelClass: Class<T>, extras: CreationExtras)."
289                 )
290             } else {
291                 create(modelClass, application)
292             }
293         }
294 
295         @Suppress("DocumentExceptions")
createnull296         private fun <T : ViewModel> create(modelClass: Class<T>, app: Application): T {
297             return if (AndroidViewModel::class.java.isAssignableFrom(modelClass)) {
298                 try {
299                     modelClass.getConstructor(Application::class.java).newInstance(app)
300                 } catch (e: NoSuchMethodException) {
301                     throw RuntimeException("Cannot create an instance of $modelClass", e)
302                 } catch (e: IllegalAccessException) {
303                     throw RuntimeException("Cannot create an instance of $modelClass", e)
304                 } catch (e: InstantiationException) {
305                     throw RuntimeException("Cannot create an instance of $modelClass", e)
306                 } catch (e: InvocationTargetException) {
307                     throw RuntimeException("Cannot create an instance of $modelClass", e)
308                 }
309             } else super.create(modelClass)
310         }
311 
312         public companion object {
313             private var _instance: AndroidViewModelFactory? = null
314 
315             /**
316              * Retrieve a singleton instance of AndroidViewModelFactory.
317              *
318              * @param application an application to pass in [AndroidViewModel]
319              * @return A valid [AndroidViewModelFactory]
320              */
321             @JvmStatic
getInstancenull322             public fun getInstance(application: Application): AndroidViewModelFactory {
323                 if (_instance == null) {
324                     _instance = AndroidViewModelFactory(application)
325                 }
326                 return _instance!!
327             }
328 
329             /**
330              * A [CreationExtras.Key] to query an application in which ViewModel is being created.
331              */
332             @JvmField public val APPLICATION_KEY: Key<Application> = CreationExtras.Companion.Key()
333         }
334     }
335 
336     public actual companion object {
337         @JvmStatic
338         @Suppress("MissingJvmstatic")
createnull339         public actual fun create(
340             owner: ViewModelStoreOwner,
341             factory: Factory,
342             extras: CreationExtras,
343         ): ViewModelProvider = ViewModelProvider(owner.viewModelStore, factory, extras)
344 
345         @JvmStatic
346         @Suppress("MissingJvmstatic")
347         public actual fun create(
348             store: ViewModelStore,
349             factory: Factory,
350             extras: CreationExtras
351         ): ViewModelProvider = ViewModelProvider(store, factory, extras)
352 
353         @JvmField public actual val VIEW_MODEL_KEY: Key<String> = CreationExtras.Companion.Key()
354     }
355 }
356