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