1 /* <lambda>null2 * Copyright (C) 2020 The Dagger Authors. 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 dagger.hilt.android.processor.internal.viewmodel 18 19 import com.google.auto.common.GeneratedAnnotationSpecs 20 import com.squareup.javapoet.AnnotationSpec 21 import com.squareup.javapoet.ClassName 22 import com.squareup.javapoet.JavaFile 23 import com.squareup.javapoet.MethodSpec 24 import com.squareup.javapoet.TypeSpec 25 import dagger.hilt.android.processor.internal.AndroidClassNames 26 import dagger.hilt.processor.internal.ClassNames 27 import javax.annotation.processing.ProcessingEnvironment 28 import javax.lang.model.SourceVersion 29 import javax.lang.model.element.Modifier 30 import javax.lang.model.util.Elements 31 32 /** 33 * Source generator to support Hilt injection of ViewModels. 34 * 35 * Should generate: 36 * ``` 37 * public final class $_HiltModules { 38 * @Module 39 * @InstallIn(ViewModelComponent.class) 40 * public static abstract class BindsModule { 41 * @Binds 42 * @IntoMap 43 * @StringKey("pkg.$") 44 * @HiltViewModelMap 45 * public abstract ViewModel bind($ vm) 46 * } 47 * @Module 48 * @InstallIn(ActivityRetainedComponent.class) 49 * public static final class KeyModule { 50 * @Provides 51 * @IntoSet 52 * @HiltViewModelMap.KeySet 53 * public static String provide() { 54 * return "pkg.$"; 55 * } 56 * } 57 * } 58 * ``` 59 */ 60 internal class ViewModelModuleGenerator( 61 private val processingEnv: ProcessingEnvironment, 62 private val injectedViewModel: ViewModelMetadata 63 ) { 64 fun generate() { 65 val modulesTypeSpec = TypeSpec.classBuilder(injectedViewModel.modulesClassName) 66 .addOriginatingElement(injectedViewModel.typeElement) 67 .addGeneratedAnnotation(processingEnv.elementUtils, processingEnv.sourceVersion) 68 .addAnnotation( 69 AnnotationSpec.builder(ClassNames.ORIGINATING_ELEMENT) 70 .addMember( 71 "topLevelClass", 72 "$T.class", 73 injectedViewModel.className.topLevelClassName() 74 ) 75 .build() 76 ) 77 .addModifiers(Modifier.PUBLIC, Modifier.FINAL) 78 .addType(getBindsModuleTypeSpec()) 79 .addType(getKeyModuleTypeSpec()) 80 .addMethod( 81 MethodSpec.constructorBuilder() 82 .addModifiers(Modifier.PRIVATE) 83 .build() 84 ) 85 .build() 86 JavaFile.builder(injectedViewModel.modulesClassName.packageName(), modulesTypeSpec) 87 .build() 88 .writeTo(processingEnv.filer) 89 } 90 91 private fun getBindsModuleTypeSpec() = createModuleTypeSpec( 92 className = "BindsModule", 93 component = AndroidClassNames.VIEW_MODEL_COMPONENT 94 ) 95 .addModifiers(Modifier.ABSTRACT) 96 .addMethod( 97 MethodSpec.constructorBuilder() 98 .addModifiers(Modifier.PRIVATE) 99 .build() 100 ) 101 .addMethod(getViewModelBindsMethod()) 102 .build() 103 104 private fun getViewModelBindsMethod() = 105 MethodSpec.methodBuilder("binds") 106 .addAnnotation(ClassNames.BINDS) 107 .addAnnotation(ClassNames.INTO_MAP) 108 .addAnnotation( 109 AnnotationSpec.builder(ClassNames.STRING_KEY) 110 .addMember("value", S, injectedViewModel.className.reflectionName()) 111 .build() 112 ) 113 .addAnnotation(AndroidClassNames.HILT_VIEW_MODEL_MAP_QUALIFIER) 114 .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) 115 .returns(AndroidClassNames.VIEW_MODEL) 116 .addParameter(injectedViewModel.className, "vm") 117 .build() 118 119 private fun getKeyModuleTypeSpec() = createModuleTypeSpec( 120 className = "KeyModule", 121 component = AndroidClassNames.ACTIVITY_RETAINED_COMPONENT 122 ) 123 .addModifiers(Modifier.FINAL) 124 .addMethod( 125 MethodSpec.constructorBuilder() 126 .addModifiers(Modifier.PRIVATE) 127 .build() 128 ) 129 .addMethod(getViewModelKeyProvidesMethod()) 130 .build() 131 132 private fun getViewModelKeyProvidesMethod() = 133 MethodSpec.methodBuilder("provide") 134 .addAnnotation(ClassNames.PROVIDES) 135 .addAnnotation(ClassNames.INTO_SET) 136 .addAnnotation(AndroidClassNames.HILT_VIEW_MODEL_KEYS_QUALIFIER) 137 .addModifiers(Modifier.PUBLIC, Modifier.STATIC) 138 .returns(String::class.java) 139 .addStatement("return $S", injectedViewModel.className.reflectionName()) 140 .build() 141 142 private fun createModuleTypeSpec(className: String, component: ClassName) = 143 TypeSpec.classBuilder(className) 144 .addOriginatingElement(injectedViewModel.typeElement) 145 .addAnnotation(ClassNames.MODULE) 146 .addAnnotation( 147 AnnotationSpec.builder(ClassNames.INSTALL_IN) 148 .addMember("value", "$T.class", component) 149 .build() 150 ) 151 .addModifiers(Modifier.PUBLIC, Modifier.STATIC) 152 153 companion object { 154 155 const val L = "\$L" 156 const val T = "\$T" 157 const val N = "\$N" 158 const val S = "\$S" 159 const val W = "\$W" 160 161 private fun TypeSpec.Builder.addGeneratedAnnotation( 162 elements: Elements, 163 sourceVersion: SourceVersion 164 ) = apply { 165 GeneratedAnnotationSpecs.generatedAnnotationSpec( 166 elements, 167 sourceVersion, 168 ViewModelProcessor::class.java 169 ).ifPresent { generatedAnnotation -> 170 addAnnotation(generatedAnnotation) 171 } 172 } 173 } 174 } 175