• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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.androidentrypoint;
18 
19 import static dagger.internal.codegen.langmodel.DaggerElements.getMethodDescriptor;
20 
21 import com.google.common.collect.Iterables;
22 import com.squareup.javapoet.ClassName;
23 import com.squareup.javapoet.FieldSpec;
24 import com.squareup.javapoet.JavaFile;
25 import com.squareup.javapoet.MethodSpec;
26 import com.squareup.javapoet.ParameterSpec;
27 import com.squareup.javapoet.TypeName;
28 import com.squareup.javapoet.TypeSpec;
29 import com.squareup.javapoet.TypeVariableName;
30 import dagger.hilt.android.processor.internal.AndroidClassNames;
31 import dagger.hilt.android.processor.internal.MoreTypes;
32 import dagger.hilt.processor.internal.Processors;
33 import java.io.IOException;
34 import javax.annotation.processing.ProcessingEnvironment;
35 import javax.lang.model.element.ExecutableElement;
36 import javax.lang.model.element.Modifier;
37 import javax.lang.model.element.TypeElement;
38 import javax.lang.model.type.TypeKind;
39 import javax.lang.model.util.ElementFilter;
40 
41 /** Generates an Hilt BroadcastReceiver class for the @AndroidEntryPoint annotated class. */
42 public final class BroadcastReceiverGenerator {
43 
44   private static final String ON_RECEIVE_DESCRIPTOR =
45       "onReceive(Landroid/content/Context;Landroid/content/Intent;)V";
46 
47   private final ProcessingEnvironment env;
48   private final AndroidEntryPointMetadata metadata;
49   private final ClassName generatedClassName;
50 
BroadcastReceiverGenerator( ProcessingEnvironment env, AndroidEntryPointMetadata metadata)51   public BroadcastReceiverGenerator(
52       ProcessingEnvironment env, AndroidEntryPointMetadata metadata) {
53     this.env = env;
54     this.metadata = metadata;
55 
56     generatedClassName = metadata.generatedClassName();
57   }
58 
59   // @Generated("BroadcastReceiverGenerator")
60   // abstract class Hilt_$CLASS extends $BASE {
61   //   ...
62   // }
generate()63   public void generate() throws IOException {
64     TypeSpec.Builder builder =
65         TypeSpec.classBuilder(generatedClassName.simpleName())
66             .addOriginatingElement(metadata.element())
67             .superclass(metadata.baseClassName())
68             .addModifiers(metadata.generatedClassModifiers())
69             .addMethod(onReceiveMethod());
70 
71     Generators.addGeneratedBaseClassJavadoc(builder, AndroidClassNames.ANDROID_ENTRY_POINT);
72     Processors.addGeneratedAnnotation(builder, env, getClass());
73     Generators.copyConstructors(metadata.baseElement(), builder);
74 
75     metadata.baseElement().getTypeParameters().stream()
76         .map(TypeVariableName::get)
77         .forEachOrdered(builder::addTypeVariable);
78 
79     Generators.addInjectionMethods(metadata, builder);
80     Generators.copyLintAnnotations(metadata.element(), builder);
81     Generators.copySuppressAnnotations(metadata.element(), builder);
82 
83     // Add an unused field used as a marker to let the bytecode injector know this receiver will
84     // need to be injected with a super.onReceive call. This is only necessary if no concrete
85     // onReceive call is implemented in any of the super classes.
86     if (metadata.requiresBytecodeInjection() && !isOnReceiveImplemented(metadata.baseElement())) {
87       builder.addField(
88           FieldSpec.builder(
89                   TypeName.BOOLEAN,
90                   "onReceiveBytecodeInjectionMarker",
91                   Modifier.PRIVATE,
92                   Modifier.FINAL)
93               .initializer("false")
94               .build());
95     }
96 
97     JavaFile.builder(generatedClassName.packageName(),
98         builder.build()).build().writeTo(env.getFiler());
99   }
100 
isOnReceiveImplemented(TypeElement typeElement)101   private static boolean isOnReceiveImplemented(TypeElement typeElement) {
102     boolean isImplemented =
103         ElementFilter.methodsIn(typeElement.getEnclosedElements()).stream()
104             .anyMatch(
105                 methodElement ->
106                     getMethodDescriptor(methodElement).equals(ON_RECEIVE_DESCRIPTOR)
107                         && !methodElement.getModifiers().contains(Modifier.ABSTRACT));
108     if (isImplemented) {
109       return true;
110     } else if (typeElement.getSuperclass().getKind() != TypeKind.NONE) {
111       return isOnReceiveImplemented(MoreTypes.asTypeElement(typeElement.getSuperclass()));
112     } else {
113       return false;
114     }
115   }
116 
117   // @Override
118   // public void onReceive(Context context, Intent intent) {
119   //   inject(context);
120   //   super.onReceive();
121   // }
onReceiveMethod()122   private MethodSpec onReceiveMethod() throws IOException {
123     MethodSpec.Builder method =
124         MethodSpec.methodBuilder("onReceive")
125             .addAnnotation(Override.class)
126             .addAnnotation(AndroidClassNames.CALL_SUPER)
127             .addModifiers(Modifier.PUBLIC)
128             .addParameter(ParameterSpec.builder(AndroidClassNames.CONTEXT, "context").build())
129             .addParameter(ParameterSpec.builder(AndroidClassNames.INTENT, "intent").build())
130             .addStatement("inject(context)");
131 
132     if (metadata.overridesAndroidEntryPointClass()) {
133       // We directly call super.onReceive here because we know Hilt base classes have a
134       // non-abstract onReceive method. However, because the Hilt base class may not be generated
135       // already we cannot fall down to the below logic to find it.
136       method.addStatement("super.onReceive(context, intent)");
137     } else {
138       // Get the onReceive method element from BroadcastReceiver.
139       ExecutableElement onReceiveElement =
140           Iterables.getOnlyElement(
141               MoreTypes.findMethods(
142                   env.getElementUtils()
143                       .getTypeElement(AndroidClassNames.BROADCAST_RECEIVER.toString()),
144                   "onReceive"));
145 
146       // If the base class or one of its super classes implements onReceive, call super.onReceive()
147       MoreTypes.findInheritedMethod(env.getTypeUtils(), metadata.baseElement(), onReceiveElement)
148           .filter(onReceive -> !onReceive.getModifiers().contains(Modifier.ABSTRACT))
149           .ifPresent(onReceive -> method.addStatement("super.onReceive(context, intent)"));
150     }
151 
152     return method.build();
153   }
154 }
155