diff --git a/app/build.gradle b/app/build.gradle index 069caf6..3043b47 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -72,21 +72,17 @@ android { } dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.0" + implementation project(':library-mvp-bakery') // Support libs - implementation 'androidx.annotation:annotation:1.1.0' - implementation 'androidx.appcompat:appcompat:1.2.0' - implementation 'androidx.preference:preference:1.1.1' + implementation 'androidx.annotation:annotation:1.2.0' + implementation 'androidx.appcompat:appcompat:1.3.1' + implementation 'androidx.preference:preference-ktx:1.1.1' implementation 'com.google.android.material:material:1.2.1' - implementation 'androidx.browser:browser:1.2.0' - implementation 'androidx.constraintlayout:constraintlayout:2.0.2' - - // MVP lib - implementation 'eu.darken.mvpbakery:library:0.7.1' + implementation 'androidx.constraintlayout:constraintlayout:2.1.1' // RX - implementation "io.reactivex.rxjava2:rxjava:2.2.17" + implementation "io.reactivex.rxjava2:rxjava:2.2.21" implementation "io.reactivex.rxjava2:rxandroid:2.1.1" //Dagger @@ -103,27 +99,4 @@ dependencies { implementation "com.jakewharton.timber:timber:4.7.1" implementation 'com.github.tbruyelle:rxpermissions:0.10.2' - - // Testing - testImplementation "junit:junit:4.13" - testImplementation "org.mockito:mockito-core:3.0.0" - testImplementation 'com.nhaarman.mockitokotlin2:mockito-kotlin:2.1.0' - testImplementation 'com.github.tmurakami:dexopener:1.0.2' - - androidTestImplementation "org.mockito:mockito-core:3.0.0" - androidTestImplementation 'org.mockito:mockito-android:2.23.0' - androidTestImplementation 'com.github.tmurakami:dexopener:1.0.2' - - androidTestImplementation 'androidx.test:runner:1.2.0' - androidTestImplementation 'androidx.test:rules:1.2.0' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' - androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.2.0' - androidTestImplementation 'androidx.test.espresso:espresso-intents:3.2.0' - androidTestImplementation 'androidx.test.espresso.idling:idling-concurrent:3.2.0' -} - -kotlin { - experimental { - - } } \ No newline at end of file diff --git a/build.gradle b/build.gradle index 5e1b117..7ce6de2 100755 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,9 @@ buildscript { 'compileSdk': 30, 'minSdk' : 16, 'targetSdk' : 30, - + 'deps' : [ + 'dagger': '2.21', + ], 'version' : [ 'major': 3, 'minor': 3, @@ -16,21 +18,21 @@ buildscript { ext.buildConfig.version['fullName'] = "${buildConfig.version.name}.${buildConfig.version.build}" ext.buildConfig.version['code'] = buildConfig.version.major * 1000000 + buildConfig.version.minor * 10000 + buildConfig.version.patch * 100 + buildConfig.version.build + repositories { google() - jcenter() + mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:4.1.0' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.0" + classpath 'com.android.tools.build:gradle:7.0.2' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.31" } } allprojects { repositories { google() - jcenter() - + mavenCentral() maven { url 'https://jitpack.io' } } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index f0bd82c..c08b24f 100755 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip diff --git a/library-mvp-bakery/.gitignore b/library-mvp-bakery/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/library-mvp-bakery/.gitignore @@ -0,0 +1 @@ +/build diff --git a/library-mvp-bakery/build.gradle b/library-mvp-bakery/build.gradle new file mode 100644 index 0000000..874cadd --- /dev/null +++ b/library-mvp-bakery/build.gradle @@ -0,0 +1,45 @@ +plugins { + id 'com.android.library' + id 'kotlin-android-extensions' + id 'kotlin-android' +} + +android { + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + compileSdkVersion buildConfig.compileSdk + + defaultConfig { + minSdkVersion buildConfig.minSdk + targetSdkVersion buildConfig.targetSdk + + consumerProguardFiles 'proguard-rules.pro' + } + buildTypes { + debug { + } + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + implementation "androidx.lifecycle:lifecycle-common:2.3.1" + implementation "androidx.lifecycle:lifecycle-common-java8:2.3.1" + implementation "androidx.lifecycle:lifecycle-extensions:2.2.0" + + implementation "androidx.annotation:annotation:1.2.0" + implementation "androidx.appcompat:appcompat:1.3.1" + implementation "androidx.preference:preference-ktx:1.1.1" + + implementation "com.google.dagger:dagger:${buildConfig.deps.dagger}" + implementation "com.google.dagger:dagger-android:${buildConfig.deps.dagger}" + implementation "com.google.dagger:dagger-android-support:${buildConfig.deps.dagger}" + + annotationProcessor "com.google.dagger:dagger-compiler:${buildConfig.deps.dagger}" + annotationProcessor "com.google.dagger:dagger-android-processor:${buildConfig.deps.dagger}" +} \ No newline at end of file diff --git a/library-mvp-bakery/proguard-rules.pro b/library-mvp-bakery/proguard-rules.pro new file mode 100644 index 0000000..218482b --- /dev/null +++ b/library-mvp-bakery/proguard-rules.pro @@ -0,0 +1,18 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /Users/darken/android-sdks/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} +-keep class androidx.lifecycle.** { *; } diff --git a/library-mvp-bakery/src/main/AndroidManifest.xml b/library-mvp-bakery/src/main/AndroidManifest.xml new file mode 100644 index 0000000..22f13d9 --- /dev/null +++ b/library-mvp-bakery/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/base/MVPBakery.kt b/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/base/MVPBakery.kt new file mode 100644 index 0000000..e5aedec --- /dev/null +++ b/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/base/MVPBakery.kt @@ -0,0 +1,93 @@ +package eu.darken.mvpbakery.base + +import android.app.Activity +import android.app.Fragment +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.LifecycleOwner +import eu.darken.mvpbakery.injection.InjectedPresenter +import java.util.* + +class MVPBakery> internal constructor(builder: Builder) { + companion object { + @JvmStatic + fun > builder(): Builder { + return Builder() + } + } + + private val presenterRetainer: PresenterRetainer + private val stateForwarder: StateForwarder? + private val presenterFactory: PresenterFactory + private val presenterCallbacks: List> + + val presenter: PresenterT? + get() = presenterRetainer.presenter + + init { + this.presenterRetainer = builder.presenterRetainer + this.stateForwarder = builder.stateForwarder + this.presenterCallbacks = builder.presenterCallbacks + this.presenterFactory = builder.presenterFactory + } + + fun attach(lifecycleOwner: LifecycleOwner) { + if (stateForwarder != null) this.presenterRetainer.stateForwarder = stateForwarder + this.presenterRetainer.presenterFactory = presenterFactory + this.presenterRetainer.attach(lifecycleOwner, object : PresenterRetainer.Callback { + override fun onPresenterAvailable(presenter: PresenterT) { + presenterCallbacks.forEach { it.onPresenterAvailable(presenter) } + } + }) + } + + class Builder> internal constructor() where ViewT : Presenter.View, ViewT : LifecycleOwner { + internal lateinit var presenterFactory: PresenterFactory + internal lateinit var presenterRetainer: PresenterRetainer + internal var stateForwarder: StateForwarder? = null + internal val presenterCallbacks: MutableList> = ArrayList() + + /** + * If you want the presenter to be able to store data via [Activity.onSaveInstanceState] then you need to call this. + * + * @param stateForwarder pass a [object][StateForwarder] that you have to call on in onCreate()/onSaveInstance() + */ + fun stateForwarder(stateForwarder: StateForwarder): Builder { + this.stateForwarder = stateForwarder + return this + } + + fun addPresenterCallback(callback: PresenterRetainer.Callback): Builder { + presenterCallbacks.add(callback) + return this + } + + /** + * For injection you probably want to pass a [eu.darken.mvpbakery.injection.PresenterInjectionCallback] + */ + fun presenterRetainer(presenterRetainer: PresenterRetainer): Builder { + this.presenterRetainer = presenterRetainer + return this + } + + /** + * For injection pass an [InjectedPresenter] + */ + fun presenterFactory(presenterFactory: PresenterFactory): Builder { + this.presenterFactory = presenterFactory + return this + } + + fun build(): MVPBakery { + return MVPBakery(this) + } + + /** + * @param lifecycleOwner Your [AppCompatActivity], [Fragment] or [Fragment] + */ + fun attach(lifecycleOwner: ViewT): MVPBakery { + val lib = build() + lib.attach(lifecycleOwner) + return lib + } + } +} \ No newline at end of file diff --git a/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/base/Presenter.kt b/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/base/Presenter.kt new file mode 100644 index 0000000..0cb04b0 --- /dev/null +++ b/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/base/Presenter.kt @@ -0,0 +1,12 @@ +package eu.darken.mvpbakery.base + + +import androidx.lifecycle.LifecycleOwner + +interface Presenter { + fun onBindChange(view: ViewT?) + + fun onDestroy() + + interface View : LifecycleOwner +} diff --git a/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/base/PresenterFactory.kt b/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/base/PresenterFactory.kt new file mode 100644 index 0000000..77c24ef --- /dev/null +++ b/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/base/PresenterFactory.kt @@ -0,0 +1,22 @@ +package eu.darken.mvpbakery.base + +interface PresenterFactory> { + fun createPresenter(): FactoryResult + + class FactoryResult> internal constructor( + internal val presenter: PresenterT?, + internal val retry: Boolean, + internal val retryException: Throwable? = null + ) { + companion object { + + fun > retry(retryException: Throwable? = null): FactoryResult { + return FactoryResult(null, true, retryException) + } + + fun > forPresenter(presenter: PresenterT): FactoryResult { + return FactoryResult(presenter, false) + } + } + } +} diff --git a/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/base/PresenterRetainer.kt b/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/base/PresenterRetainer.kt new file mode 100644 index 0000000..442d8b6 --- /dev/null +++ b/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/base/PresenterRetainer.kt @@ -0,0 +1,34 @@ +package eu.darken.mvpbakery.base + + +import android.os.Bundle +import androidx.lifecycle.LifecycleOwner + +interface PresenterRetainer> { + val presenter: PresenterT? + var stateForwarder: StateForwarder? + var presenterFactory: PresenterFactory + + fun attach(lifecycleOwner: LifecycleOwner, callback: Callback) + + interface Callback> { + fun onPresenterAvailable(presenter: PresenterT) + } + + class DefaultStateListener(private val retainer: PresenterRetainer<*, *>) : StateForwarder.Listener { + + override fun onCreate(savedInstanceState: Bundle?): Boolean { + if (retainer.presenter is StateListener) { + (retainer.presenter as StateListener).onRestoreState(savedInstanceState) + return true + } + return false + } + + override fun onSaveInstanceState(outState: Bundle) { + if (retainer.presenter is StateListener) { + (retainer.presenter as StateListener).onSaveState(outState) + } + } + } +} diff --git a/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/base/StateForwarder.kt b/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/base/StateForwarder.kt new file mode 100644 index 0000000..3df9cf3 --- /dev/null +++ b/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/base/StateForwarder.kt @@ -0,0 +1,52 @@ +package eu.darken.mvpbakery.base + +import android.os.Bundle + +class StateForwarder { + private var internalCallback: Listener? = null + internal var inState: Bundle? = null + private var outState: Bundle? = null + private var isRestoreConsumed = false + val hasRestoreEvent: Boolean + get() = !isRestoreConsumed + + fun setListener(internalCallback: Listener?) { + this.internalCallback = internalCallback + if (internalCallback == null) return + if (inState != null) { + isRestoreConsumed = internalCallback.onCreate(inState) + if (isRestoreConsumed) inState = null + } + outState?.let { + internalCallback.onSaveInstanceState(it) + outState = null + } + } + + fun onCreate(savedInstanceState: Bundle?) { + isRestoreConsumed = internalCallback != null && internalCallback!!.onCreate(savedInstanceState) + if (!isRestoreConsumed) this.inState = savedInstanceState + } + + fun onSaveInstanceState(outState: Bundle) { + if (internalCallback != null) { + internalCallback!!.onSaveInstanceState(outState) + } else { + this.outState = outState + } + } + + interface Listener { + /** + * Call directly after [android.app.Activity.onCreate] or [android.app.Fragment.onCreate] + * + * @return true if the instance state was delivered or false if it should be persisted + */ + fun onCreate(savedInstanceState: Bundle?): Boolean + + /** + * Call before [android.app.Activity.onSaveInstanceState] or [android.app.Fragment.onSaveInstanceState] + */ + fun onSaveInstanceState(outState: Bundle) + } +} diff --git a/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/base/StateListener.kt b/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/base/StateListener.kt new file mode 100644 index 0000000..6cf44a5 --- /dev/null +++ b/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/base/StateListener.kt @@ -0,0 +1,8 @@ +package eu.darken.mvpbakery.base + +import android.os.Bundle + +interface StateListener { + fun onRestoreState(inState: Bundle?) + fun onSaveState(outState: Bundle) +} diff --git a/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/base/ViewModelRetainer.kt b/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/base/ViewModelRetainer.kt new file mode 100644 index 0000000..0c65426 --- /dev/null +++ b/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/base/ViewModelRetainer.kt @@ -0,0 +1,134 @@ +package eu.darken.mvpbakery.base + +import androidx.appcompat.app.AppCompatActivity +import androidx.fragment.app.Fragment +import androidx.lifecycle.* +import java.util.* + +class ViewModelRetainer> : PresenterRetainer { + companion object { + private fun getKey(owner: LifecycleOwner): String { + return (owner.javaClass.canonicalName ?: owner.javaClass.name) + ".MVPBakery.Container." + "Default" + } + } + + override val presenter: PresenterT? + get() = container?.presenter + internal val repo: ContainerRepo + internal var container: Container? = null + override var stateForwarder: StateForwarder? = null + set(value) { + field = value + stateForwarder?.setListener(PresenterRetainer.DefaultStateListener(this)) + } + override lateinit var presenterFactory: PresenterFactory + + constructor(appCompatActivity: AppCompatActivity) { + repo = ViewModelProviders.of(appCompatActivity, ContainerRepo.FACTORY).get(ContainerRepo::class.java) + } + + constructor(supportFragment: Fragment) { + repo = ViewModelProviders.of(supportFragment, ContainerRepo.FACTORY).get(ContainerRepo::class.java) + } + + internal class ContainerRepo : ViewModel() { + private val containerMap = HashMap>() + + operator fun get(key: Any): T { + @Suppress("UNCHECKED_CAST") + return containerMap[key] as T + } + + fun put(key: Any, item: Container<*, *>?) { + containerMap[key] = item!! + } + + override fun onCleared() { + for ((_, value) in containerMap) { + value.destroy() + } + containerMap.clear() + super.onCleared() + } + + companion object { + val FACTORY: ViewModelProvider.Factory = object : ViewModelProvider.Factory { + override fun create(modelClass: Class): T { + try { + return modelClass.newInstance() + } catch (e: Exception) { + throw RuntimeException(e) + } + } + } + } + } + + override fun attach(lifecycleOwner: LifecycleOwner, callback: PresenterRetainer.Callback) { + val key = getKey(lifecycleOwner) + container = repo.get>(key) + + if (container == null || container!!.observer == null) { + val observer = object : DefaultLifecycleObserver { + var delayedInit = false + + fun tryInitialization(isDelayedInit: Boolean) { + if (container == null) { + val result = presenterFactory.createPresenter() + if (result.retry) { + if (isDelayedInit) { + throw IllegalStateException("No presenter after final init attempt.", result.retryException) + } else { + delayedInit = true + return + } + } + + container = Container(result.presenter) + repo.put(key, container) + } + + container!!.observer = this + + stateForwarder?.let { + if (it.hasRestoreEvent && presenter is StateListener) { + (presenter as StateListener).onRestoreState(it.inState) + } + } + + callback.onPresenterAvailable(presenter!!) + } + + override fun onCreate(owner: LifecycleOwner) = tryInitialization(false) + + override fun onStart(owner: LifecycleOwner) { + if (delayedInit) { + delayedInit = false + tryInitialization(true) + } + @Suppress("UNCHECKED_CAST") + presenter?.onBindChange(owner as ViewT) + } + + override fun onResume(owner: LifecycleOwner) = Unit + + override fun onPause(owner: LifecycleOwner) = Unit + + override fun onStop(owner: LifecycleOwner) = presenter!!.onBindChange(null) + + override fun onDestroy(owner: LifecycleOwner) { + stateForwarder?.setListener(null) + container?.observer = null + owner.lifecycle.removeObserver(this) + } + } + lifecycleOwner.lifecycle.addObserver(observer) + } + } + + internal class Container>(val presenter: PresenterT?) { + var observer: LifecycleObserver? = null + + fun destroy() = presenter?.onDestroy() + } +} diff --git a/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/injection/ComponentPresenter.kt b/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/injection/ComponentPresenter.kt new file mode 100644 index 0000000..a68766b --- /dev/null +++ b/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/injection/ComponentPresenter.kt @@ -0,0 +1,35 @@ +package eu.darken.mvpbakery.injection + + +import androidx.annotation.CallSuper +import eu.darken.mvpbakery.base.Presenter + +abstract class ComponentPresenter> : Presenter { + lateinit var component: ComponentT + + var view: ViewT? = null + private set + + @CallSuper + override fun onBindChange(view: ViewT?) { + this.view = view + } + + @CallSuper + override fun onDestroy() { + + } + + @FunctionalInterface + interface ViewAction { + fun runOnView(v: T) + } + + fun onView(action: ViewAction) { + view?.let { action.runOnView(it) } + } + + fun withView(action: (v: ViewT) -> Unit) { + view?.let { action.invoke(it) } + } +} diff --git a/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/injection/ComponentSource.kt b/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/injection/ComponentSource.kt new file mode 100644 index 0000000..56400f8 --- /dev/null +++ b/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/injection/ComponentSource.kt @@ -0,0 +1,24 @@ +package eu.darken.mvpbakery.injection + +import dagger.android.AndroidInjector +import dagger.internal.Preconditions.checkNotNull +import javax.inject.Inject +import javax.inject.Provider + + +class ComponentSource @Inject constructor(private val injectorFactories: Map, @JvmSuppressWildcards Provider>>) + : ManualInjector { + + override fun inject(instance: T) { + get(instance).inject(instance) + } + + override fun get(instance: T): AndroidInjector { + val factoryProvider = injectorFactories[instance.javaClass] + ?: throw ClassNotFoundException("No injector available for $instance") + + val factory = factoryProvider.get() as AndroidInjector.Factory + + return checkNotNull(factory.create(instance), "%s.create(I) should not return null.", factory.javaClass.canonicalName) + } +} diff --git a/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/injection/InjectedPresenter.kt b/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/injection/InjectedPresenter.kt new file mode 100644 index 0000000..690e913 --- /dev/null +++ b/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/injection/InjectedPresenter.kt @@ -0,0 +1,57 @@ +package eu.darken.mvpbakery.injection + +import android.app.Activity + +import androidx.fragment.app.Fragment +import eu.darken.mvpbakery.base.Presenter +import eu.darken.mvpbakery.base.PresenterFactory +import eu.darken.mvpbakery.injection.activity.HasManualActivityInjector +import eu.darken.mvpbakery.injection.fragment.HasManualFragmentInjector + + +class InjectedPresenter, ComponentT : PresenterComponent> : PresenterFactory { + private val activity: Activity? + private val supportFragment: Fragment? + + constructor(source: Activity) { + this.activity = source + this.supportFragment = null + } + + constructor(source: Fragment) { + this.supportFragment = source + this.activity = null + } + + override fun createPresenter(): PresenterFactory.FactoryResult { + val component: ComponentT + when { + activity != null -> { + val injectorSource = activity.application as HasManualActivityInjector + @Suppress("UNCHECKED_CAST") + component = injectorSource.activityInjector()[activity] as ComponentT + } + supportFragment != null -> { + val injectorSource = supportFragment.activity as HasManualFragmentInjector + try { + val injector = injectorSource.supportFragmentInjector()!! + @Suppress("UNCHECKED_CAST") + component = injector[supportFragment] as ComponentT + } catch (e: Throwable) { + when (e) { + is NullPointerException, is UninitializedPropertyAccessException -> { + return PresenterFactory.FactoryResult.retry(retryException = e) + } + else -> throw e + } + } + } + else -> throw RuntimeException("No injection source.") + } + + val presenter = component.presenter + presenter.component = component + + return PresenterFactory.FactoryResult.forPresenter(presenter) + } +} diff --git a/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/injection/ManualInjector.kt b/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/injection/ManualInjector.kt new file mode 100644 index 0000000..c798f5e --- /dev/null +++ b/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/injection/ManualInjector.kt @@ -0,0 +1,7 @@ +package eu.darken.mvpbakery.injection + +import dagger.android.AndroidInjector + +interface ManualInjector : AndroidInjector { + operator fun get(instance: T): AndroidInjector +} diff --git a/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/injection/PresenterComponent.kt b/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/injection/PresenterComponent.kt new file mode 100644 index 0000000..3f075fd --- /dev/null +++ b/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/injection/PresenterComponent.kt @@ -0,0 +1,7 @@ +package eu.darken.mvpbakery.injection + +import eu.darken.mvpbakery.base.Presenter + +interface PresenterComponent, Self : PresenterComponent> { + val presenter: PresenterT +} diff --git a/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/injection/PresenterInjectionCallback.kt b/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/injection/PresenterInjectionCallback.kt new file mode 100644 index 0000000..7e9dcf6 --- /dev/null +++ b/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/injection/PresenterInjectionCallback.kt @@ -0,0 +1,14 @@ +package eu.darken.mvpbakery.injection + +import dagger.android.AndroidInjector +import eu.darken.mvpbakery.base.Presenter +import eu.darken.mvpbakery.base.PresenterRetainer + +class PresenterInjectionCallback, ComponentT>(private val injectionTarget: TargetT) + : PresenterRetainer.Callback where ComponentT : AndroidInjector, ComponentT : PresenterComponent { + + override fun onPresenterAvailable(presenter: PresenterT) { + val component = presenter.component + component.inject(injectionTarget) + } +} diff --git a/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/injection/activity/ActivityComponent.kt b/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/injection/activity/ActivityComponent.kt new file mode 100644 index 0000000..f9b3c62 --- /dev/null +++ b/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/injection/activity/ActivityComponent.kt @@ -0,0 +1,11 @@ +package eu.darken.mvpbakery.injection.activity + +import android.app.Activity + +import dagger.android.AndroidInjector + +interface ActivityComponent : AndroidInjector { + abstract class Builder> : AndroidInjector.Builder() { + abstract override fun build(): ComponentT + } +} diff --git a/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/injection/activity/ActivityKey.kt b/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/injection/activity/ActivityKey.kt new file mode 100644 index 0000000..f77012a --- /dev/null +++ b/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/injection/activity/ActivityKey.kt @@ -0,0 +1,11 @@ +package eu.darken.mvpbakery.injection.activity + +import android.app.Activity +import dagger.MapKey +import kotlin.reflect.KClass + +@MustBeDocumented +@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER) +@Retention(AnnotationRetention.RUNTIME) +@MapKey +annotation class ActivityKey(val value: KClass) \ No newline at end of file diff --git a/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/injection/activity/HasManualActivityInjector.kt b/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/injection/activity/HasManualActivityInjector.kt new file mode 100644 index 0000000..77094f8 --- /dev/null +++ b/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/injection/activity/HasManualActivityInjector.kt @@ -0,0 +1,11 @@ +package eu.darken.mvpbakery.injection.activity + +import android.app.Activity + +import dagger.android.HasActivityInjector +import eu.darken.mvpbakery.injection.ManualInjector + +interface HasManualActivityInjector : HasActivityInjector { + + override fun activityInjector(): ManualInjector +} diff --git a/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/injection/broadcastreceiver/BroadcastReceiverComponent.kt b/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/injection/broadcastreceiver/BroadcastReceiverComponent.kt new file mode 100644 index 0000000..e026daf --- /dev/null +++ b/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/injection/broadcastreceiver/BroadcastReceiverComponent.kt @@ -0,0 +1,11 @@ +package eu.darken.mvpbakery.injection.broadcastreceiver + +import android.content.BroadcastReceiver + +import dagger.android.AndroidInjector + +interface BroadcastReceiverComponent : AndroidInjector { + abstract class Builder> : AndroidInjector.Builder() { + abstract override fun build(): ComponentT + } +} diff --git a/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/injection/broadcastreceiver/BroadcastReceiverKey.kt b/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/injection/broadcastreceiver/BroadcastReceiverKey.kt new file mode 100644 index 0000000..697d884 --- /dev/null +++ b/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/injection/broadcastreceiver/BroadcastReceiverKey.kt @@ -0,0 +1,11 @@ +package eu.darken.mvpbakery.injection.broadcastreceiver + +import android.content.BroadcastReceiver +import dagger.MapKey +import kotlin.reflect.KClass + +@MustBeDocumented +@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER) +@Retention(AnnotationRetention.RUNTIME) +@MapKey +annotation class BroadcastReceiverKey(val value: KClass) \ No newline at end of file diff --git a/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/injection/broadcastreceiver/HasManualBroadcastReceiverInjector.kt b/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/injection/broadcastreceiver/HasManualBroadcastReceiverInjector.kt new file mode 100644 index 0000000..c269a69 --- /dev/null +++ b/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/injection/broadcastreceiver/HasManualBroadcastReceiverInjector.kt @@ -0,0 +1,11 @@ +package eu.darken.mvpbakery.injection.broadcastreceiver + + +import android.content.BroadcastReceiver + +import dagger.android.HasBroadcastReceiverInjector +import eu.darken.mvpbakery.injection.ManualInjector + +interface HasManualBroadcastReceiverInjector : HasBroadcastReceiverInjector { + override fun broadcastReceiverInjector(): ManualInjector +} diff --git a/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/injection/fragment/FragmentComponent.kt b/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/injection/fragment/FragmentComponent.kt new file mode 100644 index 0000000..125d153 --- /dev/null +++ b/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/injection/fragment/FragmentComponent.kt @@ -0,0 +1,10 @@ +package eu.darken.mvpbakery.injection.fragment + +import androidx.fragment.app.Fragment +import dagger.android.AndroidInjector + +interface FragmentComponent : AndroidInjector { + abstract class Builder> : AndroidInjector.Builder() { + abstract override fun build(): ComponentT + } +} diff --git a/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/injection/fragment/FragmentKey.kt b/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/injection/fragment/FragmentKey.kt new file mode 100644 index 0000000..18bfd85 --- /dev/null +++ b/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/injection/fragment/FragmentKey.kt @@ -0,0 +1,11 @@ +package eu.darken.mvpbakery.injection.fragment + +import androidx.fragment.app.Fragment +import dagger.MapKey +import kotlin.reflect.KClass + +@MustBeDocumented +@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER) +@Retention(AnnotationRetention.RUNTIME) +@MapKey +annotation class FragmentKey(val value: KClass) \ No newline at end of file diff --git a/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/injection/fragment/HasManualFragmentInjector.kt b/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/injection/fragment/HasManualFragmentInjector.kt new file mode 100644 index 0000000..ea8350d --- /dev/null +++ b/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/injection/fragment/HasManualFragmentInjector.kt @@ -0,0 +1,9 @@ +package eu.darken.mvpbakery.injection.fragment + +import androidx.fragment.app.Fragment +import dagger.android.support.HasSupportFragmentInjector +import eu.darken.mvpbakery.injection.ManualInjector + +interface HasManualFragmentInjector : HasSupportFragmentInjector { + override fun supportFragmentInjector(): ManualInjector? +} diff --git a/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/injection/service/HasManualServiceInjector.kt b/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/injection/service/HasManualServiceInjector.kt new file mode 100644 index 0000000..f3c4983 --- /dev/null +++ b/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/injection/service/HasManualServiceInjector.kt @@ -0,0 +1,11 @@ +package eu.darken.mvpbakery.injection.service + + +import android.app.Service + +import dagger.android.HasServiceInjector +import eu.darken.mvpbakery.injection.ManualInjector + +interface HasManualServiceInjector : HasServiceInjector { + override fun serviceInjector(): ManualInjector +} diff --git a/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/injection/service/ServiceComponent.kt b/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/injection/service/ServiceComponent.kt new file mode 100644 index 0000000..9740141 --- /dev/null +++ b/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/injection/service/ServiceComponent.kt @@ -0,0 +1,11 @@ +package eu.darken.mvpbakery.injection.service + +import android.app.Service + +import dagger.android.AndroidInjector + +interface ServiceComponent : AndroidInjector { + abstract class Builder> : AndroidInjector.Builder() { + abstract override fun build(): ComponentT + } +} diff --git a/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/injection/service/ServiceKey.kt b/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/injection/service/ServiceKey.kt new file mode 100644 index 0000000..c5344d5 --- /dev/null +++ b/library-mvp-bakery/src/main/java/eu/darken/mvpbakery/injection/service/ServiceKey.kt @@ -0,0 +1,11 @@ +package eu.darken.mvpbakery.injection.service + +import android.app.Service +import dagger.MapKey +import kotlin.reflect.KClass + +@MustBeDocumented +@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER) +@Retention(AnnotationRetention.RUNTIME) +@MapKey +annotation class ServiceKey(val value: KClass) \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index e7b4def..d0e1069 100755 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1,2 @@ include ':app' +include ':library-mvp-bakery' \ No newline at end of file