diff --git a/integration-tests/android-base-class/src/main/AndroidManifest.xml b/integration-tests/android-base-class/src/main/AndroidManifest.xml index 5a595e1f..a8fdaffd 100644 --- a/integration-tests/android-base-class/src/main/AndroidManifest.xml +++ b/integration-tests/android-base-class/src/main/AndroidManifest.xml @@ -18,6 +18,7 @@ + diff --git a/integration-tests/android-base-class/src/main/java/com/example/ActivityModule.java b/integration-tests/android-base-class/src/main/java/com/example/ActivityModule.java new file mode 100644 index 00000000..ed29d87a --- /dev/null +++ b/integration-tests/android-base-class/src/main/java/com/example/ActivityModule.java @@ -0,0 +1,19 @@ +package com.example; + +import dagger.Module; +import dagger.android.ContributesAndroidInjector; + +@Module +public abstract class ActivityModule { + @ContributesAndroidInjector(modules = ExampleActivity.LongModule.class) + abstract ExampleActivity contributeExampleActivity(); + + @ContributesAndroidInjector(modules = {ExampleActivity.LongModule.class, FragmentModule.class}) + abstract ExampleFragmentInjectionActivity contributeExampleFragmentInjectionActivity(); + + @Module + abstract static class FragmentModule { + @ContributesAndroidInjector + abstract ExampleFragment exampleFragment(); + } +} diff --git a/integration-tests/android-base-class/src/main/java/com/example/AppComponent.java b/integration-tests/android-base-class/src/main/java/com/example/AppComponent.java index 6cbb5081..526bd67d 100644 --- a/integration-tests/android-base-class/src/main/java/com/example/AppComponent.java +++ b/integration-tests/android-base-class/src/main/java/com/example/AppComponent.java @@ -1,20 +1,18 @@ package com.example; -import com.example.ExampleActivity.ExampleActivityModule; import com.example.ExampleService.ExampleServiceModule; import dagger.Component; import dagger.android.AndroidInjectionModule; import dagger.android.AndroidInjector; -import dagger.android.DispatchingAndroidInjector; -@Component(modules = { - ExampleActivityModule.class, - ExampleServiceModule.class, - AndroidInjectionModule.class, - StringModule.class -}) +@Component( + modules = { + ActivityModule.class, + ExampleServiceModule.class, + AndroidInjectionModule.class, + StringModule.class + }) public interface AppComponent extends AndroidInjector { @Component.Factory - interface Factory extends AndroidInjector.Factory { - } + interface Factory extends AndroidInjector.Factory {} } diff --git a/integration-tests/android-base-class/src/main/java/com/example/ExampleActivity.java b/integration-tests/android-base-class/src/main/java/com/example/ExampleActivity.java index 9dd481a8..6a07e53c 100644 --- a/integration-tests/android-base-class/src/main/java/com/example/ExampleActivity.java +++ b/integration-tests/android-base-class/src/main/java/com/example/ExampleActivity.java @@ -1,5 +1,9 @@ package com.example; +import static android.util.TypedValue.COMPLEX_UNIT_DIP; +import static android.view.Gravity.CENTER; +import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; + import android.content.Intent; import android.os.Bundle; import android.view.ViewGroup.LayoutParams; @@ -7,15 +11,9 @@ import androidx.annotation.Nullable; import dagger.Module; import dagger.Provides; -import dagger.android.AndroidInjection; -import dagger.android.ContributesAndroidInjector; import dagger.android.DaggerActivity; import javax.inject.Inject; -import static android.util.TypedValue.COMPLEX_UNIT_DIP; -import static android.view.Gravity.CENTER; -import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; - public final class ExampleActivity extends DaggerActivity { @Inject String string; @Inject long aLong; @@ -35,12 +33,6 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { startService(new Intent(this, ExampleService.class)); } - @Module - static abstract class ExampleActivityModule { - @ContributesAndroidInjector(modules = LongModule.class) - abstract ExampleActivity activity(); - } - @Module(includes = IntegerModule.class) static class LongModule { @@ -48,7 +40,6 @@ static class LongModule { static long provideLong() { return 10L; } - } @Module @@ -59,5 +50,4 @@ static int provideInt() { return 20; } } - } diff --git a/integration-tests/android-base-class/src/main/java/com/example/ExampleApp.java b/integration-tests/android-base-class/src/main/java/com/example/ExampleApp.java index 84093814..bb2402ca 100644 --- a/integration-tests/android-base-class/src/main/java/com/example/ExampleApp.java +++ b/integration-tests/android-base-class/src/main/java/com/example/ExampleApp.java @@ -4,7 +4,8 @@ import dagger.android.DaggerApplication; public final class ExampleApp extends DaggerApplication { - @Override protected AndroidInjector applicationInjector() { + @Override + protected AndroidInjector applicationInjector() { return DaggerAppComponent.factory().create(this); } } diff --git a/integration-tests/android-base-class/src/main/java/com/example/ExampleFragment.java b/integration-tests/android-base-class/src/main/java/com/example/ExampleFragment.java new file mode 100644 index 00000000..d6381b2b --- /dev/null +++ b/integration-tests/android-base-class/src/main/java/com/example/ExampleFragment.java @@ -0,0 +1,33 @@ +package com.example; + +import static android.util.TypedValue.COMPLEX_UNIT_DIP; +import static android.view.Gravity.CENTER; +import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; +import androidx.annotation.Nullable; +import dagger.android.DaggerFragment; +import javax.inject.Inject; + +public class ExampleFragment extends DaggerFragment { + @Inject String string; + @Inject long aLong; + @Inject int anInt; + + @Nullable + @Override + public View onCreateView( + LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { + TextView textView = new TextView(getActivity()); + textView.setLayoutParams(new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); + textView.setGravity(CENTER); + textView.setTextSize(COMPLEX_UNIT_DIP, 40); + textView.setText(string); + + return textView; + } +} diff --git a/integration-tests/android-base-class/src/main/java/com/example/ExampleFragmentInjectionActivity.java b/integration-tests/android-base-class/src/main/java/com/example/ExampleFragmentInjectionActivity.java new file mode 100644 index 00000000..ec495dd3 --- /dev/null +++ b/integration-tests/android-base-class/src/main/java/com/example/ExampleFragmentInjectionActivity.java @@ -0,0 +1,20 @@ +package com.example; + +import android.os.Bundle; +import androidx.annotation.Nullable; +import dagger.android.DaggerActivity; + +public class ExampleFragmentInjectionActivity extends DaggerActivity { + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + if (savedInstanceState == null) { + getFragmentManager() + .beginTransaction() + .replace(android.R.id.content, new ExampleFragment(), "ExampleFragment") + .commit(); + } + } +} diff --git a/integration-tests/android-base-class/src/main/java/com/example/ExampleService.java b/integration-tests/android-base-class/src/main/java/com/example/ExampleService.java index f7cc9c8e..674a4f77 100644 --- a/integration-tests/android-base-class/src/main/java/com/example/ExampleService.java +++ b/integration-tests/android-base-class/src/main/java/com/example/ExampleService.java @@ -4,7 +4,6 @@ import android.os.IBinder; import android.widget.Toast; import dagger.Module; -import dagger.android.AndroidInjection; import dagger.android.ContributesAndroidInjector; import dagger.android.DaggerService; import javax.inject.Inject; @@ -27,7 +26,7 @@ public void onCreate() { } @Module - static abstract class ExampleServiceModule { + abstract static class ExampleServiceModule { @ContributesAndroidInjector abstract ExampleService service(); } diff --git a/integration-tests/android-base-class/src/main/java/com/example/StringModule.java b/integration-tests/android-base-class/src/main/java/com/example/StringModule.java index 710ec1c4..424523f4 100644 --- a/integration-tests/android-base-class/src/main/java/com/example/StringModule.java +++ b/integration-tests/android-base-class/src/main/java/com/example/StringModule.java @@ -5,7 +5,8 @@ @Module abstract class StringModule { - @Provides static String string() { + @Provides + static String string() { return "Hello!"; } } diff --git a/integration-tests/android-base-class/src/test/java/com/example/ExampleActivityTest.java b/integration-tests/android-base-class/src/test/java/com/example/ExampleActivityTest.java index cc2e2026..d3ab792b 100644 --- a/integration-tests/android-base-class/src/test/java/com/example/ExampleActivityTest.java +++ b/integration-tests/android-base-class/src/test/java/com/example/ExampleActivityTest.java @@ -1,16 +1,15 @@ package com.example; +import static androidx.lifecycle.Lifecycle.State.CREATED; +import static androidx.test.core.app.ActivityScenario.launch; +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assume.assumeFalse; + import androidx.test.core.app.ActivityScenario; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; -import org.robolectric.annotation.Config; - -import static androidx.lifecycle.Lifecycle.State.CREATED; -import static androidx.test.core.app.ActivityScenario.launch; -import static com.google.common.truth.Truth.assertThat; -import static org.junit.Assume.assumeFalse; @RunWith(RobolectricTestRunner.class) // public final class ExampleActivityTest { @@ -20,14 +19,16 @@ public static void beforeClass() { assumeFalse(BuildConfig.FLAVOR.equals("reflect")); } - @Test public void activityInjection() { + @Test + public void activityInjection() { try (ActivityScenario scenario = launch(ExampleActivity.class)) { scenario.moveToState(CREATED); - scenario.onActivity(activity -> { - assertThat(activity.string).isEqualTo("Hello!"); - assertThat(activity.aLong).isEqualTo(10L); - assertThat(activity.anInt).isEqualTo(20); - }); + scenario.onActivity( + activity -> { + assertThat(activity.string).isEqualTo("Hello!"); + assertThat(activity.aLong).isEqualTo(10L); + assertThat(activity.anInt).isEqualTo(20); + }); } } } diff --git a/integration-tests/android-base-class/src/test/java/com/example/ExampleFragmentInjectionActivityTest.java b/integration-tests/android-base-class/src/test/java/com/example/ExampleFragmentInjectionActivityTest.java new file mode 100644 index 00000000..e35623c1 --- /dev/null +++ b/integration-tests/android-base-class/src/test/java/com/example/ExampleFragmentInjectionActivityTest.java @@ -0,0 +1,38 @@ +package com.example; + +import static androidx.lifecycle.Lifecycle.State.CREATED; +import static androidx.test.core.app.ActivityScenario.launch; +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assume.assumeFalse; + +import androidx.test.core.app.ActivityScenario; + +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +@RunWith(RobolectricTestRunner.class) // +public class ExampleFragmentInjectionActivityTest { + @BeforeClass + public static void beforeClass() { + // TODO https://github.com/JakeWharton/dagger-reflect/issues/202 + assumeFalse(BuildConfig.FLAVOR.equals("reflect")); + } + @Test + public void activityFragmentInjection() { + try (ActivityScenario scenario = + launch(ExampleFragmentInjectionActivity.class)) { + scenario.moveToState(CREATED); + scenario.onActivity( + activity -> { + ExampleFragment fragment = + (ExampleFragment) + activity.getFragmentManager().findFragmentByTag("ExampleFragment"); + assertThat(fragment.string).isEqualTo("Hello!"); + assertThat(fragment.aLong).isEqualTo(10L); + assertThat(fragment.anInt).isEqualTo(20); + }); + } + } +} diff --git a/integration-tests/android-has-interface/src/main/AndroidManifest.xml b/integration-tests/android-has-interface/src/main/AndroidManifest.xml index 5a595e1f..a8fdaffd 100644 --- a/integration-tests/android-has-interface/src/main/AndroidManifest.xml +++ b/integration-tests/android-has-interface/src/main/AndroidManifest.xml @@ -18,6 +18,7 @@ + diff --git a/integration-tests/android-has-interface/src/main/java/com/example/ActivityModule.java b/integration-tests/android-has-interface/src/main/java/com/example/ActivityModule.java new file mode 100644 index 00000000..ed29d87a --- /dev/null +++ b/integration-tests/android-has-interface/src/main/java/com/example/ActivityModule.java @@ -0,0 +1,19 @@ +package com.example; + +import dagger.Module; +import dagger.android.ContributesAndroidInjector; + +@Module +public abstract class ActivityModule { + @ContributesAndroidInjector(modules = ExampleActivity.LongModule.class) + abstract ExampleActivity contributeExampleActivity(); + + @ContributesAndroidInjector(modules = {ExampleActivity.LongModule.class, FragmentModule.class}) + abstract ExampleFragmentInjectionActivity contributeExampleFragmentInjectionActivity(); + + @Module + abstract static class FragmentModule { + @ContributesAndroidInjector + abstract ExampleFragment exampleFragment(); + } +} diff --git a/integration-tests/android-has-interface/src/main/java/com/example/AppComponent.java b/integration-tests/android-has-interface/src/main/java/com/example/AppComponent.java index f3b1dc42..c90b70ac 100644 --- a/integration-tests/android-has-interface/src/main/java/com/example/AppComponent.java +++ b/integration-tests/android-has-interface/src/main/java/com/example/AppComponent.java @@ -1,17 +1,17 @@ package com.example; -import com.example.ExampleActivity.ExampleActivityModule; import com.example.ExampleService.ExampleServiceModule; import dagger.Component; import dagger.android.AndroidInjectionModule; import dagger.android.DispatchingAndroidInjector; -@Component(modules = { - ExampleActivityModule.class, - ExampleServiceModule.class, - AndroidInjectionModule.class, - StringModule.class -}) +@Component( + modules = { + ActivityModule.class, + ExampleServiceModule.class, + AndroidInjectionModule.class, + StringModule.class + }) interface AppComponent { DispatchingAndroidInjector androidInjector(); } diff --git a/integration-tests/android-has-interface/src/main/java/com/example/ExampleActivity.java b/integration-tests/android-has-interface/src/main/java/com/example/ExampleActivity.java index 5ddcee62..82f2c40b 100644 --- a/integration-tests/android-has-interface/src/main/java/com/example/ExampleActivity.java +++ b/integration-tests/android-has-interface/src/main/java/com/example/ExampleActivity.java @@ -1,5 +1,9 @@ package com.example; +import static android.util.TypedValue.COMPLEX_UNIT_DIP; +import static android.view.Gravity.CENTER; +import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; + import android.app.Activity; import android.content.Intent; import android.os.Bundle; @@ -9,13 +13,8 @@ import dagger.Module; import dagger.Provides; import dagger.android.AndroidInjection; -import dagger.android.ContributesAndroidInjector; import javax.inject.Inject; -import static android.util.TypedValue.COMPLEX_UNIT_DIP; -import static android.view.Gravity.CENTER; -import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; - public final class ExampleActivity extends Activity { @Inject String string; @Inject long aLong; @@ -36,12 +35,6 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { startService(new Intent(this, ExampleService.class)); } - @Module - static abstract class ExampleActivityModule { - @ContributesAndroidInjector(modules = LongModule.class) - abstract ExampleActivity activity(); - } - @Module(includes = IntegerModule.class) static class LongModule { @@ -49,7 +42,6 @@ static class LongModule { static long provideLong() { return 10L; } - } @Module @@ -60,5 +52,4 @@ static int provideInt() { return 20; } } - } diff --git a/integration-tests/android-has-interface/src/main/java/com/example/ExampleApp.java b/integration-tests/android-has-interface/src/main/java/com/example/ExampleApp.java index d37b08d0..d080c488 100644 --- a/integration-tests/android-has-interface/src/main/java/com/example/ExampleApp.java +++ b/integration-tests/android-has-interface/src/main/java/com/example/ExampleApp.java @@ -8,7 +8,8 @@ public final class ExampleApp extends Application implements HasAndroidInjector { private AppComponent component; - @Override public void onCreate() { + @Override + public void onCreate() { super.onCreate(); component = Dagger.create(AppComponent.class); diff --git a/integration-tests/android-has-interface/src/main/java/com/example/ExampleFragment.java b/integration-tests/android-has-interface/src/main/java/com/example/ExampleFragment.java new file mode 100644 index 00000000..841271ec --- /dev/null +++ b/integration-tests/android-has-interface/src/main/java/com/example/ExampleFragment.java @@ -0,0 +1,50 @@ +package com.example; + +import static android.util.TypedValue.COMPLEX_UNIT_DIP; +import static android.view.Gravity.CENTER; +import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; + +import android.app.Activity; +import android.app.Fragment; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; +import androidx.annotation.Nullable; +import dagger.android.AndroidInjection; +import dagger.android.AndroidInjector; +import dagger.android.DispatchingAndroidInjector; +import dagger.android.HasAndroidInjector; +import javax.inject.Inject; + +public class ExampleFragment extends Fragment implements HasAndroidInjector { + @Inject DispatchingAndroidInjector androidInjector; + @Inject String string; + @Inject long aLong; + @Inject int anInt; + + @Override + public void onAttach(Activity activity) { + AndroidInjection.inject(this); + super.onAttach(activity); + } + + @Nullable + @Override + public View onCreateView( + LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { + TextView textView = new TextView(getActivity()); + textView.setLayoutParams(new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); + textView.setGravity(CENTER); + textView.setTextSize(COMPLEX_UNIT_DIP, 40); + textView.setText(string); + + return textView; + } + + @Override + public AndroidInjector androidInjector() { + return androidInjector; + } +} diff --git a/integration-tests/android-has-interface/src/main/java/com/example/ExampleFragmentInjectionActivity.java b/integration-tests/android-has-interface/src/main/java/com/example/ExampleFragmentInjectionActivity.java new file mode 100644 index 00000000..d0a66009 --- /dev/null +++ b/integration-tests/android-has-interface/src/main/java/com/example/ExampleFragmentInjectionActivity.java @@ -0,0 +1,32 @@ +package com.example; + +import android.app.Activity; +import android.os.Bundle; +import androidx.annotation.Nullable; +import dagger.android.AndroidInjection; +import dagger.android.AndroidInjector; +import dagger.android.DispatchingAndroidInjector; +import dagger.android.HasAndroidInjector; +import javax.inject.Inject; + +public class ExampleFragmentInjectionActivity extends Activity implements HasAndroidInjector { + @Inject DispatchingAndroidInjector androidInjector; + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + AndroidInjection.inject(this); + super.onCreate(savedInstanceState); + + if (savedInstanceState == null) { + getFragmentManager() + .beginTransaction() + .replace(android.R.id.content, new ExampleFragment(), "ExampleFragment") + .commit(); + } + } + + @Override + public AndroidInjector androidInjector() { + return androidInjector; + } +} diff --git a/integration-tests/android-has-interface/src/main/java/com/example/ExampleService.java b/integration-tests/android-has-interface/src/main/java/com/example/ExampleService.java index f6ecbf6d..213d81a6 100644 --- a/integration-tests/android-has-interface/src/main/java/com/example/ExampleService.java +++ b/integration-tests/android-has-interface/src/main/java/com/example/ExampleService.java @@ -7,7 +7,6 @@ import dagger.Module; import dagger.android.AndroidInjection; import dagger.android.ContributesAndroidInjector; - import javax.inject.Inject; public final class ExampleService extends Service { @@ -29,7 +28,7 @@ public void onCreate() { } @Module - static abstract class ExampleServiceModule { + abstract static class ExampleServiceModule { @ContributesAndroidInjector abstract ExampleService service(); } diff --git a/integration-tests/android-has-interface/src/main/java/com/example/StringModule.java b/integration-tests/android-has-interface/src/main/java/com/example/StringModule.java index 710ec1c4..424523f4 100644 --- a/integration-tests/android-has-interface/src/main/java/com/example/StringModule.java +++ b/integration-tests/android-has-interface/src/main/java/com/example/StringModule.java @@ -5,7 +5,8 @@ @Module abstract class StringModule { - @Provides static String string() { + @Provides + static String string() { return "Hello!"; } } diff --git a/integration-tests/android-has-interface/src/test/java/com/example/ExampleActivityTest.java b/integration-tests/android-has-interface/src/test/java/com/example/ExampleActivityTest.java index 4201fe58..9736e36c 100644 --- a/integration-tests/android-has-interface/src/test/java/com/example/ExampleActivityTest.java +++ b/integration-tests/android-has-interface/src/test/java/com/example/ExampleActivityTest.java @@ -1,24 +1,26 @@ package com.example; +import static androidx.lifecycle.Lifecycle.State.CREATED; +import static androidx.test.core.app.ActivityScenario.launch; +import static com.google.common.truth.Truth.assertThat; + import androidx.test.core.app.ActivityScenario; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; -import static androidx.lifecycle.Lifecycle.State.CREATED; -import static androidx.test.core.app.ActivityScenario.launch; -import static com.google.common.truth.Truth.assertThat; - @RunWith(RobolectricTestRunner.class) // public final class ExampleActivityTest { - @Test public void activityInjection() { + @Test + public void activityInjection() { try (ActivityScenario scenario = launch(ExampleActivity.class)) { scenario.moveToState(CREATED); - scenario.onActivity(activity -> { - assertThat(activity.string).isEqualTo("Hello!"); - assertThat(activity.aLong).isEqualTo(10L); - assertThat(activity.anInt).isEqualTo(20); - }); + scenario.onActivity( + activity -> { + assertThat(activity.string).isEqualTo("Hello!"); + assertThat(activity.aLong).isEqualTo(10L); + assertThat(activity.anInt).isEqualTo(20); + }); } } } diff --git a/integration-tests/android-has-interface/src/test/java/com/example/ExampleFragmentInjectionActivityTest.java b/integration-tests/android-has-interface/src/test/java/com/example/ExampleFragmentInjectionActivityTest.java new file mode 100644 index 00000000..0e879c56 --- /dev/null +++ b/integration-tests/android-has-interface/src/test/java/com/example/ExampleFragmentInjectionActivityTest.java @@ -0,0 +1,30 @@ +package com.example; + +import static androidx.lifecycle.Lifecycle.State.CREATED; +import static androidx.test.core.app.ActivityScenario.launch; +import static com.google.common.truth.Truth.assertThat; + +import androidx.test.core.app.ActivityScenario; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +@RunWith(RobolectricTestRunner.class) // +public class ExampleFragmentInjectionActivityTest { + @Test + public void activityFragmentInjection() { + try (ActivityScenario scenario = + launch(ExampleFragmentInjectionActivity.class)) { + scenario.moveToState(CREATED); + scenario.onActivity( + activity -> { + ExampleFragment fragment = + (ExampleFragment) + activity.getFragmentManager().findFragmentByTag("ExampleFragment"); + assertThat(fragment.string).isEqualTo("Hello!"); + assertThat(fragment.aLong).isEqualTo(10L); + assertThat(fragment.anInt).isEqualTo(20); + }); + } + } +} diff --git a/integration-tests/build.gradle b/integration-tests/build.gradle index aa1f6127..605be554 100644 --- a/integration-tests/build.gradle +++ b/integration-tests/build.gradle @@ -10,6 +10,7 @@ dependencies { testImplementation deps.junit testImplementation deps.truth + testImplementation deps.dagger.androidRuntimeJar testImplementation project(':reflect') testImplementation project(':codegen') testAnnotationProcessor deps.auto.value.compiler diff --git a/reflect/src/main/java/dagger/reflect/ReflectiveJustInTimeLookupFactory.java b/reflect/src/main/java/dagger/reflect/ReflectiveJustInTimeLookupFactory.java index 40bba4a0..11526a32 100644 --- a/reflect/src/main/java/dagger/reflect/ReflectiveJustInTimeLookupFactory.java +++ b/reflect/src/main/java/dagger/reflect/ReflectiveJustInTimeLookupFactory.java @@ -43,7 +43,13 @@ final class ReflectiveJustInTimeLookupFactory implements JustInTimeLookup.Factor } Annotation scope = findScope(cls.getAnnotations()); - Binding binding = new UnlinkedJustInTimeBinding<>(cls, target, typeArguments); + Binding binding; + if (TypeUtil.isAndroidInjectorFactory(type)) { + binding = + new UnlinkedJustInTimeContributesAndroidInjectorBinding<>(cls, target, typeArguments); + } else { + binding = new UnlinkedJustInTimeBinding<>(cls, target, typeArguments); + } return new JustInTimeLookup(scope, binding); } diff --git a/reflect/src/main/java/dagger/reflect/Scope.java b/reflect/src/main/java/dagger/reflect/Scope.java index b1189125..c2b68dcb 100644 --- a/reflect/src/main/java/dagger/reflect/Scope.java +++ b/reflect/src/main/java/dagger/reflect/Scope.java @@ -35,6 +35,11 @@ private Scope( this.parent = parent; } + @Nullable + public Scope getParent() { + return parent; + } + @Override public String toString() { return "Scope" + annotations; diff --git a/reflect/src/main/java/dagger/reflect/TypeUtil.java b/reflect/src/main/java/dagger/reflect/TypeUtil.java index 36e98c10..ff6ca86a 100644 --- a/reflect/src/main/java/dagger/reflect/TypeUtil.java +++ b/reflect/src/main/java/dagger/reflect/TypeUtil.java @@ -15,6 +15,7 @@ */ package dagger.reflect; +import dagger.android.DispatchingAndroidInjector; import java.lang.reflect.GenericArrayType; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; @@ -28,6 +29,15 @@ final class TypeUtil { private TypeUtil() {} + static boolean isAndroidInjectorFactory(Type type) { + if (type instanceof ParameterizedType) { + ParameterizedType parameterizedKeyType = (ParameterizedType) type; + Type rawKeyType = parameterizedKeyType.getRawType(); + return DispatchingAndroidInjector.class.equals(rawKeyType); + } + return false; + } + /** * Returns a type that is functionally equal but not necessarily equal according to {@link * Object#equals(Object) Object.equals()}. diff --git a/reflect/src/main/java/dagger/reflect/UnlinkedJustInTimeContributesAndroidInjectorBinding.java b/reflect/src/main/java/dagger/reflect/UnlinkedJustInTimeContributesAndroidInjectorBinding.java new file mode 100644 index 00000000..47f0d48c --- /dev/null +++ b/reflect/src/main/java/dagger/reflect/UnlinkedJustInTimeContributesAndroidInjectorBinding.java @@ -0,0 +1,79 @@ +package dagger.reflect; + +import static dagger.reflect.Reflection.findQualifier; + +import dagger.MembersInjector; +import java.lang.annotation.Annotation; +import java.lang.reflect.Constructor; +import java.lang.reflect.Type; +import java.util.Arrays; +import java.util.Map; +import org.jetbrains.annotations.Nullable; + +final class UnlinkedJustInTimeContributesAndroidInjectorBinding extends Binding.UnlinkedBinding { + private final Class cls; + private final Constructor constructor; + // Type arguments might be used as types for this binding's parameterized constructor parameters. + private @Nullable Type[] concreteTypeArguments; + + UnlinkedJustInTimeContributesAndroidInjectorBinding( + Class cls, Constructor constructor, @Nullable Type[] concreteTypeArguments) { + this.cls = cls; + this.constructor = constructor; + this.concreteTypeArguments = concreteTypeArguments; + } + + @Override + public LinkedBinding link(Linker linker, Scope scope) { + Type[] parameterTypes = constructor.getGenericParameterTypes(); + Annotation[][] parameterAnnotations = constructor.getParameterAnnotations(); + + LinkedBinding[] bindings = new LinkedBinding[parameterTypes.length]; + for (int i = 0; i < parameterTypes.length; i++) { + Type parameterType = parameterTypes[i]; + Key key = Key.of(findQualifier(parameterAnnotations[i]), parameterType); + bindings[i] = linker.get(key); + addParentMappings(key, scope.getParent(), linker); + } + + MembersInjector membersInjector = ReflectiveMembersInjector.create(cls, scope); + + return new LinkedJustInTimeBinding<>(constructor, bindings, membersInjector); + } + + private void addParentMappings(Key key, @Nullable Scope parentScope, Linker linker) { + if (parentScope == null) { + return; + } + + Binding.LinkedBinding bindingLinked = linker.get(key); + Object value = bindingLinked.get(); + + if (value instanceof Map) { + Map, Binding> bindingValue = (Map, Binding>) value; + Binding.LinkedBinding parentBinding = parentScope.getBinding(key); + if (parentBinding != null) { + Object parentBindingValue = parentBinding.get(); + if (parentBindingValue instanceof Map) { + Map, LinkedBinding> parentBindingValueMap = + (Map, LinkedBinding>) parentBindingValue; + bindingValue.putAll(parentBindingValueMap); + } + } + } + + addParentMappings(key, parentScope.getParent(), linker); + } + + @Override + public String toString() { + return "@Inject[" + cls.getName() + getTypeArgumentsStringOrEmpty() + ".(…)]"; + } + + private String getTypeArgumentsStringOrEmpty() { + if (concreteTypeArguments == null) { + return ""; + } + return Arrays.toString(concreteTypeArguments); + } +}