diff --git a/java/dagger/hilt/EntryPoint.java b/java/dagger/hilt/EntryPoint.java index e216e77c0eb..23e3e1c88cf 100644 --- a/java/dagger/hilt/EntryPoint.java +++ b/java/dagger/hilt/EntryPoint.java @@ -17,6 +17,7 @@ package dagger.hilt; import static java.lang.annotation.RetentionPolicy.CLASS; +import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -44,7 +45,7 @@ * * @see Entry points */ -@Retention(CLASS) +@Retention(RUNTIME) @Target(ElementType.TYPE) @GeneratesRootInput public @interface EntryPoint {} diff --git a/java/dagger/hilt/android/internal/managers/BUILD b/java/dagger/hilt/android/internal/managers/BUILD index 950b51129ee..7b5a8838a81 100644 --- a/java/dagger/hilt/android/internal/managers/BUILD +++ b/java/dagger/hilt/android/internal/managers/BUILD @@ -33,6 +33,7 @@ android_library( "SavedStateHandleModule.java", "ServiceComponentManager.java", "ViewComponentManager.java", + "TestInjectInterceptor.java", ], exports = [":saved_state_handle_holder"], deps = [ diff --git a/java/dagger/hilt/android/internal/managers/TestInjectInterceptor.java b/java/dagger/hilt/android/internal/managers/TestInjectInterceptor.java new file mode 100644 index 00000000000..9bbed73e837 --- /dev/null +++ b/java/dagger/hilt/android/internal/managers/TestInjectInterceptor.java @@ -0,0 +1,23 @@ +package dagger.hilt.android.internal.managers; + +/** + * This class does nothing in production or in tests when running under Hilt testing framework. However, the calls + * to TestInjectInterceptor.injectForTesting() are done in a few strategic places just before Hilt does the injection + * into Android Components. + * + * As a result this class enables non-Hilt based frameworks to take over the injection process. + */ +public class TestInjectInterceptor { + /** + * This method always returns false by default. However, if this method is intercepted during testing + * by frameworks like Robolectric, the intercepting code can take over the injection process and + * instruct Hilt to skip doing anything extra for this instance. + * + * Return false if no custom injection was done and Hilt should continue as normal. Return true + * if the testing framework has takes over the injection process and Hilt should skip any extra + * work. + */ + public static boolean injectForTesting(Object injectTo) { + return false; + } +} \ No newline at end of file diff --git a/java/dagger/hilt/android/processor/internal/androidentrypoint/Generators.java b/java/dagger/hilt/android/processor/internal/androidentrypoint/Generators.java index dc2339b6991..a0970977901 100644 --- a/java/dagger/hilt/android/processor/internal/androidentrypoint/Generators.java +++ b/java/dagger/hilt/android/processor/internal/androidentrypoint/Generators.java @@ -379,6 +379,9 @@ && ancestorExtendsGeneratedHiltClass(metadata)) { methodSpecBuilder .beginControlFlow("if (!injected)") .addStatement("injected = true") + .beginControlFlow("if ($T.injectForTesting(this))", ClassNames.TEST_INJECT_INTERCEPTOR) + .addStatement("return") + .endControlFlow() .addStatement( "(($T) $L).$L($L)", metadata.injectorClassName(), @@ -395,6 +398,10 @@ && ancestorExtendsGeneratedHiltClass(metadata)) { .beginControlFlow("if (!injected)") .beginControlFlow("synchronized (injectedLock)") .beginControlFlow("if (!injected)") + .beginControlFlow("if ($T.injectForTesting(this))", ClassNames.TEST_INJECT_INTERCEPTOR) + .addStatement("injected = true") + .addStatement("return") + .endControlFlow() .addStatement( "(($T) $T.generatedComponent(context)).$L($L)", metadata.injectorClassName(), diff --git a/java/dagger/hilt/android/proguard-rules.pro b/java/dagger/hilt/android/proguard-rules.pro index 6fd3a82ead2..799ac372f1d 100644 --- a/java/dagger/hilt/android/proguard-rules.pro +++ b/java/dagger/hilt/android/proguard-rules.pro @@ -1,3 +1,4 @@ # Keep for the reflective cast done in EntryPoints. # See b/183070411#comment4 for more info. --keep,allowobfuscation,allowshrinking @dagger.hilt.android.EarlyEntryPoint class * \ No newline at end of file +-keep,allowobfuscation,allowshrinking @dagger.hilt.android.EarlyEntryPoint class * +-assumenosideeffects class dagger.hilt.android.internal.managers.TestInjectInterceptor { *; } \ No newline at end of file diff --git a/java/dagger/hilt/android/qualifiers/ActivityContext.java b/java/dagger/hilt/android/qualifiers/ActivityContext.java index cfcc40efac4..ceb1841a4b5 100644 --- a/java/dagger/hilt/android/qualifiers/ActivityContext.java +++ b/java/dagger/hilt/android/qualifiers/ActivityContext.java @@ -24,6 +24,6 @@ /** Annotation for a {@code Context} that corresponds to the activity. */ @Qualifier -@Retention(RetentionPolicy.CLASS) +@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD}) public @interface ActivityContext {} diff --git a/java/dagger/hilt/android/qualifiers/ApplicationContext.java b/java/dagger/hilt/android/qualifiers/ApplicationContext.java index 226ef758514..769e36aa000 100644 --- a/java/dagger/hilt/android/qualifiers/ApplicationContext.java +++ b/java/dagger/hilt/android/qualifiers/ApplicationContext.java @@ -17,10 +17,13 @@ package dagger.hilt.android.qualifiers; import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import javax.inject.Qualifier; /** Annotation for an Application Context dependency. */ @Qualifier +@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD}) public @interface ApplicationContext {} diff --git a/java/dagger/hilt/processor/internal/ClassNames.java b/java/dagger/hilt/processor/internal/ClassNames.java index bc0591cb0ff..36e35f14ad4 100644 --- a/java/dagger/hilt/processor/internal/ClassNames.java +++ b/java/dagger/hilt/processor/internal/ClassNames.java @@ -162,6 +162,8 @@ public final class ClassNames { get("dagger.hilt.android.internal.testing", "InternalTestRoot"); public static final ClassName TEST_INJECTOR = get("dagger.hilt.android.internal.testing", "TestInjector"); + + public static final ClassName TEST_INJECT_INTERCEPTOR = get("dagger.hilt.android.internal.managers", "TestInjectInterceptor"); public static final ClassName TEST_APPLICATION_COMPONENT_MANAGER = get("dagger.hilt.android.internal.testing", "TestApplicationComponentManager"); public static final ClassName TEST_APPLICATION_COMPONENT_MANAGER_HOLDER =