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 
17 package androidx.lifecycle.viewmodel.internal
18 
19 import androidx.lifecycle.HasDefaultViewModelProviderFactory
20 import androidx.lifecycle.ViewModel
21 import androidx.lifecycle.ViewModelProvider
22 import androidx.lifecycle.ViewModelStoreOwner
23 import androidx.lifecycle.viewmodel.CreationExtras
24 import androidx.lifecycle.viewmodel.InitializerViewModelFactory
25 import androidx.lifecycle.viewmodel.ViewModelInitializer
26 import kotlin.reflect.KClass
27 
28 /**
29  * [ViewModelProviders] provides common helper functionalities.
30  *
31  * Kotlin Multiplatform does not support expect class with default implementation yet, so we
32  * extracted the common logic used by all platforms to this internal class.
33  *
34  * @see <a href="https://youtrack.jetbrains.com/issue/KT-20427">KT-20427</a>
35  */
36 internal object ViewModelProviders {
37 
38     private const val VIEW_MODEL_PROVIDER_DEFAULT_KEY: String =
39         "androidx.lifecycle.ViewModelProvider.DefaultKey"
40 
getDefaultKeynull41     internal fun <T : ViewModel> getDefaultKey(modelClass: KClass<T>): String {
42         val canonicalName =
43             requireNotNull(modelClass.canonicalName) {
44                 "Local and anonymous classes can not be ViewModels"
45             }
46         return "$VIEW_MODEL_PROVIDER_DEFAULT_KEY:$canonicalName"
47     }
48 
unsupportedCreateViewModelnull49     internal fun <VM : ViewModel> unsupportedCreateViewModel(): VM =
50         throw UnsupportedOperationException(
51             "`Factory.create(String, CreationExtras)` is not implemented. You may need to " +
52                 "override the method and provide a custom implementation. Note that using " +
53                 "`Factory.create(String)` is not supported and considered an error."
54         )
55 
56     internal fun createInitializerFactory(
57         initializers: Collection<ViewModelInitializer<*>>,
58     ): ViewModelProvider.Factory = InitializerViewModelFactory(*initializers.toTypedArray())
59 
60     internal fun createInitializerFactory(
61         vararg initializers: ViewModelInitializer<*>,
62     ): ViewModelProvider.Factory = InitializerViewModelFactory(*initializers)
63 
64     internal fun getDefaultFactory(owner: ViewModelStoreOwner): ViewModelProvider.Factory =
65         if (owner is HasDefaultViewModelProviderFactory) {
66             owner.defaultViewModelProviderFactory
67         } else {
68             DefaultViewModelProviderFactory
69         }
70 
getDefaultCreationExtrasnull71     internal fun getDefaultCreationExtras(owner: ViewModelStoreOwner): CreationExtras =
72         if (owner is HasDefaultViewModelProviderFactory) {
73             owner.defaultViewModelCreationExtras
74         } else {
75             CreationExtras.Empty
76         }
77 
createViewModelFromInitializersnull78     internal fun <VM : ViewModel> createViewModelFromInitializers(
79         modelClass: KClass<VM>,
80         extras: CreationExtras,
81         vararg initializers: ViewModelInitializer<*>,
82     ): VM {
83         @Suppress("UNCHECKED_CAST")
84         val viewModel =
85             initializers.firstOrNull { it.clazz == modelClass }?.initializer?.invoke(extras) as VM?
86         return requireNotNull(viewModel) {
87             "No initializer set for given class ${modelClass.canonicalName}"
88         }
89     }
90 }
91 
92 /**
93  * Multiplatform replacement for [KClass.qualifiedName] reflection API. It's required because it's
94  * not supported for all platforms.
95  */
96 internal expect val <T : Any> KClass<T>.canonicalName: String?
97