From 111f5911a65c2137a44d2d978f688a53f50d6a5d Mon Sep 17 00:00:00 2001 From: Bach Ngoc Hoai Date: Tue, 8 Oct 2019 11:05:10 +0700 Subject: [PATCH] Add support Activity/Fragment/ViewModel on feature module injection --- app/build.gradle | 24 ++++++++------- .../dynamiccodeloading/MainViewModel.kt | 0 .../dynamiccodeloading/MyApplication.kt | 18 ++++++++--- .../dynamiccodeloading/di/BaseDagger.kt | 16 +++++----- .../di/BaseViewModelModule.kt | 11 +++++++ .../dynamiccodeloading/di/ViewModelFactory.kt | 29 ++++++++++++++++++ .../di/annotation/ViewModelKey.kt | 15 ++++++++++ .../dynamiccodeloading/MainActivity.kt | 23 ++++++++++++++ app/src/main/res/menu/menu_main.xml | 9 ++++++ storage/build.gradle | 13 ++++++-- .../android/samples/storage/ActivityModule.kt | 23 ++++++++++++++ .../samples/storage/StorageFeatureImpl.kt | 13 +++++++- .../samples/storage/di/StorageDagger.kt | 9 ++++-- storage/src/main/AndroidManifest.xml | 3 ++ .../samples/storage/StorageActivity.kt | 28 +++++++++++++++++ .../samples/storage/StorageViewModel.kt | 12 ++++++++ .../src/main/res/layout/activity_storage.xml | 30 +++++++++++++++++++ storage/src/main/res/values/strings.xml | 4 +++ 18 files changed, 254 insertions(+), 26 deletions(-) mode change 100644 => 100755 app/src/dagger/java/com/google/android/samples/dynamiccodeloading/MainViewModel.kt mode change 100644 => 100755 app/src/dagger/java/com/google/android/samples/dynamiccodeloading/MyApplication.kt mode change 100644 => 100755 app/src/dagger/java/com/google/android/samples/dynamiccodeloading/di/BaseDagger.kt create mode 100755 app/src/dagger/java/com/google/android/samples/dynamiccodeloading/di/BaseViewModelModule.kt create mode 100755 app/src/dagger/java/com/google/android/samples/dynamiccodeloading/di/ViewModelFactory.kt create mode 100755 app/src/dagger/java/com/google/android/samples/dynamiccodeloading/di/annotation/ViewModelKey.kt create mode 100644 app/src/main/res/menu/menu_main.xml create mode 100644 storage/src/dagger/java/com/google/android/samples/storage/ActivityModule.kt mode change 100644 => 100755 storage/src/dagger/java/com/google/android/samples/storage/StorageFeatureImpl.kt mode change 100644 => 100755 storage/src/dagger/java/com/google/android/samples/storage/di/StorageDagger.kt mode change 100644 => 100755 storage/src/main/AndroidManifest.xml create mode 100644 storage/src/main/java/com/google/android/samples/storage/StorageActivity.kt create mode 100644 storage/src/main/java/com/google/android/samples/storage/StorageViewModel.kt create mode 100644 storage/src/main/res/layout/activity_storage.xml create mode 100644 storage/src/main/res/values/strings.xml diff --git a/app/build.gradle b/app/build.gradle index b143b08..520a655 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,8 +1,8 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' +apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-kapt' - android { compileSdkVersion 28 defaultConfig { @@ -51,18 +51,22 @@ dependencies { api "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" - api 'androidx.appcompat:appcompat:1.0.2' - implementation 'androidx.core:core-ktx:1.0.1' + api 'androidx.appcompat:appcompat:1.1.0' + implementation 'androidx.core:core-ktx:1.1.0' implementation 'androidx.constraintlayout:constraintlayout:1.1.3' - implementation "com.google.android.play:core:1.4.1" + api 'com.google.android.play:core:1.3.5' + api "androidx.lifecycle:lifecycle-extensions:2.1.0" - daggerApi 'com.google.dagger:dagger:2.16' - api 'com.google.dagger:dagger-android-support:2.16' + testImplementation 'junit:junit:4.12' + androidTestImplementation 'androidx.test:runner:1.2.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' - kapt 'com.google.dagger:dagger-compiler:2.16' + api 'com.jakewharton.timber:timber:4.7.1' - implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0-alpha04' - androidTestImplementation 'androidx.test:rules:1.2.0-alpha04' + api 'com.google.dagger:dagger:2.23.2' + kapt 'com.google.dagger:dagger-compiler:2.23.2' + api 'com.google.dagger:dagger-android:2.23.2' + api 'com.google.dagger:dagger-android-support:2.23.2' + kapt 'com.google.dagger:dagger-android-processor:2.23.2' } diff --git a/app/src/dagger/java/com/google/android/samples/dynamiccodeloading/MainViewModel.kt b/app/src/dagger/java/com/google/android/samples/dynamiccodeloading/MainViewModel.kt old mode 100644 new mode 100755 diff --git a/app/src/dagger/java/com/google/android/samples/dynamiccodeloading/MyApplication.kt b/app/src/dagger/java/com/google/android/samples/dynamiccodeloading/MyApplication.kt old mode 100644 new mode 100755 index df19bbf..bb10244 --- a/app/src/dagger/java/com/google/android/samples/dynamiccodeloading/MyApplication.kt +++ b/app/src/dagger/java/com/google/android/samples/dynamiccodeloading/MyApplication.kt @@ -16,16 +16,26 @@ */ package com.google.android.samples.dynamiccodeloading -import androidx.annotation.MainThread -import com.google.android.play.core.splitcompat.SplitCompatApplication +import com.google.android.play.core.splitcompat.SplitCompat import com.google.android.samples.dynamiccodeloading.di.BaseComponent import com.google.android.samples.dynamiccodeloading.di.DaggerBaseComponent +import dagger.android.AndroidInjector +import dagger.android.DaggerApplication + +class MyApplication : DaggerApplication() { + override fun applicationInjector(): AndroidInjector { + return baseComponent + } + + override fun onCreate() { + super.onCreate() + SplitCompat.install(this) + baseComponent.inject(this) + } -class MyApplication : SplitCompatApplication() { val baseComponent: BaseComponent by lazy { DaggerBaseComponent.builder() .application(this) - .logger(MainLogger) .build() } } \ No newline at end of file diff --git a/app/src/dagger/java/com/google/android/samples/dynamiccodeloading/di/BaseDagger.kt b/app/src/dagger/java/com/google/android/samples/dynamiccodeloading/di/BaseDagger.kt old mode 100644 new mode 100755 index d74cc13..73b3288 --- a/app/src/dagger/java/com/google/android/samples/dynamiccodeloading/di/BaseDagger.kt +++ b/app/src/dagger/java/com/google/android/samples/dynamiccodeloading/di/BaseDagger.kt @@ -19,28 +19,26 @@ package com.google.android.samples.dynamiccodeloading.di import android.app.Application import android.content.Context import android.util.Log -import com.google.android.samples.dynamiccodeloading.Logger -import com.google.android.samples.dynamiccodeloading.StorageFeature -import com.google.android.samples.dynamiccodeloading.TAG +import com.google.android.samples.dynamiccodeloading.* import dagger.BindsInstance import dagger.Component import dagger.Module import dagger.Provides +import dagger.android.AndroidInjectionModule +import dagger.android.AndroidInjector const val PROVIDER_CLASS = "com.google.android.samples.storage.StorageFeatureImpl\$Provider" -@Component(modules = [BaseModule::class]) -interface BaseComponent : StorageFeature.Dependencies { +@Component(modules = [AndroidInjectionModule::class, BaseModule::class, BaseViewModelModule::class]) +interface BaseComponent : StorageFeature.Dependencies, AndroidInjector { fun storageFeature(): StorageFeature? @Component.Builder interface Builder { @BindsInstance fun application(application: Application): Builder - @BindsInstance fun logger(logger: Logger): Builder fun build(): BaseComponent } - } @Module @@ -72,4 +70,8 @@ object BaseModule { @Provides @JvmStatic fun appContextProvider(application: Application): Context = application + + @Provides + @JvmStatic + fun loggerProvider(): Logger = MainLogger } \ No newline at end of file diff --git a/app/src/dagger/java/com/google/android/samples/dynamiccodeloading/di/BaseViewModelModule.kt b/app/src/dagger/java/com/google/android/samples/dynamiccodeloading/di/BaseViewModelModule.kt new file mode 100755 index 0000000..1ed3b45 --- /dev/null +++ b/app/src/dagger/java/com/google/android/samples/dynamiccodeloading/di/BaseViewModelModule.kt @@ -0,0 +1,11 @@ +package com.google.android.samples.dynamiccodeloading.di + +import androidx.lifecycle.ViewModelProvider +import dagger.Binds +import dagger.Module + +@Module +abstract class BaseViewModelModule { + @Binds + abstract fun bindViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory +} diff --git a/app/src/dagger/java/com/google/android/samples/dynamiccodeloading/di/ViewModelFactory.kt b/app/src/dagger/java/com/google/android/samples/dynamiccodeloading/di/ViewModelFactory.kt new file mode 100755 index 0000000..73c0787 --- /dev/null +++ b/app/src/dagger/java/com/google/android/samples/dynamiccodeloading/di/ViewModelFactory.kt @@ -0,0 +1,29 @@ +package com.google.android.samples.dynamiccodeloading.di + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import javax.inject.Inject +import javax.inject.Provider + +class ViewModelFactory @Inject constructor( + private val creators: Map, @JvmSuppressWildcards Provider> +) : ViewModelProvider.Factory { + + override fun create(modelClass: Class): T { + var creator = creators[modelClass] + if (creator == null) { + for ((key, value) in creators) { + if (modelClass.isAssignableFrom(key)) { + creator = value + break + } + } + } + if (creator == null) { + throw IllegalAccessException("unknown model class $modelClass") + } + + @Suppress("UNCHECKED_CAST") + return creator.get() as T + } +} diff --git a/app/src/dagger/java/com/google/android/samples/dynamiccodeloading/di/annotation/ViewModelKey.kt b/app/src/dagger/java/com/google/android/samples/dynamiccodeloading/di/annotation/ViewModelKey.kt new file mode 100755 index 0000000..d7a765e --- /dev/null +++ b/app/src/dagger/java/com/google/android/samples/dynamiccodeloading/di/annotation/ViewModelKey.kt @@ -0,0 +1,15 @@ +package com.google.android.samples.dynamiccodeloading.di.annotation + +import androidx.lifecycle.ViewModel +import dagger.MapKey +import kotlin.reflect.KClass + +@MustBeDocumented +@Target( + AnnotationTarget.FUNCTION, + AnnotationTarget.PROPERTY_GETTER, + AnnotationTarget.PROPERTY_SETTER +) +@Retention(AnnotationRetention.RUNTIME) +@MapKey +annotation class ViewModelKey(val value: KClass) diff --git a/app/src/main/java/com/google/android/samples/dynamiccodeloading/MainActivity.kt b/app/src/main/java/com/google/android/samples/dynamiccodeloading/MainActivity.kt index 85a1d0c..6192123 100644 --- a/app/src/main/java/com/google/android/samples/dynamiccodeloading/MainActivity.kt +++ b/app/src/main/java/com/google/android/samples/dynamiccodeloading/MainActivity.kt @@ -16,12 +16,16 @@ */ package com.google.android.samples.dynamiccodeloading +import android.content.Intent import android.os.Bundle +import android.view.Menu +import android.view.MenuItem import android.widget.Button import android.widget.TextView import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProviders +import java.lang.Exception /** * The single, main activity of this sample. @@ -39,6 +43,25 @@ class MainActivity : AppCompatActivity() { private lateinit var viewModel: MainViewModel + override fun onCreateOptionsMenu(menu: Menu?): Boolean { + menuInflater.inflate(R.menu.menu_main, menu) + return super.onCreateOptionsMenu(menu) + } + + override fun onOptionsItemSelected(item: MenuItem?): Boolean { + when (item?.itemId) { + R.id.storage_activity -> { + try { + val intent = Intent(this, Class.forName("com.google.android.samples.storage.StorageActivity")) + startActivity(intent) + } catch (exception: Exception) { + exception.printStackTrace() + } + } + } + return super.onOptionsItemSelected(item) + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml new file mode 100644 index 0000000..e0debfe --- /dev/null +++ b/app/src/main/res/menu/menu_main.xml @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/storage/build.gradle b/storage/build.gradle index 15ec4e1..5bbdc0d 100644 --- a/storage/build.gradle +++ b/storage/build.gradle @@ -43,6 +43,15 @@ android { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation 'androidx.constraintlayout:constraintlayout:1.1.3' + implementation project(':app') - kapt 'com.google.dagger:dagger-compiler:2.16' -} + kapt 'com.android.tools.build.jetifier:jetifier-core:1.0.0-beta07' + annotationProcessor 'com.android.tools.build.jetifier:jetifier-core:1.0.0-beta07' + + kapt 'com.google.dagger:dagger-compiler:2.23.2' + kapt 'com.google.dagger:dagger-android-processor:2.23.2' + + implementation 'androidx.appcompat:appcompat:1.1.0' + implementation "androidx.lifecycle:lifecycle-extensions:2.1.0" +} \ No newline at end of file diff --git a/storage/src/dagger/java/com/google/android/samples/storage/ActivityModule.kt b/storage/src/dagger/java/com/google/android/samples/storage/ActivityModule.kt new file mode 100644 index 0000000..ab63587 --- /dev/null +++ b/storage/src/dagger/java/com/google/android/samples/storage/ActivityModule.kt @@ -0,0 +1,23 @@ +package com.google.android.samples.storage + +import androidx.lifecycle.ViewModel +import com.google.android.samples.dynamiccodeloading.di.BaseViewModelModule +import com.google.android.samples.dynamiccodeloading.di.annotation.ViewModelKey +import dagger.Binds +import dagger.Module +import dagger.android.ContributesAndroidInjector +import dagger.multibindings.IntoMap + +@Module(includes = [BaseViewModelModule::class]) +abstract class ActivityModule { + @ContributesAndroidInjector(modules = [ActivityViewModelModule::class]) + abstract fun contributeStorageActivity(): StorageActivity + + @Module + abstract class ActivityViewModelModule { + @Binds + @IntoMap + @ViewModelKey(StorageViewModel::class) + abstract fun bindStorageViewModel(viewModel: StorageViewModel): ViewModel + } +} \ No newline at end of file diff --git a/storage/src/dagger/java/com/google/android/samples/storage/StorageFeatureImpl.kt b/storage/src/dagger/java/com/google/android/samples/storage/StorageFeatureImpl.kt old mode 100644 new mode 100755 index ebad5ee..742f051 --- a/storage/src/dagger/java/com/google/android/samples/storage/StorageFeatureImpl.kt +++ b/storage/src/dagger/java/com/google/android/samples/storage/StorageFeatureImpl.kt @@ -18,6 +18,7 @@ package com.google.android.samples.storage import android.content.SharedPreferences import com.google.android.samples.dynamiccodeloading.Logger +import com.google.android.samples.dynamiccodeloading.MyApplication import com.google.android.samples.dynamiccodeloading.StorageFeature import com.google.android.samples.storage.di.DaggerStorageComponent import javax.inject.Inject @@ -47,7 +48,17 @@ class StorageFeatureImpl @Inject constructor( */ companion object Provider : StorageFeature.Provider { override fun get(dependencies: StorageFeature.Dependencies): StorageFeature { - return DaggerStorageComponent.builder().dependencies(dependencies).build().storageFeature() + val component = DaggerStorageComponent.builder() + .dependencies(dependencies) + .build() + + when (dependencies.getContext()) { + is MyApplication -> { + component.inject(dependencies.getContext() as MyApplication) + } + } + + return component.storageFeature() } } } \ No newline at end of file diff --git a/storage/src/dagger/java/com/google/android/samples/storage/di/StorageDagger.kt b/storage/src/dagger/java/com/google/android/samples/storage/di/StorageDagger.kt old mode 100644 new mode 100755 index 754fb82..4752c8f --- a/storage/src/dagger/java/com/google/android/samples/storage/di/StorageDagger.kt +++ b/storage/src/dagger/java/com/google/android/samples/storage/di/StorageDagger.kt @@ -18,19 +18,24 @@ package com.google.android.samples.storage.di import android.content.Context import android.preference.PreferenceManager +import com.google.android.samples.dynamiccodeloading.MyApplication import com.google.android.samples.dynamiccodeloading.StorageFeature +import com.google.android.samples.dynamiccodeloading.di.BaseViewModelModule +import com.google.android.samples.storage.ActivityModule import com.google.android.samples.storage.StorageFeatureImpl import dagger.Component import dagger.Module import dagger.Provides +import dagger.android.AndroidInjectionModule +import dagger.android.AndroidInjector import javax.inject.Singleton @Singleton @Component( - modules = [StorageModule::class], + modules = [AndroidInjectionModule::class, StorageModule::class, BaseViewModelModule::class, ActivityModule::class], dependencies = [StorageFeature.Dependencies::class] // needs dependencies passed in to create component ) -interface StorageComponent { +interface StorageComponent : AndroidInjector { fun storageFeature(): StorageFeature } diff --git a/storage/src/main/AndroidManifest.xml b/storage/src/main/AndroidManifest.xml old mode 100644 new mode 100755 index 596edb3..4336aa4 --- a/storage/src/main/AndroidManifest.xml +++ b/storage/src/main/AndroidManifest.xml @@ -23,5 +23,8 @@ dist:title="@string/title_storage"> + + + diff --git a/storage/src/main/java/com/google/android/samples/storage/StorageActivity.kt b/storage/src/main/java/com/google/android/samples/storage/StorageActivity.kt new file mode 100644 index 0000000..a403409 --- /dev/null +++ b/storage/src/main/java/com/google/android/samples/storage/StorageActivity.kt @@ -0,0 +1,28 @@ +package com.google.android.samples.storage + +import android.os.Bundle +import android.widget.TextView +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.ViewModelProviders +import dagger.android.support.DaggerAppCompatActivity +import javax.inject.Inject + +class StorageActivity : DaggerAppCompatActivity() { + + @Inject + lateinit var viewModelFactory: ViewModelProvider.Factory + + private val viewModel by lazy { + ViewModelProviders.of(this, viewModelFactory).get(StorageViewModel::class.java) + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_storage) + + viewModel.counter.observe(this, Observer { + findViewById(R.id.counter).text = "$it" + }) + } +} \ No newline at end of file diff --git a/storage/src/main/java/com/google/android/samples/storage/StorageViewModel.kt b/storage/src/main/java/com/google/android/samples/storage/StorageViewModel.kt new file mode 100644 index 0000000..18bd00b --- /dev/null +++ b/storage/src/main/java/com/google/android/samples/storage/StorageViewModel.kt @@ -0,0 +1,12 @@ +package com.google.android.samples.storage + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.google.android.samples.dynamiccodeloading.StorageFeature +import javax.inject.Inject + +class StorageViewModel @Inject constructor( + private val storageFeature: StorageFeature +) : ViewModel() { + val counter = MutableLiveData(storageFeature.loadCounter()) +} \ No newline at end of file diff --git a/storage/src/main/res/layout/activity_storage.xml b/storage/src/main/res/layout/activity_storage.xml new file mode 100644 index 0000000..c27baf3 --- /dev/null +++ b/storage/src/main/res/layout/activity_storage.xml @@ -0,0 +1,30 @@ + + + + + + + + \ No newline at end of file diff --git a/storage/src/main/res/values/strings.xml b/storage/src/main/res/values/strings.xml new file mode 100644 index 0000000..bef71bb --- /dev/null +++ b/storage/src/main/res/values/strings.xml @@ -0,0 +1,4 @@ + + + The counter number saved + \ No newline at end of file