Implement Worker injection code generation

Similar to ViewModels, Worker are assisted injected and a Hilt
module for the multibinding map of assisted factories is generated.
To reused code assisted factory generator was created to be used by
both ViewModel and Worker generators.

This CL also adds a small integration app to demonstrate usage and to
further write tests.

A follow-up CL will contain validation.

Test: hilt-compiler:test
Change-Id: I6484aecb03b6e2877020755e8d280fd0f42c9df5
diff --git a/hilt/compiler/build.gradle b/hilt/compiler/build.gradle
index 11d4a47..e5d80e0 100644
--- a/hilt/compiler/build.gradle
+++ b/hilt/compiler/build.gradle
@@ -19,6 +19,8 @@
 import androidx.build.LibraryGroups
 import androidx.build.LibraryVersions
 import androidx.build.AndroidXExtension
+import androidx.build.SdkHelperKt
+import androidx.build.SupportConfig
 
 import static androidx.build.dependencies.DependenciesKt.*
 import androidx.build.Publish
@@ -43,13 +45,20 @@
     testImplementation(JUNIT)
     testImplementation(TRUTH)
     testImplementation(GOOGLE_COMPILE_TESTING)
+    testImplementation(HILT_ANDROID)
     testImplementation fileTree(
             dir: "${new File(project(":hilt:hilt-lifecycle-viewmodel").buildDir, "libJar")}",
             include : "*.jar")
-    testImplementation(HILT_ANDROID)
+    testImplementation fileTree(
+            dir: "${new File(project(":hilt:hilt-work").buildDir, "libJar")}",
+            include : "*.jar")
+    testImplementation fileTree(
+            dir: "${SdkHelperKt.getSdkPath(project)}/platforms/$SupportConfig.COMPILE_SDK_VERSION/",
+            include : "android.jar")
 }
 
 tasks.findByName("compileKotlin").dependsOn(":hilt:hilt-lifecycle-viewmodel:jarDebug")
+tasks.findByName("compileKotlin").dependsOn(":hilt:hilt-work:jarDebug")
 
 androidx {
     name = "AndroidX Hilt Extension Compiler"
diff --git a/hilt/compiler/src/main/kotlin/androidx/hilt/ClassNames.kt b/hilt/compiler/src/main/kotlin/androidx/hilt/ClassNames.kt
index 2499be3..e3f089d 100644
--- a/hilt/compiler/src/main/kotlin/androidx/hilt/ClassNames.kt
+++ b/hilt/compiler/src/main/kotlin/androidx/hilt/ClassNames.kt
@@ -21,7 +21,10 @@
 internal object ClassNames {
     val ACTIVITY_RETAINED_COMPONENT =
         ClassName.get("dagger.hilt.android.components", "ActivityRetainedComponent")
+    val APPLICATION_COMPONENT =
+        ClassName.get("dagger.hilt.android.components", "ApplicationComponent")
     val BINDS = ClassName.get("dagger", "Binds")
+    val CONTEXT = ClassName.get("android.content", "Context")
     val NON_NULL = ClassName.get("androidx.annotation", "NonNull")
     val INJECT = ClassName.get("javax.inject", "Inject")
     val INSTALL_IN = ClassName.get("dagger.hilt", "InstallIn")
@@ -33,4 +36,7 @@
     val VIEW_MODEL = ClassName.get("androidx.lifecycle", "ViewModel")
     val SAVED_STATE_HANDLE = ClassName.get("androidx.lifecycle", "SavedStateHandle")
     val STRING_KEY = ClassName.get("dagger.multibindings", "StringKey")
+    val WORKER = ClassName.get("androidx.work", "Worker")
+    val WORKER_ASSISTED_FACTORY = ClassName.get("androidx.hilt.work", "WorkerAssistedFactory")
+    val WORKER_PARAMETERS = ClassName.get("androidx.work", "WorkerParameters")
 }
diff --git a/hilt/compiler/src/main/kotlin/androidx/hilt/assisted/AssistedFactoryGenerator.kt b/hilt/compiler/src/main/kotlin/androidx/hilt/assisted/AssistedFactoryGenerator.kt
new file mode 100644
index 0000000..34de592
--- /dev/null
+++ b/hilt/compiler/src/main/kotlin/androidx/hilt/assisted/AssistedFactoryGenerator.kt
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * 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 androidx.hilt.assisted
+
+import androidx.hilt.ClassNames
+import androidx.hilt.ext.L
+import androidx.hilt.ext.T
+import androidx.hilt.ext.W
+import androidx.hilt.ext.addGeneratedAnnotation
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.CodeBlock
+import com.squareup.javapoet.FieldSpec
+import com.squareup.javapoet.JavaFile
+import com.squareup.javapoet.MethodSpec
+import com.squareup.javapoet.ParameterSpec
+import com.squareup.javapoet.ParameterizedTypeName
+import com.squareup.javapoet.TypeName
+import com.squareup.javapoet.TypeSpec
+import javax.annotation.processing.ProcessingEnvironment
+import javax.lang.model.element.Modifier
+import javax.lang.model.element.TypeElement
+import javax.lang.model.util.ElementFilter
+
+/**
+ * Source generator for assisted factories.
+ */
+internal class AssistedFactoryGenerator(
+    private val processingEnv: ProcessingEnvironment,
+    private val productClassName: ClassName,
+    private val factoryClassName: ClassName,
+    private val factorySuperTypeName: ParameterizedTypeName,
+    private val originatingElement: TypeElement,
+    private val dependencyRequests: List<DependencyRequest>
+) {
+
+    fun generate() {
+        val factoryTypeSpec = TypeSpec.classBuilder(factoryClassName)
+            .addOriginatingElement(originatingElement)
+            .addSuperinterface(factorySuperTypeName)
+            .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
+            .addGeneratedAnnotation(processingEnv.elementUtils, processingEnv.sourceVersion)
+            .addFields(getFieldSpecs())
+            .addMethod(getConstructorMethodSpec())
+            .addMethod(getCreateMethodSpec())
+            .build()
+        JavaFile.builder(factoryClassName.packageName(), factoryTypeSpec)
+            .build()
+            .writeTo(processingEnv.filer)
+    }
+
+    private fun getFieldSpecs() = dependencyRequests
+        .filterNot { it.isAssisted }
+        .map { dependencyRequest ->
+            val fieldTypeName = dependencyRequest.providerTypeName.withoutAnnotations()
+            FieldSpec.builder(fieldTypeName, dependencyRequest.name)
+                .addModifiers(Modifier.PRIVATE, Modifier.FINAL)
+                .build()
+        }
+
+    private fun getConstructorMethodSpec() =
+        MethodSpec.constructorBuilder()
+            .addAnnotation(ClassNames.INJECT)
+            .apply {
+                dependencyRequests
+                    .filterNot { it.isAssisted }
+                    .forEach { dependencyRequest ->
+                        addParameter(dependencyRequest.providerTypeName, dependencyRequest.name)
+                        addStatement("this.$1N = $1N", dependencyRequest.name)
+                    }
+            }
+            .build()
+
+    private fun getCreateMethodSpec(): MethodSpec {
+        val factoryTypeElement =
+            processingEnv.elementUtils.getTypeElement(factorySuperTypeName.rawType.canonicalName())
+        val factoryMethod = ElementFilter.methodsIn(factoryTypeElement.enclosedElements).first()
+        val parameterSpecs = factoryMethod.parameters.map { ParameterSpec.get(it) }
+        val constructorArgs = dependencyRequests.map {
+            val paramLiteral = when {
+                it.isAssisted -> {
+                    factoryMethod.parameters.first { param ->
+                        TypeName.get(param.asType()) == it.type
+                    }.simpleName.toString()
+                }
+                it.isProvider -> it.name
+                else -> "${it.name}.get()"
+            }
+            CodeBlock.of(L, paramLiteral)
+        }
+        return MethodSpec.methodBuilder(factoryMethod.simpleName.toString())
+            .addAnnotation(Override::class.java)
+            .addAnnotation(ClassNames.NON_NULL)
+            .addModifiers(Modifier.PUBLIC)
+            .returns(productClassName)
+            .addParameters(parameterSpecs)
+            .addStatement("return new $T($L)",
+                productClassName, CodeBlock.join(constructorArgs, ",$W"))
+            .build()
+    }
+}
\ No newline at end of file
diff --git a/hilt/compiler/src/main/kotlin/androidx/hilt/assisted/DependencyRequest.kt b/hilt/compiler/src/main/kotlin/androidx/hilt/assisted/DependencyRequest.kt
new file mode 100644
index 0000000..e0f6646
--- /dev/null
+++ b/hilt/compiler/src/main/kotlin/androidx/hilt/assisted/DependencyRequest.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * 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 androidx.hilt.assisted
+
+import androidx.hilt.ClassNames
+import androidx.hilt.ext.hasAnnotation
+import com.squareup.javapoet.AnnotationSpec
+import com.squareup.javapoet.ParameterizedTypeName
+import com.squareup.javapoet.TypeName
+import javax.lang.model.element.VariableElement
+
+/**
+ * Data class that represents a binding request for an assisted injected type.
+ */
+internal data class DependencyRequest(
+    val name: String,
+    val type: TypeName,
+    val isAssisted: Boolean,
+    val qualifier: AnnotationSpec? = null
+) {
+    val isProvider = type is ParameterizedTypeName && type.rawType == ClassNames.PROVIDER
+
+    val providerTypeName: TypeName = let {
+        val type = if (isProvider) {
+            type // Do not wrap a Provider inside another Provider.
+        } else {
+            ParameterizedTypeName.get(
+                ClassNames.PROVIDER,
+                type.box()
+            )
+        }
+        if (qualifier != null) {
+            type.annotated(qualifier)
+        } else {
+            type
+        }
+    }
+}
+
+internal fun VariableElement.toDependencyRequest(
+    isAssistedPredicate: (TypeName) -> Boolean
+): DependencyRequest {
+    val qualifier = annotationMirrors.find {
+        it.annotationType.asElement().hasAnnotation("javax.inject.Qualifier")
+    }?.let { AnnotationSpec.get(it) }
+    val type = TypeName.get(asType())
+    return DependencyRequest(
+        name = simpleName.toString(),
+        type = type,
+        isAssisted = isAssistedPredicate(type) && qualifier == null,
+        qualifier = qualifier
+    )
+}
\ No newline at end of file
diff --git a/hilt/compiler/src/main/kotlin/androidx/hilt/lifecycle/ViewModelGenerator.kt b/hilt/compiler/src/main/kotlin/androidx/hilt/lifecycle/ViewModelGenerator.kt
index a130d59..e72b5b8 100644
--- a/hilt/compiler/src/main/kotlin/androidx/hilt/lifecycle/ViewModelGenerator.kt
+++ b/hilt/compiler/src/main/kotlin/androidx/hilt/lifecycle/ViewModelGenerator.kt
@@ -17,17 +17,13 @@
 package androidx.hilt.lifecycle
 
 import androidx.hilt.ClassNames
-import androidx.hilt.ext.L
+import androidx.hilt.assisted.AssistedFactoryGenerator
 import androidx.hilt.ext.S
 import androidx.hilt.ext.T
-import androidx.hilt.ext.W
 import androidx.hilt.ext.addGeneratedAnnotation
 import com.squareup.javapoet.AnnotationSpec
-import com.squareup.javapoet.CodeBlock
-import com.squareup.javapoet.FieldSpec
 import com.squareup.javapoet.JavaFile
 import com.squareup.javapoet.MethodSpec
-import com.squareup.javapoet.ParameterSpec
 import com.squareup.javapoet.ParameterizedTypeName
 import com.squareup.javapoet.TypeSpec
 import com.squareup.javapoet.WildcardTypeName
@@ -35,7 +31,7 @@
 import javax.lang.model.element.Modifier
 
 /**
- * Source generator to support Hilt injection of ViewModels.
+ * Source generator to support Hilt injection of Workers.
  *
  * Should generate:
  * ```
@@ -76,18 +72,14 @@
     private val injectedViewModel: ViewModelInjectElements
 ) {
     fun generate() {
-        val factoryTypeSpec = TypeSpec.classBuilder(injectedViewModel.factoryClassName)
-            .addOriginatingElement(injectedViewModel.typeElement)
-            .addSuperinterface(injectedViewModel.factorySuperTypeName)
-            .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
-            .addGeneratedAnnotation(processingEnv.elementUtils, processingEnv.sourceVersion)
-            .addFields(getFieldSpecs())
-            .addMethod(getConstructorMethodSpec())
-            .addMethod(getCreateMethodSpec())
-            .build()
-        JavaFile.builder(injectedViewModel.factoryClassName.packageName(), factoryTypeSpec)
-            .build()
-            .writeTo(processingEnv.filer)
+        AssistedFactoryGenerator(
+            processingEnv = processingEnv,
+            productClassName = injectedViewModel.className,
+            factoryClassName = injectedViewModel.factoryClassName,
+            factorySuperTypeName = injectedViewModel.factorySuperTypeName,
+            originatingElement = injectedViewModel.typeElement,
+            dependencyRequests = injectedViewModel.dependencyRequests
+        ).generate()
 
         val hiltModuleTypeSpec = TypeSpec.interfaceBuilder(injectedViewModel.moduleClassName)
             .addOriginatingElement(injectedViewModel.typeElement)
@@ -118,52 +110,4 @@
             .build()
             .writeTo(processingEnv.filer)
     }
-
-    private fun getFieldSpecs() = injectedViewModel.dependencyRequests
-        .filterNot { it.isSavedStateHandle }
-        .map { dependencyRequest ->
-            val fieldTypeName = dependencyRequest.providerTypeName.withoutAnnotations()
-            FieldSpec.builder(fieldTypeName, dependencyRequest.name)
-                .addModifiers(Modifier.PRIVATE, Modifier.FINAL)
-                .build()
-        }
-
-    private fun getConstructorMethodSpec() =
-        MethodSpec.constructorBuilder()
-            .addAnnotation(ClassNames.INJECT)
-            .apply {
-                injectedViewModel.dependencyRequests
-                    .filterNot { it.isSavedStateHandle }
-                    .forEach { dependencyRequest ->
-                        addParameter(dependencyRequest.providerTypeName, dependencyRequest.name)
-                        addStatement("this.$1N = $1N", dependencyRequest.name)
-                }
-            }
-            .build()
-
-    private fun getCreateMethodSpec(): MethodSpec {
-        val constructorArgs = injectedViewModel.dependencyRequests.map { dependencyRequest ->
-            val paramLiteral = when {
-                dependencyRequest.isSavedStateHandle -> "handle"
-                dependencyRequest.isProvider -> dependencyRequest.name
-                else -> "${dependencyRequest.name}.get()"
-            }
-            CodeBlock.of(L, paramLiteral)
-        }
-        return MethodSpec.methodBuilder("create")
-            .addAnnotation(Override::class.java)
-            .addAnnotation(ClassNames.NON_NULL)
-            .addModifiers(Modifier.PUBLIC)
-            .returns(injectedViewModel.className)
-            .addParameter(
-                ParameterSpec.builder(ClassNames.SAVED_STATE_HANDLE, "handle")
-                    .addAnnotation(ClassNames.NON_NULL)
-                    .build())
-            .addStatement("return new $T($L)",
-                injectedViewModel.className, CodeBlock.join(constructorArgs, ",$W"))
-            .build()
-    }
-}
-
-internal val DependencyRequest.isSavedStateHandle: Boolean
-    get() = type == ClassNames.SAVED_STATE_HANDLE && qualifier == null
+}
\ No newline at end of file
diff --git a/hilt/compiler/src/main/kotlin/androidx/hilt/lifecycle/ViewModelInjectElements.kt b/hilt/compiler/src/main/kotlin/androidx/hilt/lifecycle/ViewModelInjectElements.kt
index 8bba060..4f41dab 100644
--- a/hilt/compiler/src/main/kotlin/androidx/hilt/lifecycle/ViewModelInjectElements.kt
+++ b/hilt/compiler/src/main/kotlin/androidx/hilt/lifecycle/ViewModelInjectElements.kt
@@ -17,15 +17,12 @@
 package androidx.hilt.lifecycle
 
 import androidx.hilt.ClassNames
-import androidx.hilt.ext.hasAnnotation
+import androidx.hilt.assisted.toDependencyRequest
 import com.google.auto.common.MoreElements
-import com.squareup.javapoet.AnnotationSpec
 import com.squareup.javapoet.ClassName
 import com.squareup.javapoet.ParameterizedTypeName
-import com.squareup.javapoet.TypeName
 import javax.lang.model.element.ExecutableElement
 import javax.lang.model.element.TypeElement
-import javax.lang.model.element.VariableElement
 
 /**
  * Data class that represents a Hilt injected ViewModel
@@ -48,38 +45,9 @@
         MoreElements.getPackage(typeElement).qualifiedName.toString(),
         "${className.simpleNames().joinToString("_")}_HiltModule")
 
-    val dependencyRequests = constructorElement.parameters.map { it.toDependencyRequest() }
-}
-
-/**
- * Data class that represents a binding request from the injected ViewModel
- */
-internal data class DependencyRequest(
-    val name: String,
-    val type: TypeName,
-    val qualifier: AnnotationSpec? = null
-) {
-    val isProvider = type is ParameterizedTypeName && type.rawType == ClassNames.PROVIDER
-
-    val providerTypeName: TypeName = let {
-        val type = if (isProvider) {
-            type // Do not wrap a Provider inside another Provider.
-        } else {
-            ParameterizedTypeName.get(ClassNames.PROVIDER, type.box())
-        }
-        if (qualifier != null) {
-            type.annotated(qualifier)
-        } else {
-            type
+    val dependencyRequests = constructorElement.parameters.map { constructorArg ->
+        constructorArg.toDependencyRequest { paramTypeName ->
+            paramTypeName == ClassNames.SAVED_STATE_HANDLE
         }
     }
 }
-
-internal fun VariableElement.toDependencyRequest() =
-    DependencyRequest(
-        name = simpleName.toString(),
-        type = TypeName.get(asType()),
-        qualifier = annotationMirrors.find {
-            it.annotationType.asElement().hasAnnotation("javax.inject.Qualifier")
-        }?.let { AnnotationSpec.get(it) }
-    )
\ No newline at end of file
diff --git a/hilt/compiler/src/main/kotlin/androidx/hilt/work/WorkerGenerator.kt b/hilt/compiler/src/main/kotlin/androidx/hilt/work/WorkerGenerator.kt
new file mode 100644
index 0000000..0499815
--- /dev/null
+++ b/hilt/compiler/src/main/kotlin/androidx/hilt/work/WorkerGenerator.kt
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * 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 androidx.hilt.work
+
+import androidx.hilt.ClassNames
+import androidx.hilt.assisted.AssistedFactoryGenerator
+import androidx.hilt.ext.S
+import androidx.hilt.ext.T
+import androidx.hilt.ext.addGeneratedAnnotation
+import com.squareup.javapoet.AnnotationSpec
+import com.squareup.javapoet.JavaFile
+import com.squareup.javapoet.MethodSpec
+import com.squareup.javapoet.ParameterizedTypeName
+import com.squareup.javapoet.TypeSpec
+import com.squareup.javapoet.WildcardTypeName
+import javax.annotation.processing.ProcessingEnvironment
+import javax.lang.model.element.Modifier
+
+/**
+ * Source generator to support Hilt injection of Workers.
+ *
+ * Should generate:
+ * ```
+ * @Module
+ * @InstallIn(ApplicationComponent.class)
+ * public interface $_HiltModule {
+ *   @Binds
+ *   @IntoMap
+ *   @StringKey("pkg.$")
+ *   ViewModelAssistedFactory<? extends Worker> bind($_AssistedFactory factory)
+ * }
+ * ```
+ * and
+ * ```
+ * public final class $_AssistedFactory extends WorkerAssistedFactory<$> {
+ *
+ *   private final Provider<Dep1> dep1;
+ *   private final Provider<Dep2> dep2;
+ *   ...
+ *
+ *   @Inject
+ *   $_AssistedFactory(Provider<Dep1> dep1, Provider<Dep2> dep2, ...) {
+ *     this.dep1 = dep1;
+ *     this.dep2 = dep2;
+ *     ...
+ *   }
+ *
+ *   @Override
+ *   @NonNull
+ *   public $ create(@NonNull Context context, @NonNull WorkerParameter params) {
+ *     return new $(context, params, dep1.get(), dep2.get());
+ *   }
+ * }
+ * ```
+ */
+internal class WorkerGenerator(
+    private val processingEnv: ProcessingEnvironment,
+    private val injectedWorker: WorkerInjectElements
+) {
+    fun generate() {
+        AssistedFactoryGenerator(
+            processingEnv = processingEnv,
+            productClassName = injectedWorker.className,
+            factoryClassName = injectedWorker.factoryClassName,
+            factorySuperTypeName = injectedWorker.factorySuperTypeName,
+            originatingElement = injectedWorker.typeElement,
+            dependencyRequests = injectedWorker.dependencyRequests
+        ).generate()
+
+        val hiltModuleTypeSpec = TypeSpec.interfaceBuilder(injectedWorker.moduleClassName)
+            .addOriginatingElement(injectedWorker.typeElement)
+            .addGeneratedAnnotation(processingEnv.elementUtils, processingEnv.sourceVersion)
+            .addAnnotation(ClassNames.MODULE)
+            .addAnnotation(
+                AnnotationSpec.builder(ClassNames.INSTALL_IN)
+                    .addMember("value", "$T.class", ClassNames.APPLICATION_COMPONENT)
+                    .build())
+            .addModifiers(Modifier.PUBLIC)
+            .addMethod(
+                MethodSpec.methodBuilder("bind")
+                    .addAnnotation(ClassNames.BINDS)
+                    .addAnnotation(ClassNames.INTO_MAP)
+                    .addAnnotation(
+                        AnnotationSpec.builder(ClassNames.STRING_KEY)
+                            .addMember("value", S, injectedWorker.className.canonicalName())
+                            .build())
+                    .addModifiers(Modifier.ABSTRACT, Modifier.PUBLIC)
+                    .returns(
+                        ParameterizedTypeName.get(
+                            ClassNames.WORKER_ASSISTED_FACTORY,
+                            WildcardTypeName.subtypeOf(ClassNames.WORKER)))
+                    .addParameter(injectedWorker.factoryClassName, "factory")
+                    .build())
+            .build()
+        JavaFile.builder(injectedWorker.moduleClassName.packageName(), hiltModuleTypeSpec)
+            .build()
+            .writeTo(processingEnv.filer)
+    }
+}
diff --git a/hilt/compiler/src/main/kotlin/androidx/hilt/work/WorkerInjectElements.kt b/hilt/compiler/src/main/kotlin/androidx/hilt/work/WorkerInjectElements.kt
new file mode 100644
index 0000000..5bd7258
--- /dev/null
+++ b/hilt/compiler/src/main/kotlin/androidx/hilt/work/WorkerInjectElements.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * 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 androidx.hilt.work
+
+import androidx.hilt.ClassNames
+import androidx.hilt.assisted.toDependencyRequest
+import com.google.auto.common.MoreElements
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.ParameterizedTypeName
+import javax.lang.model.element.ExecutableElement
+import javax.lang.model.element.TypeElement
+
+/**
+ * Data class that represents a Hilt injected Worker
+ */
+internal data class WorkerInjectElements(
+    val typeElement: TypeElement,
+    val constructorElement: ExecutableElement
+) {
+    val className = ClassName.get(typeElement)
+
+    val factoryClassName = ClassName.get(
+        MoreElements.getPackage(typeElement).qualifiedName.toString(),
+        "${className.simpleNames().joinToString("_")}_AssistedFactory")
+
+    val factorySuperTypeName = ParameterizedTypeName.get(
+        ClassNames.WORKER_ASSISTED_FACTORY,
+        className)
+
+    val moduleClassName = ClassName.get(
+        MoreElements.getPackage(typeElement).qualifiedName.toString(),
+        "${className.simpleNames().joinToString("_")}_HiltModule")
+
+    val dependencyRequests = constructorElement.parameters.map { constructorArg ->
+        constructorArg.toDependencyRequest { paramTypeName ->
+            listOf(ClassNames.CONTEXT, ClassNames.WORKER_PARAMETERS).any { paramTypeName == it }
+        }
+    }
+}
\ No newline at end of file
diff --git a/hilt/compiler/src/main/kotlin/androidx/hilt/work/WorkerInjectStep.kt b/hilt/compiler/src/main/kotlin/androidx/hilt/work/WorkerInjectStep.kt
index 3f1b0e5..18046d0 100644
--- a/hilt/compiler/src/main/kotlin/androidx/hilt/work/WorkerInjectStep.kt
+++ b/hilt/compiler/src/main/kotlin/androidx/hilt/work/WorkerInjectStep.kt
@@ -17,10 +17,16 @@
 package androidx.hilt.work
 
 import com.google.auto.common.BasicAnnotationProcessor
+import com.google.auto.common.MoreElements
 import com.google.common.collect.SetMultimap
 import javax.annotation.processing.ProcessingEnvironment
 import javax.lang.model.element.Element
+import javax.lang.model.element.ExecutableElement
+import javax.lang.model.element.TypeElement
 
+/**
+ * Processing step that generates code enabling assisted injection of Workers using Hilt.
+ */
 class WorkerInjectStep(
     private val processingEnv: ProcessingEnvironment
 ) : BasicAnnotationProcessor.ProcessingStep {
@@ -30,7 +36,29 @@
     override fun process(
         elementsByAnnotation: SetMultimap<Class<out Annotation>, Element>
     ): MutableSet<out Element> {
-        // TODO(danysantiago): Implement me plz!
+        val parsedElements = mutableSetOf<TypeElement>()
+        elementsByAnnotation[WorkerInject::class.java].forEach { element ->
+            val constructorElement =
+                MoreElements.asExecutable(element)
+            val typeElement =
+                MoreElements.asType(constructorElement.enclosingElement)
+            if (parsedElements.add(typeElement)) {
+                parse(typeElement, constructorElement)?.let { worker ->
+                    WorkerGenerator(
+                        processingEnv,
+                        worker
+                    ).generate()
+                }
+            }
+        }
         return mutableSetOf()
     }
+
+    private fun parse(
+        typeElement: TypeElement,
+        constructorElement: ExecutableElement
+    ): WorkerInjectElements? {
+        // TODO(danysantiago): Validate Worker
+        return WorkerInjectElements(typeElement, constructorElement)
+    }
 }
\ No newline at end of file
diff --git a/hilt/compiler/src/test/data/sources/Worker.java b/hilt/compiler/src/test/data/sources/Worker.java
new file mode 100644
index 0000000..78ac024
--- /dev/null
+++ b/hilt/compiler/src/test/data/sources/Worker.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * 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 androidx.work;
+
+import android.content.Context;
+
+public abstract class Worker {
+
+    public Worker(Context context, WorkerParameters workerParams) {
+
+    }
+}
diff --git a/hilt/compiler/src/test/data/sources/WorkerParameters.java b/hilt/compiler/src/test/data/sources/WorkerParameters.java
new file mode 100644
index 0000000..480f9f2
--- /dev/null
+++ b/hilt/compiler/src/test/data/sources/WorkerParameters.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * 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 androidx.work;
+
+public class WorkerParameters {
+
+}
diff --git a/hilt/compiler/src/test/kotlin/androidx/hilt/lifecycle/ViewModelGeneratorTest.kt b/hilt/compiler/src/test/kotlin/androidx/hilt/lifecycle/ViewModelGeneratorTest.kt
index 1d8c670..6e9f67b 100644
--- a/hilt/compiler/src/test/kotlin/androidx/hilt/lifecycle/ViewModelGeneratorTest.kt
+++ b/hilt/compiler/src/test/kotlin/androidx/hilt/lifecycle/ViewModelGeneratorTest.kt
@@ -16,6 +16,8 @@
 
 package androidx.hilt.lifecycle
 
+import androidx.hilt.GENERATED_ANNOTATION
+import androidx.hilt.GENERATED_TYPE
 import androidx.hilt.Sources
 import androidx.hilt.compiler
 import androidx.hilt.toJFO
@@ -27,16 +29,6 @@
 @RunWith(JUnit4::class)
 class ViewModelGeneratorTest {
 
-    private val GENERATED_TYPE = try {
-        Class.forName("javax.annotation.processing.Generated")
-        "javax.annotation.processing.Generated"
-    } catch (_: ClassNotFoundException) {
-        "javax.annotation.Generated"
-    }
-
-    private val GENERATED_ANNOTATION =
-        "@Generated(\"androidx.hilt.AndroidXHiltProcessor\")"
-
     @Test
     fun verifyAssistedFactory_noArg() {
         val myViewModel = """
@@ -70,7 +62,7 @@
 
             @Override
             @NonNull
-            public MyViewModel create(@NonNull SavedStateHandle handle) {
+            public MyViewModel create(@NonNull SavedStateHandle arg0) {
                 return new MyViewModel();
             }
         }
@@ -118,8 +110,8 @@
 
             @Override
             @NonNull
-            public MyViewModel create(@NonNull SavedStateHandle handle) {
-                return new MyViewModel(handle);
+            public MyViewModel create(@NonNull SavedStateHandle arg0) {
+                return new MyViewModel(arg0);
             }
         }
         """.toJFO("androidx.hilt.lifecycle.test.MyViewModel_AssistedFactory")
@@ -184,8 +176,8 @@
 
             @Override
             @NonNull
-            public MyViewModel create(@NonNull SavedStateHandle handle) {
-                return new MyViewModel(s.get(), f.get(), handle, l.get());
+            public MyViewModel create(@NonNull SavedStateHandle arg0) {
+                return new MyViewModel(s.get(), f.get(), arg0, l.get());
             }
         }
         """.toJFO("androidx.hilt.lifecycle.test.MyViewModel_AssistedFactory")
@@ -248,8 +240,8 @@
 
             @Override
             @NonNull
-            public MyViewModel create(@NonNull SavedStateHandle handle) {
-                return new MyViewModel(s.get(), f, handle);
+            public MyViewModel create(@NonNull SavedStateHandle arg0) {
+                return new MyViewModel(s.get(), f, arg0);
             }
         }
         """.toJFO("androidx.hilt.lifecycle.test.MyViewModel_AssistedFactory")
@@ -322,8 +314,8 @@
 
             @Override
             @NonNull
-            public MyViewModel create(@NonNull SavedStateHandle handle) {
-                return new MyViewModel(s.get(), l, handle);
+            public MyViewModel create(@NonNull SavedStateHandle arg0) {
+                return new MyViewModel(s.get(), l, arg0);
             }
         }
         """.toJFO("androidx.hilt.lifecycle.test.MyViewModel_AssistedFactory")
@@ -377,8 +369,8 @@
 
             @Override
             @NonNull
-            public MyViewModel create(@NonNull SavedStateHandle handle) {
-                return new MyViewModel(handle, s.get(), handle);
+            public MyViewModel create(@NonNull SavedStateHandle arg0) {
+                return new MyViewModel(arg0, s.get(), arg0);
             }
         }
         """.toJFO("androidx.hilt.lifecycle.test.MyViewModel_AssistedFactory")
@@ -472,7 +464,7 @@
 
             @Override
             @NonNull
-            public Outer.InnerViewModel create(@NonNull SavedStateHandle handle) {
+            public Outer.InnerViewModel create(@NonNull SavedStateHandle arg0) {
                 return new Outer.InnerViewModel();
             }
         }
diff --git a/hilt/compiler/src/test/kotlin/androidx/hilt/ViewModelInjectStepTest.kt b/hilt/compiler/src/test/kotlin/androidx/hilt/lifecycle/ViewModelInjectStepTest.kt
similarity index 97%
rename from hilt/compiler/src/test/kotlin/androidx/hilt/ViewModelInjectStepTest.kt
rename to hilt/compiler/src/test/kotlin/androidx/hilt/lifecycle/ViewModelInjectStepTest.kt
index a919e27..d66ca3f 100644
--- a/hilt/compiler/src/test/kotlin/androidx/hilt/ViewModelInjectStepTest.kt
+++ b/hilt/compiler/src/test/kotlin/androidx/hilt/lifecycle/ViewModelInjectStepTest.kt
@@ -14,8 +14,11 @@
  * limitations under the License.
  */
 
-package androidx.hilt
+package androidx.hilt.lifecycle
 
+import androidx.hilt.Sources
+import androidx.hilt.compiler
+import androidx.hilt.toJFO
 import com.google.testing.compile.CompilationSubject.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
diff --git a/hilt/compiler/src/test/kotlin/androidx/hilt/testUtils.kt b/hilt/compiler/src/test/kotlin/androidx/hilt/testUtils.kt
index 20e6054..815cf362 100644
--- a/hilt/compiler/src/test/kotlin/androidx/hilt/testUtils.kt
+++ b/hilt/compiler/src/test/kotlin/androidx/hilt/testUtils.kt
@@ -22,6 +22,16 @@
 import java.io.File
 import javax.tools.JavaFileObject
 
+val GENERATED_TYPE = try {
+    Class.forName("javax.annotation.processing.Generated")
+    "javax.annotation.processing.Generated"
+} catch (_: ClassNotFoundException) {
+    "javax.annotation.Generated"
+}
+
+val GENERATED_ANNOTATION =
+    "@Generated(\"androidx.hilt.AndroidXHiltProcessor\")"
+
 object Sources {
     val VIEW_MODEL by lazy {
         loadJavaSource(
@@ -36,6 +46,20 @@
             ClassNames.SAVED_STATE_HANDLE.toString()
         )
     }
+
+    val WORKER by lazy {
+        loadJavaSource(
+            "Worker.java",
+            ClassNames.WORKER.toString()
+        )
+    }
+
+    val WORKER_PARAMETERS by lazy {
+        loadJavaSource(
+            "WorkerParameters.java",
+            ClassNames.WORKER_PARAMETERS.toString()
+        )
+    }
 }
 
 fun loadJavaSource(fileName: String, qName: String): JavaFileObject {
diff --git a/hilt/compiler/src/test/kotlin/androidx/hilt/work/WorkerGeneratorTest.kt b/hilt/compiler/src/test/kotlin/androidx/hilt/work/WorkerGeneratorTest.kt
new file mode 100644
index 0000000..411a803
--- /dev/null
+++ b/hilt/compiler/src/test/kotlin/androidx/hilt/work/WorkerGeneratorTest.kt
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * 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 androidx.hilt.work
+
+import androidx.hilt.GENERATED_ANNOTATION
+import androidx.hilt.GENERATED_TYPE
+import androidx.hilt.Sources
+import androidx.hilt.compiler
+import androidx.hilt.toJFO
+import com.google.testing.compile.CompilationSubject
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class WorkerGeneratorTest {
+
+    @Test
+    fun verifyAssistedFactory_mixedArgs() {
+        val foo = """
+        package androidx.hilt.work.test;
+
+        public class Foo { }
+        """.toJFO("androidx.hilt.work.test.Foo")
+
+        val myWorker = """
+        package androidx.hilt.work.test;
+
+        import android.content.Context;
+        import androidx.hilt.work.WorkerInject;
+        import androidx.work.Worker;
+        import androidx.work.WorkerParameters;
+        import java.lang.String;
+
+        class MyWorker extends Worker {
+            @WorkerInject
+            MyWorker(Context context, WorkerParameters params, String s, Foo f, long l) {
+                super(context, params);
+            }
+        }
+        """.toJFO("androidx.hilt.work.test.MyWorker")
+
+        val expected = """
+        package androidx.hilt.work.test;
+
+        import android.content.Context;
+        import androidx.annotation.NonNull;
+        import androidx.hilt.work.WorkerAssistedFactory;
+        import androidx.work.WorkerParameters;
+        import java.lang.Long;
+        import java.lang.Override;
+        import java.lang.String;
+        import $GENERATED_TYPE;
+        import javax.inject.Inject;
+        import javax.inject.Provider;
+
+        $GENERATED_ANNOTATION
+        public final class MyWorker_AssistedFactory implements
+                WorkerAssistedFactory<MyWorker> {
+
+            private final Provider<String> s;
+            private final Provider<Foo> f;
+            private final Provider<Long> l;
+
+            @Inject
+            MyWorker_AssistedFactory(Provider<String> s, Provider<Foo> f, Provider<Long> l) {
+                this.s = s;
+                this.f = f;
+                this.l = l;
+            }
+
+            @Override
+            @NonNull
+            public MyWorker create(@NonNull Context arg0, @NonNull WorkerParameters arg1) {
+                return new MyWorker(arg0, arg1, s.get(), f.get(), l.get());
+            }
+        }
+        """.toJFO("androidx.hilt.work.test.MyWorker_AssistedFactory")
+
+        val compilation = compiler()
+            .compile(foo, myWorker, Sources.WORKER, Sources.WORKER_PARAMETERS)
+        CompilationSubject.assertThat(compilation).succeeded()
+        CompilationSubject.assertThat(compilation)
+            .generatedSourceFile("androidx.hilt.work.test.MyWorker_AssistedFactory")
+            .hasSourceEquivalentTo(expected)
+    }
+
+    @Test
+    fun verifyMultibindModule() {
+        val myWorker = """
+        package androidx.hilt.work.test;
+
+        import android.content.Context;
+        import androidx.hilt.work.WorkerInject;
+        import androidx.work.Worker;
+        import androidx.work.WorkerParameters;
+
+        class MyWorker extends Worker {
+            @WorkerInject
+            MyWorker(Context context, WorkerParameters params) {
+                super(context, params);
+            }
+        }
+        """.toJFO("androidx.hilt.work.test.MyWorker")
+
+        val expected = """
+        package androidx.hilt.work.test;
+
+        import androidx.hilt.work.WorkerAssistedFactory;
+        import androidx.work.Worker;
+        import dagger.Binds;
+        import dagger.Module;
+        import dagger.hilt.InstallIn;
+        import dagger.hilt.android.components.ApplicationComponent;
+        import dagger.multibindings.IntoMap;
+        import dagger.multibindings.StringKey;
+        import $GENERATED_TYPE;
+
+        $GENERATED_ANNOTATION
+        @Module
+        @InstallIn(ApplicationComponent.class)
+        public interface MyWorker_HiltModule {
+            @Binds
+            @IntoMap
+            @StringKey("androidx.hilt.work.test.MyWorker")
+            WorkerAssistedFactory<? extends Worker> bind(MyWorker_AssistedFactory factory)
+        }
+        """.toJFO("androidx.hilt.work.test.MyWorker_HiltModule")
+
+        val compilation = compiler()
+            .compile(myWorker, Sources.WORKER, Sources.WORKER_PARAMETERS)
+        CompilationSubject.assertThat(compilation).succeeded()
+        CompilationSubject.assertThat(compilation)
+            .generatedSourceFile("androidx.hilt.work.test.MyWorker_HiltModule")
+            .hasSourceEquivalentTo(expected)
+    }
+}
\ No newline at end of file
diff --git a/hilt/integration-tests/workerapp/build.gradle b/hilt/integration-tests/workerapp/build.gradle
new file mode 100644
index 0000000..c423bb1
--- /dev/null
+++ b/hilt/integration-tests/workerapp/build.gradle
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * 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.
+ */
+
+import static androidx.build.dependencies.DependenciesKt.*
+
+plugins {
+    id("AndroidXPlugin")
+    id("com.android.application")
+    id("kotlin-android")
+    id("kotlin-kapt")
+    id("dagger.hilt.android.plugin")
+}
+
+android {
+    buildTypes {
+        release {
+            minifyEnabled true
+            shrinkResources true
+        }
+    }
+}
+
+dependencies {
+    implementation(project(":activity:activity"))
+    implementation project(':work:work-runtime')
+    implementation(project(":hilt:hilt-work"))
+    kapt(project(":hilt:hilt-compiler"))
+    implementation(KOTLIN_STDLIB)
+    implementation(DAGGER)
+    kapt(DAGGER_COMPILER)
+    implementation(HILT_ANDROID)
+    kapt(HILT_ANDROID_COMPILER)
+}
\ No newline at end of file
diff --git a/hilt/integration-tests/workerapp/src/main/AndroidManifest.xml b/hilt/integration-tests/workerapp/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..cf3d8fc
--- /dev/null
+++ b/hilt/integration-tests/workerapp/src/main/AndroidManifest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ 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.
+-->
+<manifest
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    package="androidx.hilt.integration.workerapp">
+    <application
+        android:name=".App">
+        <activity
+            android:name=".SimpleActivity"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        <provider
+            android:name="androidx.work.impl.WorkManagerInitializer"
+            android:authorities="${applicationId}.workmanager-init"
+            tools:node="remove" />
+    </application>
+</manifest>
diff --git a/hilt/integration-tests/workerapp/src/main/java/androidx/hilt/integration/workerapp/App.kt b/hilt/integration-tests/workerapp/src/main/java/androidx/hilt/integration/workerapp/App.kt
new file mode 100644
index 0000000..478a7a6
--- /dev/null
+++ b/hilt/integration-tests/workerapp/src/main/java/androidx/hilt/integration/workerapp/App.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * 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 androidx.hilt.integration.workerapp
+
+import android.app.Application
+import dagger.hilt.GenerateComponents
+import dagger.hilt.android.AndroidEntryPoint
+
+@AndroidEntryPoint
+@GenerateComponents
+class App : Application()
\ No newline at end of file
diff --git a/hilt/integration-tests/workerapp/src/main/java/androidx/hilt/integration/workerapp/SimpleActivity.kt b/hilt/integration-tests/workerapp/src/main/java/androidx/hilt/integration/workerapp/SimpleActivity.kt
new file mode 100644
index 0000000..d6a00d3
--- /dev/null
+++ b/hilt/integration-tests/workerapp/src/main/java/androidx/hilt/integration/workerapp/SimpleActivity.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * 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 androidx.hilt.integration.workerapp
+
+import android.os.Bundle
+import android.widget.Button
+import androidx.activity.ComponentActivity
+import androidx.work.OneTimeWorkRequest
+import androidx.work.WorkManager
+import dagger.hilt.android.AndroidEntryPoint
+import javax.inject.Inject
+
+@AndroidEntryPoint
+class SimpleActivity : ComponentActivity() {
+
+    @Inject
+    lateinit var workManager: WorkManager
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+
+        setContentView(R.layout.activity_main)
+
+        findViewById<Button>(R.id.work_button).setOnClickListener {
+            workManager.enqueue(OneTimeWorkRequest.Builder(SimpleWorker::class.java).build())
+        }
+    }
+}
\ No newline at end of file
diff --git a/hilt/integration-tests/workerapp/src/main/java/androidx/hilt/integration/workerapp/SimpleWorker.kt b/hilt/integration-tests/workerapp/src/main/java/androidx/hilt/integration/workerapp/SimpleWorker.kt
new file mode 100644
index 0000000..6e4bb46
--- /dev/null
+++ b/hilt/integration-tests/workerapp/src/main/java/androidx/hilt/integration/workerapp/SimpleWorker.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * 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 androidx.hilt.integration.workerapp
+
+import android.content.Context
+import android.util.Log
+import androidx.hilt.work.WorkerInject
+import androidx.work.Worker
+import androidx.work.WorkerParameters
+import javax.inject.Inject
+
+class SimpleWorker @WorkerInject constructor(
+    context: Context,
+    params: WorkerParameters,
+    private val logger: MyLogger
+) : Worker(context, params) {
+    override fun doWork(): Result {
+        logger.log("Hi")
+        return Result.success()
+    }
+}
+
+class MyLogger @Inject constructor() {
+    fun log(s: String) {
+        Log.i("MyLogger", s)
+    }
+}
\ No newline at end of file
diff --git a/hilt/integration-tests/workerapp/src/main/java/androidx/hilt/integration/workerapp/WorkManagerModule.kt b/hilt/integration-tests/workerapp/src/main/java/androidx/hilt/integration/workerapp/WorkManagerModule.kt
new file mode 100644
index 0000000..c9541b6
--- /dev/null
+++ b/hilt/integration-tests/workerapp/src/main/java/androidx/hilt/integration/workerapp/WorkManagerModule.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * 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 androidx.hilt.integration.workerapp
+
+import android.content.Context
+import androidx.hilt.work.WorkerFactory
+import androidx.work.Configuration
+import androidx.work.WorkManager
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.android.components.ApplicationComponent
+import dagger.hilt.android.qualifiers.ApplicationContext
+import javax.inject.Singleton
+
+@Module
+@InstallIn(ApplicationComponent::class)
+object WorkManagerModule {
+    @Provides
+    @Singleton
+    fun provideWorkManager(
+        @ApplicationContext context: Context,
+        workerFactory: WorkerFactory
+    ): WorkManager {
+        WorkManager.initialize(
+            context,
+            Configuration.Builder()
+                .setWorkerFactory(workerFactory)
+                .build()
+        )
+        return WorkManager.getInstance(context)
+    }
+}
\ No newline at end of file
diff --git a/hilt/integration-tests/workerapp/src/main/res/layout/activity_main.xml b/hilt/integration-tests/workerapp/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..54dcc17c
--- /dev/null
+++ b/hilt/integration-tests/workerapp/src/main/res/layout/activity_main.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/container"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@android:color/background_light">
+
+    <Button
+        android:id="@+id/work_button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="Do Work!"/>
+</RelativeLayout>
diff --git a/hilt/lifecycle-viewmodel/build.gradle b/hilt/lifecycle-viewmodel/build.gradle
index a76966b..fa0a748 100644
--- a/hilt/lifecycle-viewmodel/build.gradle
+++ b/hilt/lifecycle-viewmodel/build.gradle
@@ -47,7 +47,7 @@
     def name = variant.name
     def suffix = name.capitalize()
 
-    // Create jar<variant> task for testImplementation in viewmodel-hilt--compiler.
+    // Create jar<variant> task for testImplementation in hilt--compiler.
     project.tasks.create(name: "jar${suffix}", type: Jar){
         dependsOn variant.javaCompileProvider.get()
         from variant.javaCompileProvider.get().destinationDir
diff --git a/hilt/work/build.gradle b/hilt/work/build.gradle
index f7b393b..0de0e1d 100644
--- a/hilt/work/build.gradle
+++ b/hilt/work/build.gradle
@@ -42,6 +42,18 @@
     annotationProcessor(HILT_ANDROID_COMPILER)
 }
 
+android.libraryVariants.all { variant ->
+    def name = variant.name
+    def suffix = name.capitalize()
+
+    // Create jar<variant> task for testImplementation in hilt--compiler.
+    project.tasks.create(name: "jar${suffix}", type: Jar){
+        dependsOn variant.javaCompileProvider.get()
+        from variant.javaCompileProvider.get().destinationDir
+        destinationDir new File(project.buildDir, "libJar")
+    }
+}
+
 androidx {
     name = "Android Lifecycle WorkManager Hilt Extension"
     publish = Publish.NONE
diff --git a/settings.gradle b/settings.gradle
index 54d8b62d..8268967 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -154,6 +154,7 @@
 includeProject(":hilt:hilt-lifecycle-viewmodel", "hilt/lifecycle-viewmodel")
 includeProject(":hilt:hilt-work", "hilt/work")
 includeProject(":hilt:integration-tests:hilt-testapp-viewmodel", "hilt/integration-tests/viewmodelapp")
+includeProject(":hilt:integration-tests:hilt-testapp-worker", "hilt/integration-tests/workerapp")
 includeProject(":inspection:inspection", "inspection/inspection")
 includeProject(":inspection:inspection-gradle-plugin", "inspection/inspection-gradle-plugin")
 includeProject(":inspection:inspection-testing", "inspection/inspection-testing")