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("InitializerViewModelFactoryKt")
17 
18 package androidx.lifecycle.viewmodel
19 
20 import androidx.lifecycle.ViewModel
21 import androidx.lifecycle.ViewModelProvider
22 import androidx.lifecycle.viewmodel.internal.ViewModelProviders
23 import androidx.lifecycle.viewmodel.internal.canonicalName
24 import kotlin.jvm.JvmName
25 import kotlin.reflect.KClass
26 
27 @DslMarker public annotation class ViewModelFactoryDsl
28 
29 /** Creates an [InitializerViewModelFactory] with the initializers provided in the builder. */
viewModelFactorynull30 public inline fun viewModelFactory(
31     builder: InitializerViewModelFactoryBuilder.() -> Unit
32 ): ViewModelProvider.Factory = InitializerViewModelFactoryBuilder().apply(builder).build()
33 
34 /** DSL for constructing a new [ViewModelProvider.Factory] */
35 @ViewModelFactoryDsl
36 public class InitializerViewModelFactoryBuilder public constructor() {
37 
38     private val initializers = mutableMapOf<KClass<*>, ViewModelInitializer<*>>()
39 
40     /**
41      * Associates the specified [initializer] with the given [ViewModel] class.
42      *
43      * @param clazz [ViewModel] class with which the specified [initializer] is to be associated.
44      * @param initializer factory lambda to be associated with the specified [ViewModel] class.
45      */
46     @Suppress("SetterReturnsThis", "MissingGetterMatchingBuilder")
47     public fun <T : ViewModel> addInitializer(
48         clazz: KClass<T>,
49         initializer: CreationExtras.() -> T,
50     ) {
51         require(clazz !in initializers) {
52             "A `initializer` with the same `clazz` has already been added: ${clazz.canonicalName}."
53         }
54         initializers[clazz] = ViewModelInitializer(clazz, initializer)
55     }
56 
57     /**
58      * Returns an instance of [ViewModelProvider.Factory] created from the initializers set on this
59      * builder.
60      */
61     public fun build(): ViewModelProvider.Factory =
62         ViewModelProviders.createInitializerFactory(initializers.values)
63 }
64 
65 /** Add an initializer to the [InitializerViewModelFactoryBuilder] */
initializernull66 public inline fun <reified VM : ViewModel> InitializerViewModelFactoryBuilder.initializer(
67     noinline initializer: CreationExtras.() -> VM
68 ) {
69     addInitializer(VM::class, initializer)
70 }
71 
72 /** Holds a [ViewModel] class and initializer for that class */
73 public expect class ViewModelInitializer<T : ViewModel>
74 /**
75  * Construct a new [ViewModelInitializer] instance.
76  *
77  * @param clazz [ViewModel] class with which the specified [initializer] is to be associated.
78  * @param initializer factory lambda to be associated with the specified [ViewModel] class.
79  */
80 public constructor(
81     clazz: KClass<T>,
82     initializer: CreationExtras.() -> T,
83 ) {
84     internal val clazz: KClass<T>
85     internal val initializer: CreationExtras.() -> T
86 }
87 
88 /**
89  * A [ViewModelProvider.Factory] that allows you to add lambda initializers for handling particular
90  * ViewModel classes using [CreationExtras], while using the default behavior for any other classes.
91  *
92  * ```
93  * val factory = viewModelFactory {
94  *   initializer { TestViewModel(this[key]) }
95  * }
96  * val viewModel: TestViewModel = factory.create(TestViewModel::class.java, extras)
97  * ```
98  */
99 internal expect class InitializerViewModelFactory(
100     vararg initializers: ViewModelInitializer<*>,
101 ) : ViewModelProvider.Factory
102