Giter Site home page Giter Site logo

inflationinject's Introduction

Inflation Injection

Attention: This tool is now deprecated. Please switch to Compose UI or manually pass dependencies to the legacy view system. Existing versions will also continue to work, but feature development and bug fixes have stopped.


Constructor-inject views during XML layout inflation.

Looking for Assisted Inject? It's built in to Dagger now!

Usage

Write your layout XML like normal.

<LinearLayout>
  <com.example.CustomView/>
  <TextView/>
</LinearLayout>

Use @InflationInject in CustomView:

public final class CustomView extends View {
  private final Picasso picasso;
  
  @InflationInject
  public CustomView(
    @Inflated Context context,
    @Inflated AttributeSet attrs,
    Picasso picasso
  ) {
    super(context, attrs);
    this.picasso = picasso;
  }
  
  // ...
}

In order to allow Dagger to create your custom views, add @InflationModule to a Dagger module and add the generated module name to its includes=.

@InflationModule
@Module(includes = InflationInject_PresenterModule.class)
abstract class PresenterModule {}

The annotation processor will generate the InflationInject_PresenterModule for us. It will not be resolved until the processor runs.

Finally, inject InflationInjectFactory and add it to your LayoutInflater.

InflationInjectFactory factory = DaggerMainActivity_MainComponent.create().factory();
getLayoutInflater().setFactory(factory);

setContentView(R.layout.main_view);

Download

repositories {
  mavenCentral()
}
dependencies {
  implementation 'app.cash.inject:inflation-inject:1.0.1'
  annotationProcessor 'app.cash.inject:inflation-inject-processor:1.0.1'
}
Snapshots of the development version are available in Sonatype's snapshots repository.

repositories {
  maven {
    url 'https://oss.sonatype.org/content/repositories/snapshots/'
  }
}
dependencies {
  implementation 'app.cash.inject:inflation-inject:1.1.0-SNAPSHOT'
  annotationProcessor 'app.cash.inject:inflation-inject-processor:1.1.0-SNAPSHOT'
}

License

Copyright 2017 Square, Inc.

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.

inflationinject's People

Contributors

abyrnes avatar aleckazakova avatar ansman avatar egorand avatar gm-vm avatar hansenji avatar jakewharton avatar jrodbx avatar oldergod avatar renovate[bot] avatar runningcode avatar stoyicker avatar swankjesse avatar trevjonez avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

inflationinject's Issues

Generated factory includes extra Provider<T> layer

When the target class has a Provider<T> binding, the generated factory class includes a constructor param Provider<Provider<T>>. As a result, Dagger 2 can't find the binding.

Should AssistedInject flatten Provider<T> bindings in this case?

AssistedInject crashes without message when using with class with generic parameter

AssistedInject fails without explanation when I try to use it with various classes that use generics. The simplest case that fails for me with Dagger 2.24, AssistedInject 0.5.0 and Kotlin 1.3.50:

class AClass<A> @AssistedInject constructor(
    @Assisted dependency: String
) {

    @AssistedInject.Factory
    interface Factory {

        fun create(dependency: String): AClass<String>
    }
}

The only error that appears is the following:

error: cannot find symbol
@dagger.Module(includes = {AssistedInject_PresentationModule.class})
                           ^
  symbol: class AssistedInject_PresentationModule
FAILURE: Build failed with an exception.

This is just a simplified version of what I was trying to do initially, which also fails with no meaningful message:

abstract class Base<T>
class Wrapper<A, B>

class AClass<A, B> @AssistedInject constructor(
    @Assisted dependency: (A) -> B
) : Base<Wrapper<A, B>>() {

    @AssistedInject.Factory
    interface Factory {

        fun <A, B> create(dependency: (A) -> B): AClass<A, B>
    }
}

Provide transitive dependencies

It would be great if AssistedInject could manage things like this:

class Foo @Inject constructor(activity: Activity, otherDependency: Other)

class Bar @AssistedInject constructor(foo: Foo) {

  @AssistedInject.Factory
  interface Factory {
    fun create(activity: Activity)
  }
}

I need to inject something but I'm missing one dependency (or more) in the graph. So I provide it using an @AssistedInject.Factory. The difference here is that the missing dependency is not injected directly to the class that exposes the Factory.

Allow multiple non-assisted parameters with the same binding

This code:

class MyClass @AssistedInject constructor(
    @Foo foobar1: Foobar,
    @Foo foobar2: Foobar,
    @Assisted a: String
) {
    @AssistedInject.Factory
    interface Factory {
        fun create(a: String): MyClass
    }
}

Produces this error:

Duplicate non-@assisted parameters declared. Forget a qualifier annotation?

It seems like this is an unnecessary restriction. I acknowledge that it may be an uncommon use case, but it seems reasonable that I might want to construct a MyClass manually in a unit test with different values for foobar1 and foobar2.

In my particular case, it's 2 RxJava Scheduler args. When constructed via Dagger, I expect to receive the same object twice (a main thread scheduler). In my unit test, I use one of them for a timer, so I supply a TestScheduler, and the other for observeOn the main thread, so I supply Schedulers.trampoline(). The above error means I cannot supply the separate Schedulers, which in turn means I must triggerActions() on my TestScheduler in order to execute the majority of my tests.

Can this restriction be lifted? Is it truly necessary, or a case of failing eagerly to detect programmer error?

Error when @Assisted is used incorrectly

In a non-general way, the cases are:

  • Not on a constructor param
  • On a constructor without one of the two supported constructor annotations

This prevents someone else from re-using the annotation for something else though. Maybe we're fine with that to start with until someone complains?

Kapt is failing without clear error place

I see the next error message:

 symbol: class AssistedInject_AppAssistedInjectModule/app/build/tmp/kapt3/stubs/debug/com/android/AppComponent.java:40: error: [Dagger/MissingBinding] com.android.budgets.ui.overview.BudgetsOverviewViewModel.Factory cannot be provided without an @Provides-annotated method.
public abstract interface AppComponent {
                ^
      com.android.budgets.ui.overview.BudgetsOverviewViewModel.Factory is injected at
          com.android.budgets.ui.overview.BudgetsOverviewNavigation(viewModelFactory)
      com.android.budgets.ui.overview.BudgetsOverviewNavigation is provided at
          com.android.budgets.BudgetsComponent.overviewNavigation() [com.android.AppComponent → com.android.user.UserSessionComponent → com.android.user.UserComponent → com.android.budgets.BudgetsComponent]/app/src/main/java/com/yolt/android/recovery/recover/ui/pin/PinDigitsViewModel.kt:12: warning: Assisted injection without at least one non-@Assisted parameter doesn't need a factory
class PinDigitsViewModel @AssistedInject constructor(@Assisted private val onComplete: (ByteArray) -> Unit) {
^app/build/tmp/kapt3/stubs/debug/com/android/dagger/AppAssistedInjectModule.java:9: error: [ComponentProcessor:MiscError] dagger.internal.codegen.ComponentProcessor was unable to process this interface because not all of its dependencies could be resolved. Check for compilation errors or a circular dependency with generated code.

If check the Model class I see it requires injection another class that has generics and one of the constructor params is also assisted injected class.

I saw an issue with generics and assisted inject. Or I wonder what another thing can be wrong. For example, this generic class operates with data binding generated code.

Feature Request: Distinguish between type/name mismatch in constructor vs factory interface

Summary: Currently, the AssistedInject compiler does not inform the user whether the name/type mismatch occurs at the interface/factory or during usage in the assist-injected constructor.

Example:

interface ListenableWorkerFactory {
    fun create(context: Context, params: WorkerParameters): RxWorker
}

Constructor Usage:

class ConversationOperationWorker
@AssistedInject constructor(@Assisted context: Context,
                            @Assisted params2: WorkerParameters, //note, should be `params`
                            private val client: ConversationActionClient,
                            private val gson: Gson) : RxWorker(context, params2){ //note, should be `params`

    @AssistedInject.Factory
    interface Factory : ListenableWorkerFactory
    
    //... body
}

The compiler error's stacktrace was as follows:

e: error: Factory method parameters do not match constructor @Assisted parameters.
  Missing:
   * android.content.Context context
   * androidx.work.WorkerParameters params2
  Unknown:
   * android.content.Context arg0
   * androidx.work.WorkerParameters arg1

Although the compiler was correct in that the ListenableWorkerFactory's params were not being matched, it did not tell me where they weren't being matched.

Now, admittedly, there was user-error here, and it was twofold:

  1. I didn't use the same names everywhere
  2. I didn't read the error closely enough to realize that it was telling me the incorrect name usage (e.g. params2)

That being said, having the compiler point to the erroneous usage would be nice:

  1. the error message should pass the offending element so that javac highlights it like a compiler error, with the little snippet + shark-fin ^^ underneath.

  2. Noting at which site the element resides (e.g. constructor vs. factory vs. inherited factory interface), so that the error would say something like "${paramName} Declared in constructor but missing from factory" or "${paramName} present in factory but but missing from constructor"

This happens on the latest 0.5.0 binary.

Generated ViewFactory uses raw interface type

Mostly just an unfortunate side-effect of re-using code gen. Still works fine.

We probably have to split the model into two: one which represents the elements and the other which represents the JavaPoet names + keys. This way each processor can have separate element models for validation but then both produce a name+key model for code gen.

Convince Dagger to support automatic module discovery in a general way

We've now hit the rule of three with public use cases for this feature:

  • @ContributesAndroidInjector
  • @AssistedInject
  • @InflationInject

The first one has massively superior ergonomics because its generated bindings can be auto-discovered and bound into the graph. It gets this by virtue of being built by the Dagger team. Lucky!

The latter two are in this repo and do not receive auto-discovery. Instead, we force the consumers to annotate a module such that we generate a sibling module which contains only unscoped bindings. This lets us talk to Dagger where we otherwise cannot. It has devastating effects on incremental compilation and incremental annotation processing since both need to aggregate all usages in a compilation unit. And finally, it has annoying ergonomics because you have to reference generated code directly and manually include a module.

An additional hypothetic use case as well as the general Dagger feature request is tracked here: google/dagger#869. This also would eliminate the need for Dagger to build everything as a first-party feature (e.g., google/dagger#935) and allow people to mix-in behavior from other libraries.

We should come up with a proposal for the general mechanism on which Dagger's @ContributesAndroidInjector and our @AssistedInject and @InflationInject can be built.

Optionally auto generate factory interfaces?

Seems like it would be better to have the factory interfaces be autogenerated rather than require the boilerplate of declaring an interface (which in turn is mostly a duplicate of the constructor).

Kotlin interface with @Module doesn't compile

ViewModel:

class FeedDetailsViewModel @AssistedInject constructor(
    @Assisted private val feedId: FeedId,
    private val getFeedUseCase: GetFeedUseCase
) : BaseViewModel() {
    ...
    @AssistedInject.Factory
    interface Factory {
        fun create(feedId: FeedId): FeedDetailsViewModel
    }
}

Generated factory:

public final class FeedDetailsViewModel_AssistedFactory implements FeedDetailsViewModel.Factory {
  private final Provider<GetFeedUseCase> getFeedUseCase;

  @Inject
  public FeedDetailsViewModel_AssistedFactory(Provider<GetFeedUseCase> getFeedUseCase) {
    this.getFeedUseCase = getFeedUseCase;
  }

  @Override
  public FeedDetailsViewModel create(FeedId feedId) {
    return new FeedDetailsViewModel(
        feedId,
        getFeedUseCase.get());
  }
}

Module:

@Module
interface FeedDetailsActivityModuleKotlin {
    @Binds
    fun viewModelFactory(factory: FeedDetailsViewModel_AssistedFactory): FeedDetailsViewModel.Factory
}

Build fails with the following error:

e: /app/build/tmp/kapt3/stubs/debug/../FeedDetailsActivityModuleKotlin.java:11: error: @Binds methods must have only one parameter whose type is assignable to the return type
    public abstract com.nikolaykul.sebastian.presentation.feed.details.FeedDetailsViewModel.Factory viewModelFactory(@org.jetbrains.annotations.NotNull()

A few notes:

  1. Java version of the same Module builds fine:
@Module
public interface FeedDetailsActivityModuleJava {
    @Binds
    FeedDetailsViewModel.Factory viewModuleFactory(FeedDetailsViewModel_AssistedFactory factory);
}
  1. Using @AssistedModule builds fine as well. I guess thats because it generates java-code:
@AssistedModule
@Module(includes = [AssistedInject_ViewModelModule::class])
interface ViewModelModule

How can I fix it? 🤔

Processor claiming arguments not matching when they do in code

Getting this error the first time I compile each time:

e: error: Factory method parameters do not match constructor @Assisted parameters.
  Missing:
   * androidx.lifecycle.SavedStateHandle handle
  Unknown:
   * androidx.lifecycle.SavedStateHandle arg0

The argument name does actually match, and this error goes away upon building a second time. Not sure interaction is causing this.

AssistedInject_ doesn't work java

Build project is a success but AssitedInject class doesn't create and I can't start a project. Please help!

I have separate classes:

  1. module:
    @Module(includes = {AssistedInject_AssistedInjectModule.class}) @AssistedModule public interface AssistedInjectModule { }

  2. Component:
    @Component(modules = {AssistedInjectModule.class, WorkerBindingModule.class}) public interface WorkerComponent { WorkerFactory factory(); }

  3. Child interface:
    public interface ChildWorkerFactory { ListenableWorker create(Context context, WorkerParameters workerParameters); }

  4. Worker binding module:
    @Module public interface WorkerBindingModule { @Binds @IntoMap @WorkerKey(UIUpdaterWorker.class) ChildWorkerFactory bindUIUpdaterWorker(UIUpdaterWorker.Factory factory); }

  5. Worker key:
    @MapKey @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface WorkerKey { Class<? extends ListenableWorker> value(); }

  6. Worker factory:
    public class DaggerWorkerFactory extends WorkerFactory { private Map<Class<? extends ListenableWorker>, Provider<ChildWorkerFactory>> workersFactories; @Inject public DaggerWorkerFactory(Map<Class<? extends ListenableWorker>, Provider<ChildWorkerFactory>> workersFactories) { this.workersFactories = workersFactories; } @Nullable @Override public ListenableWorker createWorker(@NonNull Context appContext, @NonNull String workerClassName, @NonNull WorkerParameters workerParameters) { Provider<ChildWorkerFactory> factoryProvider = CollectionsUtil.getWorkerFactoryProviderByKey(workersFactories, workerClassName); return factoryProvider.get().create(appContext, workerParameters); } }

  7. There is my worker: (DBmodel is injected class from appComponent)
    public class UIUpdaterWorker extends Worker { private DBModel dbModel; String TAG = "FIRST_LAUNCH"; @Inject public UIUpdaterWorker(@Assisted @NonNull Context context,@Assisted @NonNull WorkerParameters workerParams, DBModel dbModel) { super(context, workerParams); this.dbModel = dbModel; } @NonNull @Override public Result doWork() { Log.d(TAG, "Hello world!"); Log.d(TAG, "Injected foo: " + dbModel); return Result.success(); } @AssistedInject.Factory interface Factory extends ChildWorkerFactory{} }

Part in application class:
private void configureWorkManager() { DaggerWorkerFactory workerFactory = new DaggerWorkerComponent.builder().factory(); WorkManager.initialize(this, new Configuration.Builder().setWorkerFactory(workerFactory).build()); }

Part of gradle: (version_work is 1.0.1)
compileOnly "com.squareup.inject:assisted-inject-annotations-dagger2:0.3.2" annotationProcessor 'com.squareup.inject:assisted-inject-processor-dagger2:0.4.0' implementation "android.arch.work:work-runtime:$versions_work" implementation "android.arch.work:work-rxjava2:$versions_work"

@ContributesAndroidInjector, custom scope and @AssistedInject issue

Hello.
I want to use lifecycle-viewmodel-savedstate in my project, so I came across AssistedInject and decided to use it.
But regardless of all my efforts to mix it with my current project stucture I get compiler errors all the time.
Everything compiles and works fine if I don't use @AssistedInject, @AssistedInject.Factory and ViewModelAssistedFactoriesModule.
But as a result I obviously can't use "@assisted SavedStateHandle savedStateHandle" which was the main goal of all this.
So is it possible to use @AssistedInject with all this @ContributesAndroidInjector and @AuthScope stuff?

Here is my simple configuration:

@Singleton
@Component(modules = [AndroidSupportInjectionModule::class, 
			AppModule::class, 
			ActivityBuildersModule::class,
			ViewModelFactoryModule::class, 
			ViewModelAssistedFactoriesModule::class])
interface AppComponent : AndroidInjector<App> {
    @Component.Builder
    interface Builder {
        @BindsInstance
        fun context(context: Context): Builder
        fun build(): AppComponent
    }
}

AppModule provides global @singleton dependencies like, for example, Context and so on.

@Module
internal abstract class ActivityBuildersModule : BaseActivityBuildersModule() {
    @MainScope
    @ContributesAndroidInjector(modules = [MainFragmentBuildersModule::class, MainViewModelsModule::class, MainModule::class])
    internal abstract fun contributeMainActivity(): MainActivity
}

@Module
public abstract class BaseActivityBuildersModule {
    @AuthScope
    @ContributesAndroidInjector(
            modules = {AuthViewModelsModule.class, AuthModule.class} //this is important, because AuthModule provides @AuthScope scoped dependencies
    )
    abstract AuthActivity contributeAuthActivity();
}

@Module
public abstract class AuthViewModelsModule {

    @Binds
    @IntoMap
    @ViewModelKey(AuthActivityVM.class)
    public abstract ViewModelAssistedFactory<? extends ViewModel> bindAuthActivityViewModel(AuthActivityVM.Factory viewModelFactory);

    @Binds
    abstract SavedStateRegistryOwner bindSavedStateRegistryOwner(AuthActivity activity);

    @Nullable
    @Provides
    static Bundle provideDefaultArgs() {
        return null;
    }
}

public interface ViewModelAssistedFactory<T extends ViewModel> {
    T create(SavedStateHandle savedStateHandle);
}
@Module
public class AuthModule {

    @Provides
    @AuthScope
    static LoginValidator provideLoginValidator(){
        return new AuthValidator();
    }
//
//and other @AuthScope things
//
}
@Module
abstract class ViewModelFactoryModule {
    @Binds
    abstract fun bindViewModelFactory(viewModelFactory: ViewModelProviderFactory): ViewModelProvider.Factory
}

@AssistedModule
@Module(includes = {AssistedInject_ViewModelAssistedFactoriesModule.class})
public abstract class ViewModelAssistedFactoriesModule {
}

public class AuthActivityVM extends ViewModel {
    @AssistedInject.Factory
    public interface Factory extends ViewModelAssistedFactory<AuthActivityVM>{}

    @AssistedInject
    //@Inject
    AuthActivityVM(@Assisted SavedStateHandle savedStateHandle,
                   @NonNull Context context, //no complier complains
		   ... //other @Singleton dependencies (also fine)
		   @NonNull LoginValidator loginValidator, **//loginValidator comes from AuthModule and it is scoped with @AuthScpoe** 
                   ...// oter things
		){
	...
}	
}

In AuthActivity I have this bit of code:

    @Inject
    ViewModelProviderFactory providerFactory;

But compiler complains:

com.alidi.base.models.auth.LoginValidator cannot be provided without an @Provides-annotated method.
javax.inject.Provider<com.alidi.base.models.auth.LoginValidator> is injected at
com.alidi.base.ui.auth.AuthActivityVM_AssistedFactory(…, loginValidator, …)

InflationInject for subcomponents and components with component dependencies

I'm trying to figure out how to support views with @InflationInject annotated constructors whose dependencies are provided by subcomponents or components that only have a component dependency on the component that provides the InflationInjectFactory. So basically I need to add view factories to the InflationInjectFactory on the fly but it doesn't seem possible when I also have a single activity app. Is there something I'm missing?

Split generated AssistedInject_*Module to multiple modules

Currently the generated AssistedInject_*Module contains all the bindings for @AssistedInject.Factory annotated factories in one module. As a result, it's impossible to have dependencies that exist only in subcomponents. For example:

I have two ViewModels, where one of them takes an argument (GithubApi in this case), which exists only in one the subcomponent hosting these view models.

class CommitDetailViewModel @AssistedInject constructor(
    @Assisted handle: SavedStateHandle,
    privatel val githubApi: GithubApi // Should be provided only in CommitDetailActivitySubcomponent
) : BaseViewModel(handle) {
    fun loadDetail() {
        Timber.v("Request commit detail")
        // use githubApi here
    }

    @AssistedInject.Factory
    interface Factory : ViewModelAssistedFactory<CommitDetailViewModel>
}

class CommitListViewModel @AssistedInject constructor(
    @Assisted handle: SavedStateHandle
) : BaseViewModel(handle) {

    fun loadCommits() {
        Timber.v("Request commit list")
    }

    @AssistedInject.Factory
    interface Factory : ViewModelAssistedFactory<CommitListViewModel>
}

interface ViewModelAssistedFactory<T : ViewModel> {
    fun create(handle: SavedStateHandle): T
}

@AssistedModule
@Module(includes = AssistedInject_ViewModelAssistedFactoriesModule.class)
public abstract class ViewModelAssistedFactoriesModule {
}

The generated AssistedInject_ViewModelAssistedFactoriesModule contains the bindings for both factories above. Due to that, I have to include it in the ApplicationComponent so it's visible to my subcomponents. But then compilation fails since GithubApi's binding is only defined in one of the subcomponents.

Generated code has a non-deterministic order, which can impact remote cacheability when using Gradle build cache

This issue will be visible when using Gradle's remote build cache. The following screenshots are from Android Studio and a Gradle Enterprise instance used by a team at Comcast, with whom I work (I am a Gradle employee).

The tl;dr is that, given a set of inputs, this annotation processor seems to generate code with non-deterministic output (the order of methods in the generated code is what is non-deterministic). It's a classic indication of HashMap usage (or similar).

File diff (CI vs local dev build):
assistedinject-diff

Gradle Enterprise task inputs comparison showing the file input that was "different" and,
therefore, an issue for the remote build cache:
different-inputs

Gradle Enterprise infrastructure comparison showing the different JDKs in use:
different-jdk

As I said, this is a classic sign of using something like a HashMap as the data structure, because of course it has no defined order, and so different JDKs will have different implementations.

Observation
Generated code has non-deterministic order with respect to JDK in use

Expectation
Generated source code is reproducible no matter where it is generated.

Why you can't have AssistedInject constructor with no non-assisted parameter?

If I have a constructor annotated with AssistedInjection with no non-assisted parameter I get this error:

error("Assisted injection requires at least one non-@assisted parameter.", targetConstructor)

Is this some technical limitation or it's only a rule applied.

I know it doesn't make much sense but what I'm trying to do is to establish some framework to inject ViewModels into fragments. I want the ViewModelProvider Factories (and then ViewModels) have injected by dagger. But usually there is some Assisted parameter so I'm using AssistedInjection Factories to provide that.

There can however be a VM without non-assisted parameters but I still want it to be injected the same way the others are. But with these VMs I'm getting this error.

AssistedInject should take source version into consideration for Generated annotation

AssistedInject searches classpath for @Generated type https://github.com/square/AssistedInject/blob/cf2b4cf76f5769e880966bf215c00eb66c7c9259/assisted-inject-processor/src/main/java/com/squareup/inject/assisted/processor/internal/generatedAnnotation.kt#L29 but it does not take source version into account. This causes issues when both annotations are present in the classpath (e.g. running with JDK11 and having javax.annotation:javax.annotation-api:1.3.2 in the compile classpath).

Doing the same check as auto-common seems like a good solution: https://github.com/google/auto/blob/79c9d15c14a898e1ae2085f265a5396b190d7017/common/src/main/java/com/google/auto/common/GeneratedAnnotations.java#L54

InflationInject doesn't provide the view.

Seems InflationInject only binds the view factory but doesn't provide the view, I ran into a use case that I need to constructor inject a view like:

class ProductView @InflationInject constructor(
    @Assisted context: Context,
    @Assisted attrs: AttributeSet? = null,
    imageLoader: ImageLoader
) : View(context, attrs)

class ProductListView @InflationInject constructor(
    @Assisted context: Context,
    @Assisted attrs: AttributeSet? = null,
    productViewProvider: Provider<ProductView>
) : View(context, attrs)

The build fails, saying "ProductView cannot be provided without an @Inject constructor or an @Provides-annotated method."

Scoped @AssistedModule

Hello,

let's say we have objects with different scopes that are annotated using @AssistedInject. For now we're limited to have one @AssistedModule per module. In that case it force us to split scoped objects into different modules (because of missing binding error in parent component). Are there any plans to change that behavior and allow to have multiple @AssistedModule in one module?

Request not to mandate same names for assisted parameters

Request not to mandate same names for assisted parameters. In my case, changing params to workerParameters fixed it but would be nice not to force same names.

InternalWorkerFactory.java:9: error: Factory method parameters do not match constructor @assisted parameters.
public abstract T create(@org.jetbrains.annotations.NotNull()
^
Missing:

  • androidx.work.WorkerParameters params
    Unknown:
  • androidx.work.WorkerParameters workerParameters

Generation fails when Factory method is in base interface and uses generics

interface BaseThing<T> {
  interface Factory {
    fun create(view: View): BaseThing<T>
  }
}

class StringThing @AssistedInject constructor (
  @Assited view: View
): BaseThing<String> {

  @AssistedInject.Factory
  interface Factory : BaseThing.Factory<String>
}

This will not generate an implementation for the factory, but a generated Dagger 2 module will still try to reference the not generated factory.
Adding override fun create(view: View): BaseThing<String> to the Factory will fix it.

Should this work with dagger and kotlin/kapt?

I'm trying to make AssistedInject work with dagger and kotlin but no success so far.
It seems dagger generates module classes before assisted factories so it ends up with a error NonExistentClass from the factory binding.

@Binds abstract fun bindHomeAssistedInjectFactory(factory: HomeAssistedInject_AssistedFactory): HomeAssistedInject.Factory

generates :
@org.jetbrains.annotations.NotNull() @dagger.Binds() public abstract HomeAssistedInject.Factory bindHomeAssistedInjectFactory(@org.jetbrains.annotations.NotNull() NonExistentClass factory);

I remember trying with google AutoFatory a while ago and ending up with the same pb so i guess this is a kapt problem?

Thx for help!

Dagger error: generated module has different visibility to abstract class

Build log:

DetailsViewModelModule.java:9: error: This module is public, but it includes non-public (or effectively non-public) modules. Either reduce the visibility of this module or make app.tivi.showdetails.AssistedInject_DetailsViewModelModule public.
public abstract class DetailsViewModelModule {
                ^

The module is just a plain Kotlin public class:

@AssistedModule
@Module(includes = [AssistedInject_DetailsViewModelModule::class])
abstract class DetailsViewModelModule

The generated class is pkg-private so I can't see a way to make it work in Kotlin

Make sure R8 can vertically merge factory interface and implementation

Since this is a single interface with a single implementation, R8 should be able to vertically merge these together and eliminate the interface even through Dagger's generated machinery. If it can't, figure out why and forward to the R8 team.

This will only work for assisted injection and not inflation inject since the latter is used in multibindings which requires the interface as an abstraction.

Primitives don't work

class Thing {
  private final String one;
  private final int two;
  private final String three;

  @AssistedInject
  Thing(String one, int two, @Assisted String three) {
    this.one = one;
    this.two = two;
    this.three = three;
  }

  String get() {
    return one + two + three;
  }

  @AssistedInject.Factory
  interface Factory {
    Thing create(String three);
  }
}
Caused by: java.lang.IllegalArgumentException: invalid type parameter: int
        at com.squareup.javapoet.Util.checkArgument(Util.java:53)
        at com.squareup.javapoet.ParameterizedTypeName.<init>(ParameterizedTypeName.java:51)
        at com.squareup.javapoet.ParameterizedTypeName.<init>(ParameterizedTypeName.java:38)
        at com.squareup.javapoet.ParameterizedTypeName.get(ParameterizedTypeName.java:114)
        at com.squareup.inject.assisted.processor.AssistedInjectionKt.getProviderType(AssistedInjection.kt:101)
        at com.squareup.inject.assisted.processor.AssistedInjectionKt.access$getProviderType$p(AssistedInjection.kt:1)
        at com.squareup.inject.assisted.processor.AssistedInjection.brewJava(AssistedInjection.kt:61)
        at com.squareup.inject.assisted.processor.AssistedInjectProcessor.writeAssistedInject(AssistedInjectProcessor.kt:240)
        at com.squareup.inject.assisted.processor.AssistedInjectProcessor.access$writeAssistedInject(AssistedInjectProcessor.kt:46)
        at com.squareup.inject.assisted.processor.AssistedInjectProcessor$process$3.invoke(AssistedInjectProcessor.kt:65)
        at com.squareup.inject.assisted.processor.AssistedInjectProcessor$process$3.invoke(AssistedInjectProcessor.kt:46)
        at com.squareup.inject.assisted.processor.AssistedInjectProcessor$sam$java_util_function_BiConsumer$0.accept(AssistedInjectProcessor.kt)
        at com.squareup.inject.assisted.processor.AssistedInjectProcessor.process(AssistedInjectProcessor.kt:65)
        at org.gradle.api.internal.tasks.compile.processing.DelegatingProcessor.process(DelegatingProcessor.java:62)
        at org.gradle.api.internal.tasks.compile.processing.NonIncrementalProcessor.process(NonIncrementalProcessor.java:45)
        at jdk.compiler/com.sun.tools.javac.processing.JavacProcessingEnvironment.callProcessor(JavacProcessingEnvironment.java:964)
        at jdk.compiler/com.sun.tools.javac.processing.JavacProcessingEnvironment.discoverAndRunProcs(JavacProcessingEnvironment.java:881)
        at jdk.compiler/com.sun.tools.javac.processing.JavacProcessingEnvironment.access$2100(JavacProcessingEnvironment.java:110)
        at jdk.compiler/com.sun.tools.javac.processing.JavacProcessingEnvironment$Round.run(JavacProcessingEnvironment.java:1202)
        at jdk.compiler/com.sun.tools.javac.processing.JavacProcessingEnvironment.doProcessing(JavacProcessingEnvironment.java:1311)
        at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.processAnnotations(JavaCompiler.java:1250)
        at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:928)
        at jdk.compiler/com.sun.tools.javac.api.JavacTaskImpl.lambda$doCall$0(JavacTaskImpl.java:100)
        at jdk.compiler/com.sun.tools.javac.api.JavacTaskImpl.handleExceptions(JavacTaskImpl.java:142)
        ... 65 more

Unexpected error when 2 provided args of same type are present

Code similar to this:

class MyClass @AssistedInject constructor(
    foobar: Foobar,
    @Assisted a: String,
    @Assisted b: String
) {
    @AssistedInject.Factory
    interface Factory {
        fun create(a: String, b: String): MyClass
    }
}

Results in:

Duplicate @Assisted parameters declared. Forget a qualifier annotation?

Since @Assisted args are supplied by the caller, it seems duplicates should be permitted. Am I missing something? Is this an annotation processing constraint?

I've solved the problem by adding qualifying annotations to both the MyClass constructor declaration and the factory function.

AssistedInject_MyModule not being generated.

I was trying to create an assisted module for my viewmodel but it doesn't generate. This is my viewmodel:

class GalleryViewModel @AssistedInject constructor(
    val galleryDataSourceFactory: GalleryDataSourceFactory
) : ViewModel() {

    @AssistedInject.Factory
    interface Factory {
        fun create(): GalleryViewModel
    }
}

And it compiles with no errors. Perhaps i should include the generated factory:

public final class GalleryViewModel_Factory implements Factory<GalleryViewModel> {
  private final Provider<GalleryDataSourceFactory> galleryDataSourceFactoryProvider;

  public GalleryViewModel_Factory(
      Provider<GalleryDataSourceFactory> galleryDataSourceFactoryProvider) {
    this.galleryDataSourceFactoryProvider = galleryDataSourceFactoryProvider;
  }

  @Override
  public GalleryViewModel get() {
    return new GalleryViewModel(galleryDataSourceFactoryProvider.get());
  }

  public static GalleryViewModel_Factory create(
      Provider<GalleryDataSourceFactory> galleryDataSourceFactoryProvider) {
    return new GalleryViewModel_Factory(galleryDataSourceFactoryProvider);
  }

  public static GalleryViewModel newInstance(GalleryDataSourceFactory galleryDataSourceFactory) {
    return new GalleryViewModel(galleryDataSourceFactory);
  }
}

Last, when I try the @AssistedModule, I can't find my assistedmodule:

@AssistedModule
@Module(includes = [AssistedInject_ViewModelModule::class]) //not generating
abstract class ViewModelModule

I'm using latest version of the lib

Error: Multiple @AssistedModule-annotated modules found

Annotating multiple modules with @AssistedModule results in error: Multiple @AssistedModule-annotated modules found. After watching your talk from DroidCon UK '18, where you mention that there should be one (and only one?) module that is annotated with @AssistedModule in the whole project, it all became clear to me and I managed to fix this error eventually.

At least in the current version of the library, this rule isn't mentioned (or at least unclear) in the README.md and/or the sample. Even though this behavior might change in the future, and it would be allowed to annotate multiple modules, I think it's still worth mentioning it - at least for the current behavior of the library.

Possibility for @FragmentInject?

The androidx.fragment library recently added support for supplying a custom FragmentFactory to handle creation (and re-creation) of Fragments. This opens a potential possibility for using Dagger to handle the creation and injection of your Fragments -- potentially supplying the arguments as @Assisted similarly to how @InflationInject is working.

Is this something you would consider adding to the library or do you think it would be better as a separate library?

Possible solution for Arch ViewModel dynamic parameters

After seeing Jake's presentation on this library at DroidCon, I was hoping that the proposed solution will solve the problem of passing dynamic parameters to Android Architecture Components ViewModel that is discussed on several places over the internet, originally probably here android/architecture-components-samples#207

Since ViewModel shouldn't be injected directly to a client as Presenter is in the examples of this library and Jake's talk, all I could come up with was double factories, the assisted factory for creating ViewModelFactory and then then ViewModelFactory to have the system lazily create the ViewModel.

Can someone see a better solution or would it require a specific fork of this library to auto generate this extra ViewModelFactory?

Ideally I would see the same solution as in Jake's presentation. No manual creation of ViewModel and no manual parameter passing, only the dynamic params to Factory create method.

My example:
The ViewModelFactory and "ViewModelFactoryFactory"

class WordDetailViewModelFactory @AssistedInject constructor(
    private val repository: WordRepository,
    @Assisted private val argument: WordDetailScreenArg
) : ViewModelProvider.Factory {

    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        return WordDetailViewModel(repository, argument) as T
    }

    @AssistedInject.Factory
    interface Factory {
        fun create(argument: WordDetailScreenArg): WordDetailViewModelFactory
    }
}

The ViewModel

class WordDetailViewModel(
    private val repository: WordRepository,
    argument: WordDetailScreenArg
) : ViewModel() {

    val wordDetail: LiveData<Word> = repository.word(argument.word)

    fun onDeleteWordClicked() {
        wordDetail.value?.let {
            repository.deleteWord(it.word)
        }
    }
}

Injection

@Inject
lateinit var viewModelFactory: WordDetailViewModelFactory.Factory;

override fun onAttach(context: Context?) {
    super.onAttach(context)

    val arguments: WordDetailScreenArg = ...
    Injection.appComponent.inject(this)

    viewModel = ViewModelProviders.of(this, viewModelFactory.create(arguments))[WordDetailViewModel::class.java]

}

Edit: looks like @hansenji has a solution at https://github.com/hansenji/ViewModelInject

Nested AssistedModule compilation fails with missing include error

Given the following module:

class Outer {
  @AssistedModule
  @Module(includes = AssistedInject_TestModule.class)
  public abstract class TestModule {}
}

Compilation fails with:

error: @AssistedModule's @Module must include AssistedInject_TestModule

I am not certain if nested module not being allowed is intentional or not but it seems it should either be allowed or have a specific error as output.

I have a failing unit test and will open a PR with that once I have this issue link.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.