Skip to content

Commit

Permalink
Add a helper method to find the Application from a Context and use th…
Browse files Browse the repository at this point in the history
…at in all places that getApplicationContext() is called.

Fixes #2672.

RELNOTES=Fix issues when getApplicationContext() is overridden.
PiperOrigin-RevId: 380014855
  • Loading branch information
Chang-Eric authored and Dagger Team committed Jun 17, 2021
1 parent 396be80 commit 4b695ae
Show file tree
Hide file tree
Showing 19 changed files with 110 additions and 32 deletions.
3 changes: 3 additions & 0 deletions java/dagger/hilt/android/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ android_library(
exports = [
"//java/dagger/hilt:install_in",
"//java/dagger/hilt/android/components",
"//java/dagger/hilt/android/internal",
"//java/dagger/hilt/android/internal/builders",
"//java/dagger/hilt/android/internal/managers",
"//java/dagger/hilt/android/internal/managers:component_supplier",
Expand Down Expand Up @@ -101,6 +102,7 @@ android_library(
deps = [
":package_info",
"//java/dagger/hilt:entry_point",
"//java/dagger/hilt/android/internal",
"@google_bazel_common//third_party/java/jsr305_annotations",
"@maven//:androidx_activity_activity",
"@maven//:androidx_fragment_fragment",
Expand Down Expand Up @@ -137,6 +139,7 @@ android_library(
":package_info",
"//:dagger_with_compiler",
"//java/dagger/hilt:entry_point",
"//java/dagger/hilt/android/internal",
"//java/dagger/hilt/internal:component_manager",
"//java/dagger/hilt/internal:preconditions",
"//java/dagger/hilt/internal:test_singleton_component_manager",
Expand Down
10 changes: 6 additions & 4 deletions java/dagger/hilt/android/EarlyEntryPoints.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@

package dagger.hilt.android;

import android.app.Application;
import android.content.Context;
import dagger.hilt.EntryPoints;
import dagger.hilt.android.internal.Contexts;
import dagger.hilt.internal.GeneratedComponentManagerHolder;
import dagger.hilt.internal.Preconditions;
import dagger.hilt.internal.TestSingletonComponentManager;
Expand All @@ -43,13 +45,13 @@ public final class EarlyEntryPoints {
// this method easier to use, since most code will use this with an Application or Context type.
@Nonnull
public static <T> T get(Context applicationContext, Class<T> entryPoint) {
applicationContext = applicationContext.getApplicationContext();
Application application = Contexts.getApplication(applicationContext.getApplicationContext());
Preconditions.checkState(
applicationContext instanceof GeneratedComponentManagerHolder,
application instanceof GeneratedComponentManagerHolder,
"Expected application context to implement GeneratedComponentManagerHolder. "
+ "Check that you're passing in an application context that uses Hilt.");
Object componentManager =
((GeneratedComponentManagerHolder) applicationContext).componentManager();
((GeneratedComponentManagerHolder) application).componentManager();
if (componentManager instanceof TestSingletonComponentManager) {
Preconditions.checkState(
hasAnnotationReflection(entryPoint, EarlyEntryPoint.class),
Expand All @@ -62,7 +64,7 @@ public static <T> T get(Context applicationContext, Class<T> entryPoint) {

// @EarlyEntryPoint only has an effect in test environment, so if this is not a test we
// delegate to EntryPoints.
return EntryPoints.get(applicationContext, entryPoint);
return EntryPoints.get(application, entryPoint);
}

// Note: This method uses reflection but it should only be called in test environments.
Expand Down
5 changes: 3 additions & 2 deletions java/dagger/hilt/android/EntryPointAccessors.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import androidx.fragment.app.Fragment;
import android.view.View;
import dagger.hilt.EntryPoints;
import dagger.hilt.android.internal.Contexts;
import javax.annotation.Nonnull;

/** Static utility methods for dealing with entry points for standard Android components. */
Expand All @@ -29,11 +30,11 @@ public final class EntryPointAccessors {
/**
* Returns the entry point interface from an application. The context can be any context derived
* from the application context. May only be used with entry point interfaces installed in the
* ApplicationComponent.
* SingletonComponent.
*/
@Nonnull
public static <T> T fromApplication(Context context, Class<T> entryPoint) {
return EntryPoints.get(context.getApplicationContext(), entryPoint);
return EntryPoints.get(Contexts.getApplication(context.getApplicationContext()), entryPoint);
}

/**
Expand Down
5 changes: 4 additions & 1 deletion java/dagger/hilt/android/internal/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ package(default_visibility = ["//:src"])

android_library(
name = "internal",
srcs = ["ThreadUtil.java"],
srcs = [
"Contexts.java",
"ThreadUtil.java",
],
)

filegroup(
Expand Down
47 changes: 47 additions & 0 deletions java/dagger/hilt/android/internal/Contexts.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright (C) 2021 The Dagger Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package dagger.hilt.android.internal;

import android.app.Application;
import android.content.Context;
import android.content.ContextWrapper;

/**
* Utility methods for dealing with contexts.
*/
public final class Contexts {

/** Finds the android Application from a context. */
public static Application getApplication(Context context) {
if (context instanceof Application) {
return (Application) context;
}

Context unwrapContext = context;
while (unwrapContext instanceof ContextWrapper) {
unwrapContext = ((ContextWrapper) unwrapContext).getBaseContext();
if (unwrapContext instanceof Application) {
return (Application) unwrapContext;
}
}

throw new IllegalStateException(
"Could not find an Application in the given context: " + context);
}

private Contexts() {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import dagger.hilt.EntryPoints;
import dagger.hilt.InstallIn;
import dagger.hilt.android.ActivityRetainedLifecycle;
import dagger.hilt.android.EntryPointAccessors;
import dagger.hilt.android.components.ActivityRetainedComponent;
import dagger.hilt.android.internal.ThreadUtil;
import dagger.hilt.android.internal.builders.ActivityRetainedComponentBuilder;
Expand Down Expand Up @@ -97,9 +98,8 @@ private ViewModelProvider getViewModelProvider(
@SuppressWarnings("unchecked")
public <T extends ViewModel> T create(@NonNull Class<T> aClass) {
ActivityRetainedComponent component =
EntryPoints.get(
context.getApplicationContext(),
ActivityRetainedComponentBuilderEntryPoint.class)
EntryPointAccessors.fromApplication(
context, ActivityRetainedComponentBuilderEntryPoint.class)
.retainedComponentBuilder()
.build();
return (T) new ActivityRetainedComponentViewModel(component);
Expand Down
1 change: 1 addition & 0 deletions java/dagger/hilt/android/internal/managers/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ android_library(
"//java/dagger/hilt:entry_point",
"//java/dagger/hilt:install_in",
"//java/dagger/hilt/android:activity_retained_lifecycle",
"//java/dagger/hilt/android:entry_point_accessors",
"//java/dagger/hilt/android/components",
"//java/dagger/hilt/android/internal",
"//java/dagger/hilt/android/internal/builders",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import android.app.Application;
import android.content.Context;
import dagger.hilt.android.internal.Contexts;
import dagger.hilt.internal.GeneratedComponentManager;
import dagger.hilt.internal.Preconditions;

Expand All @@ -27,9 +28,9 @@
* <p>A manager for the creation of components that live in the BroadcastReceiver.
*/
public final class BroadcastReceiverComponentManager {
@SuppressWarnings("unchecked")

public static Object generatedComponent(Context context) {
Application application = (Application) context.getApplicationContext();
Application application = Contexts.getApplication(context.getApplicationContext());

Preconditions.checkArgument(
application instanceof GeneratedComponentManager,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import dagger.hilt.InstallIn;
import dagger.hilt.android.components.ActivityComponent;
import dagger.hilt.android.components.FragmentComponent;
import dagger.hilt.android.internal.Contexts;
import dagger.hilt.android.internal.builders.ViewComponentBuilder;
import dagger.hilt.android.internal.builders.ViewWithFragmentComponentBuilder;
import dagger.hilt.internal.GeneratedComponentManager;
Expand Down Expand Up @@ -143,8 +144,8 @@ private GeneratedComponentManager<?> getParentComponentManager(boolean allowMiss

private Context getParentContext(Class<?> parentType, boolean allowMissing) {
Context context = unwrap(view.getContext(), parentType);
if (context == unwrap(context.getApplicationContext(), GeneratedComponentManager.class)) {
// If we searched for a type but ended up on the application context, just return null
if (context == Contexts.getApplication(context.getApplicationContext())) {
// If we searched for a type but ended up on the application, just return null
// as this is never what we are looking for
Preconditions.checkState(
allowMissing,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import dagger.Module;
import dagger.Provides;
import dagger.hilt.InstallIn;
import dagger.hilt.android.internal.Contexts;
import dagger.hilt.android.qualifiers.ApplicationContext;
import dagger.hilt.components.SingletonComponent;

Expand All @@ -42,6 +43,6 @@ Context provideContext() {

@Provides
Application provideApplication() {
return (Application) applicationContext.getApplicationContext();
return Contexts.getApplication(applicationContext);
}
}
1 change: 1 addition & 0 deletions java/dagger/hilt/android/internal/modules/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ android_library(
"//:dagger_with_compiler",
"//java/dagger/hilt:install_in",
"//java/dagger/hilt/android/components",
"//java/dagger/hilt/android/internal",
"//java/dagger/hilt/android/qualifiers",
"@maven//:androidx_activity_activity",
"@maven//:androidx_annotation_annotation",
Expand Down
1 change: 1 addition & 0 deletions java/dagger/hilt/android/internal/testing/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ android_library(
deps = [
":test_application_component_manager",
":test_application_component_manager_holder",
"//java/dagger/hilt/android/internal",
"//java/dagger/hilt/internal:component_manager",
"//java/dagger/hilt/internal:preconditions",
"@maven//:androidx_test_core",
Expand Down
23 changes: 13 additions & 10 deletions java/dagger/hilt/android/internal/testing/MarkThatRulesRanRule.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@
import static dagger.hilt.internal.Preconditions.checkNotNull;
import static dagger.hilt.internal.Preconditions.checkState;

import android.content.Context;
import android.app.Application;
import androidx.test.core.app.ApplicationProvider;
import dagger.hilt.android.internal.Contexts;
import dagger.hilt.internal.GeneratedComponentManager;
import java.lang.annotation.Annotation;
import java.util.concurrent.atomic.AtomicBoolean;
Expand All @@ -38,7 +39,8 @@ public final class MarkThatRulesRanRule implements TestRule {
private static final String HILT_ANDROID_APP = "dagger.hilt.android.HiltAndroidApp";
private static final String HILT_ANDROID_TEST = "dagger.hilt.android.testing.HiltAndroidTest";

private final Context context = ApplicationProvider.getApplicationContext();
private final Application application = Contexts.getApplication(
ApplicationProvider.getApplicationContext());
private final Object testInstance;
private final boolean autoAddModule;

Expand All @@ -52,19 +54,19 @@ public MarkThatRulesRanRule(Object testInstance) {
"Expected %s to be annotated with @HiltAndroidTest.",
testInstance.getClass().getName());
checkState(
context instanceof GeneratedComponentManager,
application instanceof GeneratedComponentManager,
"Hilt test, %s, must use a Hilt test application but found %s. To fix, configure the test "
+ "to use HiltTestApplication or a custom Hilt test application generated with "
+ "@CustomTestApplication.",
testInstance.getClass().getName(),
context.getClass().getName());
application.getClass().getName());
checkState(
!hasAnnotation(context, HILT_ANDROID_APP),
!hasAnnotation(application, HILT_ANDROID_APP),
"Hilt test, %s, cannot use a @HiltAndroidApp application but found %s. To fix, configure "
+ "the test to use HiltTestApplication or a custom Hilt test application generated "
+ "with @CustomTestApplication.",
testInstance.getClass().getName(),
context.getClass().getName());
application.getClass().getName());
}

public void delayComponentReady() {
Expand Down Expand Up @@ -114,10 +116,11 @@ public void evaluate() throws Throwable {

private TestApplicationComponentManager getTestApplicationComponentManager() {
checkState(
context instanceof TestApplicationComponentManagerHolder,
"The context is not an instance of TestApplicationComponentManagerHolder: %s",
context);
Object componentManager = ((TestApplicationComponentManagerHolder) context).componentManager();
application instanceof TestApplicationComponentManagerHolder,
"The application is not an instance of TestApplicationComponentManagerHolder: %s",
application);
Object componentManager =
((TestApplicationComponentManagerHolder) application).componentManager();
checkState(
componentManager instanceof TestApplicationComponentManager,
"Expected TestApplicationComponentManagerHolder to return an instance of"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -393,15 +393,17 @@ private static CodeBlock getParentCodeBlock(AndroidEntryPointMetadata metadata)
switch (metadata.androidType()) {
case ACTIVITY:
case SERVICE:
return CodeBlock.of("getApplicationContext()");
return CodeBlock.of("$T.getApplication(getApplicationContext())", ClassNames.CONTEXTS);
case FRAGMENT:
return CodeBlock.of("getHost()");
case VIEW:
return CodeBlock.of(
"$L.maybeGetParentComponentManager()", componentManagerCallBlock(metadata));
case BROADCAST_RECEIVER:
// Broadcast receivers receive a "context" parameter
return CodeBlock.of("context.getApplicationContext()");
return CodeBlock.of(
"$T.getApplication(context.getApplicationContext())",
ClassNames.CONTEXTS);
default:
throw new AssertionError();
}
Expand Down
2 changes: 2 additions & 0 deletions java/dagger/hilt/android/testing/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ android_library(
"//:dagger_with_compiler",
"//java/dagger/hilt:install_in",
"//java/dagger/hilt/android/components",
"//java/dagger/hilt/android/internal",
"//java/dagger/hilt/android/internal/builders",
"//java/dagger/hilt/android/internal/legacy:aggregated_element_proxy",
"//java/dagger/hilt/android/internal/managers",
Expand Down Expand Up @@ -121,6 +122,7 @@ android_library(
":package_info",
"//:dagger_with_compiler",
"//java/dagger/hilt:entry_point",
"//java/dagger/hilt/android/internal",
"//java/dagger/hilt/android/internal/testing:test_application_component_manager_holder",
"//java/dagger/hilt/internal:component_manager",
"//java/dagger/hilt/internal:preconditions",
Expand Down
3 changes: 2 additions & 1 deletion java/dagger/hilt/android/testing/OnComponentReadyRunner.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import android.content.Context;
import com.google.auto.value.AutoValue;
import dagger.hilt.EntryPoints;
import dagger.hilt.android.internal.Contexts;
import dagger.hilt.android.internal.testing.TestApplicationComponentManagerHolder;
import dagger.hilt.internal.GeneratedComponentManager;
import dagger.hilt.internal.Preconditions;
Expand Down Expand Up @@ -48,7 +49,7 @@ public void setComponentManager(GeneratedComponentManager<?> componentManager) {
/** Must be called on the test thread, before the Statement is evaluated. */
public static <T> void addListener(
Context context, Class<T> entryPoint, OnComponentReadyListener<T> listener) {
Application application = (Application) context.getApplicationContext();
Application application = Contexts.getApplication(context.getApplicationContext());
if (application instanceof TestApplicationComponentManagerHolder) {
TestApplicationComponentManagerHolder managerHolder =
(TestApplicationComponentManagerHolder) application;
Expand Down
2 changes: 2 additions & 0 deletions java/dagger/hilt/processor/internal/ClassNames.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ public final class ClassNames {
public static final ClassName PROCESSED_ROOT_SENTINEL =
get("dagger.hilt.internal.processedrootsentinel", "ProcessedRootSentinel");

public static final ClassName CONTEXTS = get("dagger.hilt.android.internal", "Contexts");

public static final String AGGREGATED_EARLY_ENTRY_POINT_PACKAGE =
"dagger.hilt.android.internal.earlyentrypoint.codegen";
public static final ClassName AGGREGATED_EARLY_ENTRY_POINT =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,12 @@ static void generate(ProcessingEnvironment env) throws IOException {
.returns(ClassName.OBJECT)
.addStatement(
"return $T.builder()\n"
+ ".applicationContextModule(new $T($T.getApplicationContext()))\n"
+ ".applicationContextModule(\n"
+ " new $T($T.getApplication($T.getApplicationContext())))\n"
+ ".build()",
DEFAULT_COMPONENT_IMPL,
ClassNames.APPLICATION_CONTEXT_MODULE,
ClassNames.CONTEXTS,
ClassNames.APPLICATION_PROVIDER)
.build());

Expand Down
Loading

0 comments on commit 4b695ae

Please sign in to comment.