diff --git a/java/dagger/hilt/android/internal/lifecycle/DefaultActivityViewModelFactory.java b/java/dagger/hilt/android/internal/lifecycle/DefaultActivityViewModelFactory.java deleted file mode 100644 index becce8cdb23..00000000000 --- a/java/dagger/hilt/android/internal/lifecycle/DefaultActivityViewModelFactory.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2020 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.lifecycle; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import javax.inject.Qualifier; - -/** Qualifier for the default view model factory used by @AndroidEntryPoint annotated activities. */ -@Qualifier -@Retention(RetentionPolicy.CLASS) -@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER}) -public @interface DefaultActivityViewModelFactory {} diff --git a/java/dagger/hilt/android/internal/lifecycle/DefaultFragmentViewModelFactory.java b/java/dagger/hilt/android/internal/lifecycle/DefaultFragmentViewModelFactory.java deleted file mode 100644 index 996705376ba..00000000000 --- a/java/dagger/hilt/android/internal/lifecycle/DefaultFragmentViewModelFactory.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2020 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.lifecycle; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import javax.inject.Qualifier; - -/** Qualifier for the default view model factory used by @AndroidEntryPoint annotated fragments. */ -@Qualifier -@Retention(RetentionPolicy.CLASS) -@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER}) -public @interface DefaultFragmentViewModelFactory {} diff --git a/java/dagger/hilt/android/internal/lifecycle/DefaultViewModelFactories.java b/java/dagger/hilt/android/internal/lifecycle/DefaultViewModelFactories.java index 427822dbbac..448847cd3b8 100644 --- a/java/dagger/hilt/android/internal/lifecycle/DefaultViewModelFactories.java +++ b/java/dagger/hilt/android/internal/lifecycle/DefaultViewModelFactories.java @@ -50,10 +50,11 @@ public final class DefaultViewModelFactories { * *

Do not use except in Hilt generated code! */ - public static ViewModelProvider.Factory getActivityFactory(ComponentActivity activity) { + public static ViewModelProvider.Factory getActivityFactory(ComponentActivity activity, + ViewModelProvider.Factory delegateFactory) { return EntryPoints.get(activity, ActivityEntryPoint.class) .getHiltInternalFactoryFactory() - .fromActivity(activity); + .fromActivity(activity, delegateFactory); } /** @@ -61,10 +62,11 @@ public static ViewModelProvider.Factory getActivityFactory(ComponentActivity act * *

Do not use except in Hilt generated code! */ - public static ViewModelProvider.Factory getFragmentFactory(Fragment fragment) { + public static ViewModelProvider.Factory getFragmentFactory( + Fragment fragment, ViewModelProvider.Factory delegateFactory) { return EntryPoints.get(fragment, FragmentEntryPoint.class) .getHiltInternalFactoryFactory() - .fromFragment(fragment); + .fromFragment(fragment, delegateFactory); } /** Internal factory for the Hilt ViewModel Factory. */ @@ -73,33 +75,28 @@ public static final class InternalFactoryFactory { private final Application application; private final Set keySet; private final ViewModelComponentBuilder viewModelComponentBuilder; - @Nullable private final ViewModelProvider.Factory defaultActivityFactory; - @Nullable private final ViewModelProvider.Factory defaultFragmentFactory; @Inject InternalFactoryFactory( Application application, @HiltViewModelMap.KeySet Set keySet, - ViewModelComponentBuilder viewModelComponentBuilder, - // These default factory bindings are temporary for the transition of deprecating - // the Hilt ViewModel extension for the built-in support - @DefaultActivityViewModelFactory Set defaultActivityFactorySet, - @DefaultFragmentViewModelFactory Set defaultFragmentFactorySet) { + ViewModelComponentBuilder viewModelComponentBuilder) { this.application = application; this.keySet = keySet; this.viewModelComponentBuilder = viewModelComponentBuilder; - this.defaultActivityFactory = getFactoryFromSet(defaultActivityFactorySet); - this.defaultFragmentFactory = getFactoryFromSet(defaultFragmentFactorySet); } - ViewModelProvider.Factory fromActivity(ComponentActivity activity) { - return getHiltViewModelFactory(activity, + ViewModelProvider.Factory fromActivity( + ComponentActivity activity, ViewModelProvider.Factory delegateFactory) { + return getHiltViewModelFactory( + activity, activity.getIntent() != null ? activity.getIntent().getExtras() : null, - defaultActivityFactory); + delegateFactory); } - ViewModelProvider.Factory fromFragment(Fragment fragment) { - return getHiltViewModelFactory(fragment, fragment.getArguments(), defaultFragmentFactory); + ViewModelProvider.Factory fromFragment( + Fragment fragment, ViewModelProvider.Factory delegateFactory) { + return getHiltViewModelFactory(fragment, fragment.getArguments(), delegateFactory); } private ViewModelProvider.Factory getHiltViewModelFactory( @@ -112,24 +109,6 @@ private ViewModelProvider.Factory getHiltViewModelFactory( return new HiltViewModelFactory( owner, defaultArgs, keySet, delegate, viewModelComponentBuilder); } - - @Nullable - private static ViewModelProvider.Factory getFactoryFromSet(Set set) { - // A multibinding set is used instead of BindsOptionalOf because Optional is not available in - // Android until API 24 and we don't want to have Guava as a transitive dependency. - if (set.isEmpty()) { - return null; - } - if (set.size() > 1) { - throw new IllegalStateException( - "At most one default view model factory is expected. Found " + set); - } - ViewModelProvider.Factory factory = set.iterator().next(); - if (factory == null) { - throw new IllegalStateException("Default view model factory must not be null."); - } - return factory; - } } /** The activity module to declare the optional factories. */ @@ -139,14 +118,6 @@ interface ActivityModule { @Multibinds @HiltViewModelMap.KeySet abstract Set viewModelKeys(); - - @Multibinds - @DefaultActivityViewModelFactory - Set defaultActivityViewModelFactory(); - - @Multibinds - @DefaultFragmentViewModelFactory - Set defaultFragmentViewModelFactory(); } /** The activity entry point to retrieve the factory. */ diff --git a/java/dagger/hilt/android/processor/internal/androidentrypoint/ActivityGenerator.java b/java/dagger/hilt/android/processor/internal/androidentrypoint/ActivityGenerator.java index 6069d608b44..86fbaa71260 100644 --- a/java/dagger/hilt/android/processor/internal/androidentrypoint/ActivityGenerator.java +++ b/java/dagger/hilt/android/processor/internal/androidentrypoint/ActivityGenerator.java @@ -108,7 +108,8 @@ private MethodSpec init() { // @Override // public ViewModelProvider.Factory getDefaultViewModelProviderFactory() { - // return DefaultViewModelFactories.getActivityFactory(this); + // return DefaultViewModelFactories.getActivityFactory( + // this, super.getDefaultViewModelProviderFactory()); // } private MethodSpec getDefaultViewModelProviderFactory() { return MethodSpec.methodBuilder("getDefaultViewModelProviderFactory") @@ -116,7 +117,7 @@ private MethodSpec getDefaultViewModelProviderFactory() { .addModifiers(Modifier.PUBLIC) .returns(AndroidClassNames.VIEW_MODEL_PROVIDER_FACTORY) .addStatement( - "return $T.getActivityFactory(this)", + "return $T.getActivityFactory(this, super.getDefaultViewModelProviderFactory())", AndroidClassNames.DEFAULT_VIEW_MODEL_FACTORIES) .build(); } diff --git a/java/dagger/hilt/android/processor/internal/androidentrypoint/FragmentGenerator.java b/java/dagger/hilt/android/processor/internal/androidentrypoint/FragmentGenerator.java index 4ef479f46b5..81b2b615642 100644 --- a/java/dagger/hilt/android/processor/internal/androidentrypoint/FragmentGenerator.java +++ b/java/dagger/hilt/android/processor/internal/androidentrypoint/FragmentGenerator.java @@ -202,7 +202,8 @@ private MethodSpec inflatorMethod() { // @Override // public ViewModelProvider.Factory getDefaultViewModelProviderFactory() { - // return DefaultViewModelFactories.getFragmentFactory(this); + // return DefaultViewModelFactories.getFragmentFactory( + // this, super.getDefaultViewModelProviderFactory()); // } private MethodSpec getDefaultViewModelProviderFactory() { return MethodSpec.methodBuilder("getDefaultViewModelProviderFactory") @@ -210,7 +211,7 @@ private MethodSpec getDefaultViewModelProviderFactory() { .addModifiers(Modifier.PUBLIC) .returns(AndroidClassNames.VIEW_MODEL_PROVIDER_FACTORY) .addStatement( - "return $T.getFragmentFactory(this)", + "return $T.getFragmentFactory(this, super.getDefaultViewModelProviderFactory())", AndroidClassNames.DEFAULT_VIEW_MODEL_FACTORIES) .build(); } diff --git a/javatests/artifacts/hilt-android/simple/app/build.gradle b/javatests/artifacts/hilt-android/simple/app/build.gradle index 98b79e1883e..1d76ec26cc3 100644 --- a/javatests/artifacts/hilt-android/simple/app/build.gradle +++ b/javatests/artifacts/hilt-android/simple/app/build.gradle @@ -97,7 +97,6 @@ dependencies { // To help us catch version skew related issues in hilt extensions. // TODO(bcorso): Add examples testing the actual API. - implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha01' implementation 'androidx.hilt:hilt-work:1.0.0-alpha01' annotationProcessor 'androidx.hilt:hilt-compiler:1.0.0-alpha01' testAnnotationProcessor 'androidx.hilt:hilt-compiler:1.0.0-alpha01' diff --git a/javatests/dagger/hilt/android/BUILD b/javatests/dagger/hilt/android/BUILD index 982862ff2ea..3205b84fd62 100644 --- a/javatests/dagger/hilt/android/BUILD +++ b/javatests/dagger/hilt/android/BUILD @@ -293,6 +293,7 @@ android_local_test( "//java/dagger/hilt/android:android_entry_point", "//java/dagger/hilt/android:package_info", "//java/dagger/hilt/android/lifecycle", + "//java/dagger/hilt/android/testing:bind_value", "//java/dagger/hilt/android/testing:hilt_android_test", "@google_bazel_common//third_party/java/jsr330_inject", "@google_bazel_common//third_party/java/truth", diff --git a/javatests/dagger/hilt/android/DefaultViewModelFactoryTest.java b/javatests/dagger/hilt/android/DefaultViewModelFactoryTest.java index 00471903afc..78cd5688a3e 100644 --- a/javatests/dagger/hilt/android/DefaultViewModelFactoryTest.java +++ b/javatests/dagger/hilt/android/DefaultViewModelFactoryTest.java @@ -18,15 +18,19 @@ import static com.google.common.truth.Truth.assertThat; +import androidx.lifecycle.ViewModel; +import androidx.lifecycle.ViewModelProvider; import android.os.Build; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentActivity; import androidx.test.core.app.ActivityScenario; import androidx.test.ext.junit.runners.AndroidJUnit4; -import dagger.hilt.android.internal.lifecycle.HiltViewModelFactory; +import dagger.hilt.android.lifecycle.HiltViewModel; +import dagger.hilt.android.testing.BindValue; import dagger.hilt.android.testing.HiltAndroidRule; import dagger.hilt.android.testing.HiltAndroidTest; import dagger.hilt.android.testing.HiltTestApplication; +import javax.inject.Inject; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -40,36 +44,82 @@ public class DefaultViewModelFactoryTest { @Rule public final HiltAndroidRule rule = new HiltAndroidRule(this); + @BindValue String hiltStringValue = "hilt"; + @Test - public void activityFactory() { + public void activityFactoryFallsBackToBase() { try (ActivityScenario scenario = ActivityScenario.launch(TestActivity.class)) { scenario.onActivity( activity -> { - assertThat(activity.getDefaultViewModelProviderFactory()).isNotNull(); - assertThat(activity.getDefaultViewModelProviderFactory()) - .isInstanceOf(HiltViewModelFactory.class); + assertThat(new ViewModelProvider(activity).get(TestHiltViewModel.class).value) + .isEqualTo("hilt"); + assertThat(new ViewModelProvider(activity).get(TestViewModel.class).value) + .isEqualTo("non-hilt"); }); } } @Test - public void fragmentFactory() { + public void fragmentFactoryFallbsBackToBase() { // TODO(danysantiago): Use FragmentScenario when it becomes available. try (ActivityScenario scenario = ActivityScenario.launch(TestActivity.class)) { scenario.onActivity( activity -> { TestFragment fragment = new TestFragment(); activity.getSupportFragmentManager().beginTransaction().add(fragment, "").commitNow(); - assertThat(fragment.getDefaultViewModelProviderFactory()).isNotNull(); - assertThat(fragment.getDefaultViewModelProviderFactory()) - .isInstanceOf(HiltViewModelFactory.class); + assertThat(new ViewModelProvider(fragment).get(TestHiltViewModel.class).value) + .isEqualTo("hilt"); + assertThat(new ViewModelProvider(fragment).get(TestViewModel.class).value) + .isEqualTo("non-hilt"); }); } } - @AndroidEntryPoint(FragmentActivity.class) + @HiltViewModel + public static final class TestHiltViewModel extends ViewModel { + final String value; + + @Inject + TestHiltViewModel(String value) { + this.value = value; + } + } + + public static final class TestViewModel extends ViewModel { + final String value; + // Take in a string so it cannot be constructed by the default view model factory + public TestViewModel(String value) { + this.value = value; + } + } + + @AndroidEntryPoint(BaseActivity.class) public static final class TestActivity extends Hilt_DefaultViewModelFactoryTest_TestActivity {} - @AndroidEntryPoint(Fragment.class) + public static class BaseActivity extends FragmentActivity { + @SuppressWarnings("unchecked") + @Override public ViewModelProvider.Factory getDefaultViewModelProviderFactory() { + return new ViewModelProvider.Factory() { + @Override public T create(Class clazz) { + assertThat(clazz).isEqualTo(TestViewModel.class); + return (T) new TestViewModel("non-hilt"); + } + }; + } + } + + @AndroidEntryPoint(BaseFragment.class) public static final class TestFragment extends Hilt_DefaultViewModelFactoryTest_TestFragment {} + + public static class BaseFragment extends Fragment { + @SuppressWarnings("unchecked") + @Override public ViewModelProvider.Factory getDefaultViewModelProviderFactory() { + return new ViewModelProvider.Factory() { + @Override public T create(Class clazz) { + assertThat(clazz).isEqualTo(TestViewModel.class); + return (T) new TestViewModel("non-hilt"); + } + }; + } + } }