Skip to content

Commit

Permalink
Remove support for the deprecated, alpha @ViewModelInject. This also …
Browse files Browse the repository at this point in the history
…adds support for falling back to the default view model factory of the base activity/fragment.

RELNOTES=@ViewModelInject no longer supported. Hilt now falls back to the base activity/fragment default ViewModelProviderFactory
PiperOrigin-RevId: 363228502
  • Loading branch information
Chang-Eric authored and Dagger Team committed Mar 16, 2021
1 parent d4078fd commit 3778ee2
Show file tree
Hide file tree
Showing 8 changed files with 83 additions and 118 deletions.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -50,21 +50,23 @@ public final class DefaultViewModelFactories {
*
* <p>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);
}

/**
* Retrieves the default view model factory for the activity.
*
* <p>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. */
Expand All @@ -73,33 +75,28 @@ public static final class InternalFactoryFactory {
private final Application application;
private final Set<String> keySet;
private final ViewModelComponentBuilder viewModelComponentBuilder;
@Nullable private final ViewModelProvider.Factory defaultActivityFactory;
@Nullable private final ViewModelProvider.Factory defaultFragmentFactory;

@Inject
InternalFactoryFactory(
Application application,
@HiltViewModelMap.KeySet Set<String> 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<ViewModelProvider.Factory> defaultActivityFactorySet,
@DefaultFragmentViewModelFactory Set<ViewModelProvider.Factory> 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(
Expand All @@ -112,24 +109,6 @@ private ViewModelProvider.Factory getHiltViewModelFactory(
return new HiltViewModelFactory(
owner, defaultArgs, keySet, delegate, viewModelComponentBuilder);
}

@Nullable
private static ViewModelProvider.Factory getFactoryFromSet(Set<ViewModelProvider.Factory> 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. */
Expand All @@ -139,14 +118,6 @@ interface ActivityModule {
@Multibinds
@HiltViewModelMap.KeySet
abstract Set<String> viewModelKeys();

@Multibinds
@DefaultActivityViewModelFactory
Set<ViewModelProvider.Factory> defaultActivityViewModelFactory();

@Multibinds
@DefaultFragmentViewModelFactory
Set<ViewModelProvider.Factory> defaultFragmentViewModelFactory();
}

/** The activity entry point to retrieve the factory. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,15 +108,16 @@ 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")
.addAnnotation(Override.class)
.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();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,15 +202,16 @@ 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")
.addAnnotation(Override.class)
.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();
}
Expand Down
1 change: 0 additions & 1 deletion javatests/artifacts/hilt-android/simple/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
1 change: 1 addition & 0 deletions javatests/dagger/hilt/android/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
72 changes: 61 additions & 11 deletions javatests/dagger/hilt/android/DefaultViewModelFactoryTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<TestActivity> 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<TestActivity> 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 extends ViewModel> T create(Class<T> 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 extends ViewModel> T create(Class<T> clazz) {
assertThat(clazz).isEqualTo(TestViewModel.class);
return (T) new TestViewModel("non-hilt");
}
};
}
}
}

0 comments on commit 3778ee2

Please sign in to comment.