Skip to content

Commit

Permalink
Allow AppScope -> ActivityScope -> FragmentScope dagger scope nesting
Browse files Browse the repository at this point in the history
  • Loading branch information
aitorvs committed Dec 9, 2021
1 parent bb87d74 commit b484a1f
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import android.annotation.SuppressLint
import android.content.BroadcastReceiver
import android.os.Bundle
import android.view.MenuItem
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
Expand All @@ -29,9 +28,10 @@ import com.duckduckgo.mobile.android.R
import com.duckduckgo.mobile.android.ui.applyTheme
import com.duckduckgo.mobile.android.ui.store.ThemingDataStore
import dagger.android.AndroidInjection
import dagger.android.DaggerActivity
import javax.inject.Inject

abstract class DuckDuckGoActivity : AppCompatActivity() {
abstract class DuckDuckGoActivity : DaggerActivity() {

@Inject
lateinit var viewModelFactory: ViewModelFactory
Expand Down
1 change: 1 addition & 0 deletions di/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,5 @@ dependencies {
implementation Kotlin.stdlib.jdk7
implementation AndroidX.fragmentKtx
implementation Google.dagger
implementation AndroidX.appCompat
}
39 changes: 34 additions & 5 deletions di/src/main/java/dagger/android/AndroidInjector.kt
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,13 @@ interface AndroidInjector<T> {
* 2. Use the factory to create the dagger component that relates to an Android type, eg. Activity
* 3. Inject any dependency requested by the Android type
*/
inline fun <reified T> inject(application: Application, instance: T, mapKey: Class<*>? = null) {
if ((application is HasDaggerInjector)) {
(application.daggerFactoryFor(mapKey ?: instance!!::class.java) as Factory<T>)
inline fun <reified T> inject(injector: Any, instance: T, mapKey: Class<*>? = null) {
if ((injector is HasDaggerInjector)) {
(injector.daggerFactoryFor(mapKey ?: instance!!::class.java) as Factory<T>)
.create(instance)
.inject(instance)
} else {
throw RuntimeException("Application class does not extend ${HasDaggerInjector::class.simpleName}")
throw RuntimeException("${injector.javaClass.canonicalName} class does not extend ${HasDaggerInjector::class.simpleName}")
}
}
}
Expand All @@ -87,7 +87,7 @@ class AndroidInjection {
}

inline fun <reified T : Fragment> inject(instance: T, bindingKey: Class<*>? = null) {
AndroidInjector.inject(instance.context?.applicationContext as Application, instance, bindingKey)
AndroidInjector.inject(findHasDaggerInjectorForFragment(instance), instance, bindingKey)
}

inline fun <reified T : Service> inject(instance: T, bindingKey: Class<*>? = null) {
Expand All @@ -97,5 +97,34 @@ class AndroidInjection {
inline fun <reified T : BroadcastReceiver> inject(instance: T, context: Context, bindingKey: Class<*>? = null) {
AndroidInjector.inject(context.applicationContext as Application, instance, bindingKey)
}

/**
* Injects the [fragment] if an associated [AndroidInjector] implementation is found, otherwise [IllegalArgumentException]
* is thrown.
*
* The algorithm is the following:
* * walks the parent fragment hierarchy until if finds one that implements [HasDaggerInjector], else
* * uses the [fragment]'s and returns it if it implements [HasDaggerInjector], else
* * uses the [Application] and returns it if it implements [HasDaggerInjector], else
* * throws [IllegalArgumentException]
*/
fun findHasDaggerInjectorForFragment(fragment: Fragment): HasDaggerInjector {
var parentFragment: Fragment? = fragment
while (parentFragment?.parentFragment != null) {
parentFragment = parentFragment.parentFragment

if (parentFragment is HasDaggerInjector) {
return parentFragment
}
}
val activity = fragment.activity
if (activity is HasDaggerInjector) {
return activity
}

activity?.application?.let { return it as HasDaggerInjector }

throw IllegalArgumentException("No injector found for ${fragment.javaClass.canonicalName}")
}
}
}
37 changes: 37 additions & 0 deletions di/src/main/java/dagger/android/DaggerActivity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright (c) 2021 DuckDuckGo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package dagger.android

import androidx.appcompat.app.AppCompatActivity
import com.duckduckgo.di.DaggerMap
import javax.inject.Inject

abstract class DaggerActivity : AppCompatActivity(), HasDaggerInjector {
@Inject
lateinit var injectorFactoryMap: DaggerMap<Class<*>, AndroidInjector.Factory<*>>

override fun daggerFactoryFor(key: Class<*>): AndroidInjector.Factory<*> {
return injectorFactoryMap[key]
?: throw RuntimeException(
"""
Could not find the dagger component for ${key.simpleName}.
You probably forgot to create the ${key.simpleName}Component.
If you DID create the ${key.simpleName}Component, check that it uses @ContributesTo(ActivityScope::class)
""".trimIndent()
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@

package dagger.android.support

import android.app.Application
import androidx.fragment.app.Fragment
import dagger.android.AndroidInjection
import dagger.android.AndroidInjector

class AndroidSupportInjection {
companion object {
inline fun <reified T : Fragment> inject(instance: T) {
AndroidInjector.inject(instance.context?.applicationContext as Application, instance)
AndroidInjector.inject(AndroidInjection.findHasDaggerInjectorForFragment(instance), instance)
}
}
}

0 comments on commit b484a1f

Please sign in to comment.