1 /* 2 * 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.customtestapplication; 18 19 import com.squareup.javapoet.FieldSpec; 20 import com.squareup.javapoet.JavaFile; 21 import com.squareup.javapoet.MethodSpec; 22 import com.squareup.javapoet.ParameterSpec; 23 import com.squareup.javapoet.ParameterizedTypeName; 24 import com.squareup.javapoet.TypeName; 25 import com.squareup.javapoet.TypeSpec; 26 import dagger.hilt.processor.internal.ClassNames; 27 import dagger.hilt.processor.internal.Processors; 28 import java.io.IOException; 29 import javax.annotation.processing.ProcessingEnvironment; 30 import javax.lang.model.element.Modifier; 31 32 /** 33 * Generates an Android Application that holds the Singleton component. 34 */ 35 final class CustomTestApplicationGenerator { 36 private static final ParameterSpec COMPONENT_MANAGER = 37 ParameterSpec.builder(ClassNames.TEST_APPLICATION_COMPONENT_MANAGER, "componentManager") 38 .build(); 39 40 private final ProcessingEnvironment processingEnv; 41 private final CustomTestApplicationMetadata metadata; 42 CustomTestApplicationGenerator( ProcessingEnvironment processingEnv, CustomTestApplicationMetadata metadata)43 public CustomTestApplicationGenerator( 44 ProcessingEnvironment processingEnv, CustomTestApplicationMetadata metadata) { 45 this.processingEnv = processingEnv; 46 this.metadata = metadata; 47 } 48 generate()49 public void generate() throws IOException { 50 TypeSpec.Builder generator = 51 TypeSpec.classBuilder(metadata.appName()) 52 .addOriginatingElement(metadata.element()) 53 .addModifiers(Modifier.PUBLIC, Modifier.FINAL) 54 .superclass(metadata.baseAppName()) 55 .addSuperinterface( 56 ParameterizedTypeName.get(ClassNames.GENERATED_COMPONENT_MANAGER, TypeName.OBJECT)) 57 .addSuperinterface(ClassNames.TEST_APPLICATION_COMPONENT_MANAGER_HOLDER) 58 .addField(getComponentManagerField()) 59 .addMethod(getAttachBaseContextMethod()) 60 .addMethod(getComponentManagerMethod()) 61 .addMethod(getComponentMethod()); 62 63 Processors.addGeneratedAnnotation( 64 generator, processingEnv, CustomTestApplicationProcessor.class); 65 66 JavaFile.builder(metadata.appName().packageName(), generator.build()) 67 .build() 68 .writeTo(processingEnv.getFiler()); 69 } 70 71 // Initialize this in attachBaseContext to not pull it into the main dex. 72 /** private TestApplicationComponentManager componentManager; */ getComponentManagerField()73 private static FieldSpec getComponentManagerField() { 74 return FieldSpec.builder(COMPONENT_MANAGER.type, COMPONENT_MANAGER.name, Modifier.PRIVATE) 75 .build(); 76 } 77 78 /** 79 * Initializes application fields. These fields are initialized in attachBaseContext to avoid 80 * potential multidexing issues. 81 * 82 * <pre><code> 83 * {@literal @Override} protected void attachBaseContext(Context base) { 84 * super.attachBaseContext(base); 85 * componentManager = new TestApplicationComponentManager(this); 86 * } 87 * </code></pre> 88 */ getAttachBaseContextMethod()89 private static MethodSpec getAttachBaseContextMethod() { 90 return MethodSpec.methodBuilder("attachBaseContext") 91 .addAnnotation(Override.class) 92 .addModifiers(Modifier.PROTECTED, Modifier.FINAL) 93 .addParameter(ClassNames.CONTEXT, "base") 94 .addStatement("super.attachBaseContext(base)") 95 .addStatement("$N = new $T(this)", COMPONENT_MANAGER, COMPONENT_MANAGER.type) 96 .build(); 97 } 98 getComponentMethod()99 private static MethodSpec getComponentMethod() { 100 return MethodSpec.methodBuilder("generatedComponent") 101 .addAnnotation(Override.class) 102 .addModifiers(Modifier.PUBLIC, Modifier.FINAL) 103 .returns(TypeName.OBJECT) 104 .addStatement("return $N.generatedComponent()", COMPONENT_MANAGER) 105 .build(); 106 } 107 getComponentManagerMethod()108 private static MethodSpec getComponentManagerMethod() { 109 return MethodSpec.methodBuilder("componentManager") 110 .addAnnotation(Override.class) 111 .addModifiers(Modifier.PUBLIC, Modifier.FINAL) 112 .returns(TypeName.OBJECT) 113 .addStatement("return $N", COMPONENT_MANAGER) 114 .build(); 115 } 116 } 117