From b5898148b548fd7b6a0aa66d964e1a44a638282f Mon Sep 17 00:00:00 2001 From: Ivan Alvarado Date: Fri, 10 Jan 2025 22:39:53 -0800 Subject: [PATCH 1/4] Add hilt dependency to version catalogs --- gradle/libs.versions.toml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c7de174..7992c90 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -15,6 +15,7 @@ conscrypt-android = "2.2.1" dagger = "2.51.1" dagger-android = "2.51.1" dagger-assisted-inject = "0.5.0" +hilt = "2.51.1" kotlin-coroutines = "1.9.0" mockito-core = "5.14.1" moshi = "1.15.1" @@ -51,6 +52,8 @@ dagger-android-support = { module = "com.google.dagger:dagger-android-support", dagger-android-processor = { module = "com.google.dagger:dagger-android-processor", version.ref = "dagger-android" } assisted-inject-annotations-dagger2 = { module = "com.squareup.inject:assisted-inject-annotations-dagger2", version.ref = "dagger-assisted-inject" } assisted-inject-processor-dagger2 = { module = "com.squareup.inject:assisted-inject-processor-dagger2", version.ref = "dagger-assisted-inject" } +hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hilt" } +hilt-compiler = { module = "com.google.dagger:hilt-android-compiler", version.ref = "hilt" } kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlin-coroutines" } kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlin-coroutines" } mockito-core = { module = "org.mockito:mockito-core", version.ref = "mockito-core" } @@ -70,3 +73,6 @@ rxbinding-core = { module = "com.jakewharton.rxbinding3:rxbinding-core", version rxbinding-appcompat = { module = "com.jakewharton.rxbinding3:rxbinding-appcompat", version.ref = "rxbinding" } timber = { module = "com.jakewharton.timber:timber", version.ref = "timber" } truth = { module = "com.google.truth:truth", version.ref = "truth" } + +[plugins] +hilt = { id = "com.google.dagger.hilt.andriod", version.ref = "hilt" } \ No newline at end of file From 5a3ae93c54434c7a6f73a576b717ebdf0316a0b3 Mon Sep 17 00:00:00 2001 From: Ivan Alvarado Date: Fri, 10 Jan 2025 22:51:45 -0800 Subject: [PATCH 2/4] Apply hilt to project module --- app/build.gradle | 7 +++++++ gradle/libs.versions.toml | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 0db9205..3589a12 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,3 +1,7 @@ +plugins { + alias libs.plugins.hilt +} + apply plugin: 'com.android.application' apply plugin: 'kotlin-android' @@ -117,5 +121,8 @@ dependencies { implementation libs.timber + implementation libs.hilt.android + kapt libs.hilt.compiler + testImplementation libs.truth } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7992c90..4366da6 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -75,4 +75,4 @@ timber = { module = "com.jakewharton.timber:timber", version.ref = "timber" } truth = { module = "com.google.truth:truth", version.ref = "truth" } [plugins] -hilt = { id = "com.google.dagger.hilt.andriod", version.ref = "hilt" } \ No newline at end of file +hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" } \ No newline at end of file From d690fb0cd437b2b007f3ab66de7a0533c4d3123d Mon Sep 17 00:00:00 2001 From: Ivan Alvarado Date: Fri, 10 Jan 2025 23:00:08 -0800 Subject: [PATCH 3/4] Migrate application and activity to hilt; todo - rest of app --- .../main/java/com/ivanalvarado/template/AppController.kt | 9 +++------ .../main/java/com/ivanalvarado/template/MainActivity.kt | 2 ++ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/ivanalvarado/template/AppController.kt b/app/src/main/java/com/ivanalvarado/template/AppController.kt index 0cc6adc..4c05b4c 100644 --- a/app/src/main/java/com/ivanalvarado/template/AppController.kt +++ b/app/src/main/java/com/ivanalvarado/template/AppController.kt @@ -4,7 +4,7 @@ import android.app.Activity import android.app.Application import com.ivanalvarado.template.di.component.DaggerAppComponent import dagger.android.DispatchingAndroidInjector -import dagger.android.HasActivityInjector +import dagger.hilt.android.HiltAndroidApp import javax.inject.Inject @@ -14,15 +14,12 @@ import javax.inject.Inject * This way a DispatchingAndroidInjector is injected which is * then returned when an injector for an activity is requested. * */ -class AppController : Application(), HasActivityInjector { +@HiltAndroidApp +class AppController : Application() { @Inject lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector - override fun activityInjector(): DispatchingAndroidInjector? { - return dispatchingAndroidInjector - } - override fun onCreate() { super.onCreate() DaggerAppComponent.builder() diff --git a/app/src/main/java/com/ivanalvarado/template/MainActivity.kt b/app/src/main/java/com/ivanalvarado/template/MainActivity.kt index 47bdb02..9dedf48 100644 --- a/app/src/main/java/com/ivanalvarado/template/MainActivity.kt +++ b/app/src/main/java/com/ivanalvarado/template/MainActivity.kt @@ -6,8 +6,10 @@ import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProviders import com.ivanalvarado.template.viewmodel.ExampleViewModel import dagger.android.AndroidInjection +import dagger.hilt.android.AndroidEntryPoint import javax.inject.Inject +@AndroidEntryPoint class MainActivity : AppCompatActivity() { /* From c448d1159492cd920899d7c9d378d10c958fb479 Mon Sep 17 00:00:00 2001 From: Ivan Alvarado Date: Sat, 11 Jan 2025 13:33:51 -0800 Subject: [PATCH 4/4] Migrate project to Hilt entirely --- app/build.gradle | 6 ++- .../ivanalvarado/template/AppController.kt | 25 +-------- .../com/ivanalvarado/template/MainActivity.kt | 30 +---------- .../template/di/ViewModelFactory.kt | 25 --------- .../ivanalvarado/template/di/ViewModelKey.kt | 13 ----- .../template/di/component/AppComponent.kt | 53 ------------------- .../template/di/module/ActivityModule.kt | 12 ----- .../template/di/module/DbModule.kt | 5 +- .../template/di/module/LoggingModule.kt | 5 +- .../template/di/module/NetworkModule.kt | 11 ++-- .../template/di/module/ServiceModule.kt | 6 ++- .../template/di/module/ViewModelModule.kt | 30 ----------- .../template/viewmodel/ExampleViewModel.kt | 2 + 13 files changed, 24 insertions(+), 199 deletions(-) delete mode 100644 app/src/main/java/com/ivanalvarado/template/di/ViewModelFactory.kt delete mode 100644 app/src/main/java/com/ivanalvarado/template/di/ViewModelKey.kt delete mode 100644 app/src/main/java/com/ivanalvarado/template/di/component/AppComponent.kt delete mode 100644 app/src/main/java/com/ivanalvarado/template/di/module/ActivityModule.kt delete mode 100644 app/src/main/java/com/ivanalvarado/template/di/module/ViewModelModule.kt diff --git a/app/build.gradle b/app/build.gradle index 3589a12..0e21f0d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -14,7 +14,7 @@ android { defaultConfig { applicationId "com.ivanalvarado.template" minSdkVersion 22 - targetSdkVersion 31 + targetSdkVersion 33 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" @@ -41,6 +41,10 @@ kapt { correctErrorTypes = true } +hilt { + enableAggregatingTask = false +} + dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" diff --git a/app/src/main/java/com/ivanalvarado/template/AppController.kt b/app/src/main/java/com/ivanalvarado/template/AppController.kt index 4c05b4c..466fe84 100644 --- a/app/src/main/java/com/ivanalvarado/template/AppController.kt +++ b/app/src/main/java/com/ivanalvarado/template/AppController.kt @@ -1,30 +1,7 @@ package com.ivanalvarado.template -import android.app.Activity import android.app.Application -import com.ivanalvarado.template.di.component.DaggerAppComponent -import dagger.android.DispatchingAndroidInjector import dagger.hilt.android.HiltAndroidApp -import javax.inject.Inject - -/* - * we use our AppComponent (now prefixed with Dagger) - * to inject our Application class. - * This way a DispatchingAndroidInjector is injected which is - * then returned when an injector for an activity is requested. - * */ @HiltAndroidApp -class AppController : Application() { - - @Inject - lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector - - override fun onCreate() { - super.onCreate() - DaggerAppComponent.builder() - .application(this) - .build() - .inject(this) - } -} \ No newline at end of file +class AppController : Application() \ No newline at end of file diff --git a/app/src/main/java/com/ivanalvarado/template/MainActivity.kt b/app/src/main/java/com/ivanalvarado/template/MainActivity.kt index 9dedf48..76ea653 100644 --- a/app/src/main/java/com/ivanalvarado/template/MainActivity.kt +++ b/app/src/main/java/com/ivanalvarado/template/MainActivity.kt @@ -1,45 +1,19 @@ package com.ivanalvarado.template import android.os.Bundle +import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity -import androidx.lifecycle.ViewModelProvider -import androidx.lifecycle.ViewModelProviders import com.ivanalvarado.template.viewmodel.ExampleViewModel -import dagger.android.AndroidInjection import dagger.hilt.android.AndroidEntryPoint -import javax.inject.Inject @AndroidEntryPoint class MainActivity : AppCompatActivity() { - /* - * Step 1: Here as mentioned in Step 5, we need to - * inject the ViewModelFactory. The ViewModelFactory class - * has a list of ViewModels and will provide - * the corresponding ViewModel in this activity - * */ - @Inject - internal lateinit var viewModelFactory: ViewModelProvider.Factory - - private lateinit var exampleViewModel: ExampleViewModel + private val exampleViewModel: ExampleViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { - /* - * Step 2: Remember in our ActivityModule, we - * defined MainActivity injection? So we need - * to call this method in order to inject the - * ViewModelFactory into our Activity - * */ - AndroidInjection.inject(this) super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) - - setUpViewModel() - } - - private fun setUpViewModel() { - exampleViewModel = - ViewModelProviders.of(this, viewModelFactory).get(ExampleViewModel::class.java) } } diff --git a/app/src/main/java/com/ivanalvarado/template/di/ViewModelFactory.kt b/app/src/main/java/com/ivanalvarado/template/di/ViewModelFactory.kt deleted file mode 100644 index 30db3fb..0000000 --- a/app/src/main/java/com/ivanalvarado/template/di/ViewModelFactory.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.ivanalvarado.template.di - -import androidx.lifecycle.ViewModel -import androidx.lifecycle.ViewModelProvider -import javax.inject.Inject -import javax.inject.Provider -import javax.inject.Singleton - - -@Singleton -class ViewModelFactory @Inject -constructor(private val viewModels: MutableMap, Provider>) : - ViewModelProvider.Factory { - - override fun create(modelClass: Class): T { - val creator = viewModels[modelClass] - ?: viewModels.asIterable().firstOrNull { modelClass.isAssignableFrom(it.key) }?.value - ?: throw IllegalArgumentException("unknown model class $modelClass") - return try { - creator.get() as T - } catch (e: Exception) { - throw RuntimeException(e) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/ivanalvarado/template/di/ViewModelKey.kt b/app/src/main/java/com/ivanalvarado/template/di/ViewModelKey.kt deleted file mode 100644 index 641abf3..0000000 --- a/app/src/main/java/com/ivanalvarado/template/di/ViewModelKey.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.ivanalvarado.template.di - -import androidx.lifecycle.ViewModel -import dagger.MapKey -import kotlin.reflect.KClass - -@Target( - AnnotationTarget.FUNCTION, - AnnotationTarget.PROPERTY_GETTER, - AnnotationTarget.PROPERTY_SETTER -) -@MapKey -annotation class ViewModelKey(val value: KClass) \ No newline at end of file diff --git a/app/src/main/java/com/ivanalvarado/template/di/component/AppComponent.kt b/app/src/main/java/com/ivanalvarado/template/di/component/AppComponent.kt deleted file mode 100644 index 0da8a80..0000000 --- a/app/src/main/java/com/ivanalvarado/template/di/component/AppComponent.kt +++ /dev/null @@ -1,53 +0,0 @@ -package com.ivanalvarado.template.di.component - -import android.app.Application -import com.ivanalvarado.template.AppController -import com.ivanalvarado.template.di.module.* -import com.ivanalvarado.template.di.module.ViewModelModule -import dagger.BindsInstance -import dagger.Component -import dagger.android.support.AndroidSupportInjectionModule -import javax.inject.Singleton - -/* - * We mark this interface with the @Component annotation. - * And we define all the modules that can be injected. - * Note that we provide AndroidSupportInjectionModule.class - * here. This class was not created by us. - * It is an internal class in Dagger 2.10. - * Provides our activities and fragments with given module. - * */ -@Component( - modules = [ - NetworkModule::class, - LoggingModule::class, - ServiceModule::class, - DbModule::class, - ViewModelModule::class, - ActivityModule::class, - AndroidSupportInjectionModule::class] -) -@Singleton -interface AppComponent { - - /* - * We will call this builder interface from our custom Application class. - * This will set our application object to the AppComponent. - * So inside the AppComponent the application instance is available. - * So this application instance can be accessed by our modules - * such as NetworkModule when needed - * - * */ - @Component.Builder - interface Builder { - @BindsInstance - fun application(application: Application): Builder - - fun build(): AppComponent - } - - /* - * This is our custom Application class - * */ - fun inject(appController: AppController) -} \ No newline at end of file diff --git a/app/src/main/java/com/ivanalvarado/template/di/module/ActivityModule.kt b/app/src/main/java/com/ivanalvarado/template/di/module/ActivityModule.kt deleted file mode 100644 index 3d4d8c9..0000000 --- a/app/src/main/java/com/ivanalvarado/template/di/module/ActivityModule.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.ivanalvarado.template.di.module - -import com.ivanalvarado.template.MainActivity -import dagger.Module -import dagger.android.ContributesAndroidInjector - -@Module -abstract class ActivityModule { - - @ContributesAndroidInjector() - abstract fun contributeMainActivity(): MainActivity -} \ No newline at end of file diff --git a/app/src/main/java/com/ivanalvarado/template/di/module/DbModule.kt b/app/src/main/java/com/ivanalvarado/template/di/module/DbModule.kt index e1b6103..6677736 100644 --- a/app/src/main/java/com/ivanalvarado/template/di/module/DbModule.kt +++ b/app/src/main/java/com/ivanalvarado/template/di/module/DbModule.kt @@ -6,14 +6,16 @@ import com.ivanalvarado.template.database.AppDatabase import com.ivanalvarado.template.database.dao.ExampleDao import dagger.Module import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent import javax.inject.Singleton @Module +@InstallIn(SingletonComponent::class) class DbModule { @Provides - @Singleton internal fun provideDatabase(application: Application): AppDatabase { return Room.databaseBuilder( application, AppDatabase::class.java, "AndroidBootstrap.db" @@ -21,7 +23,6 @@ class DbModule { } @Provides - @Singleton internal fun provideExampleDao(appDatabase: AppDatabase): ExampleDao { return appDatabase.exampleDao() } diff --git a/app/src/main/java/com/ivanalvarado/template/di/module/LoggingModule.kt b/app/src/main/java/com/ivanalvarado/template/di/module/LoggingModule.kt index ad64060..1cf6aaa 100644 --- a/app/src/main/java/com/ivanalvarado/template/di/module/LoggingModule.kt +++ b/app/src/main/java/com/ivanalvarado/template/di/module/LoggingModule.kt @@ -2,13 +2,14 @@ package com.ivanalvarado.template.di.module import dagger.Module import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent import okhttp3.logging.HttpLoggingInterceptor -import javax.inject.Singleton @Module +@InstallIn(SingletonComponent::class) class LoggingModule { @Provides - @Singleton fun providesHttpLoggingInterceptor(): HttpLoggingInterceptor { val httpLoggingInterceptor = HttpLoggingInterceptor() httpLoggingInterceptor.level = HttpLoggingInterceptor.Level.BODY diff --git a/app/src/main/java/com/ivanalvarado/template/di/module/NetworkModule.kt b/app/src/main/java/com/ivanalvarado/template/di/module/NetworkModule.kt index c74f7c7..87d76ce 100644 --- a/app/src/main/java/com/ivanalvarado/template/di/module/NetworkModule.kt +++ b/app/src/main/java/com/ivanalvarado/template/di/module/NetworkModule.kt @@ -3,9 +3,10 @@ package com.ivanalvarado.template.di.module import android.app.Application import com.google.gson.Gson import com.google.gson.GsonBuilder -import com.ivanalvarado.template.BuildConfig import dagger.Module import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent import okhttp3.Cache import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor @@ -14,22 +15,20 @@ import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory import retrofit2.converter.gson.GsonConverterFactory import java.io.File import java.util.concurrent.TimeUnit -import javax.inject.Singleton private const val BASE_URL = "https://base.url.com" -@Module(includes = [LoggingModule::class]) +@Module +@InstallIn(SingletonComponent::class) class NetworkModule { @Provides - @Singleton internal fun provideGson(): Gson { val gsonBuilder = GsonBuilder() return gsonBuilder.create() } @Provides - @Singleton internal fun provideCache(application: Application): Cache { val cacheSize = (10 * 1024 * 1024).toLong() // 10 MB val httpCacheDirectory = File(application.cacheDir, "http-cache") @@ -37,7 +36,6 @@ class NetworkModule { } @Provides - @Singleton internal fun provideOkHttpClient( cache: Cache, loggingInterceptor: HttpLoggingInterceptor @@ -53,7 +51,6 @@ class NetworkModule { } @Provides - @Singleton internal fun provideRetrofit(gson: Gson, okHttpClient: OkHttpClient): Retrofit { return Retrofit.Builder() .addConverterFactory(GsonConverterFactory.create(gson)) diff --git a/app/src/main/java/com/ivanalvarado/template/di/module/ServiceModule.kt b/app/src/main/java/com/ivanalvarado/template/di/module/ServiceModule.kt index 110cea3..d2a9fce 100644 --- a/app/src/main/java/com/ivanalvarado/template/di/module/ServiceModule.kt +++ b/app/src/main/java/com/ivanalvarado/template/di/module/ServiceModule.kt @@ -3,14 +3,16 @@ package com.ivanalvarado.template.di.module import com.ivanalvarado.template.network.services.ExampleApiService import dagger.Module import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent import retrofit2.Retrofit import javax.inject.Singleton -@Module(includes = [NetworkModule::class]) +@Module +@InstallIn(SingletonComponent::class) class ServiceModule { @Provides - @Singleton internal fun provideExampleApiService(retrofit: Retrofit): ExampleApiService { return retrofit.create(ExampleApiService::class.java) } diff --git a/app/src/main/java/com/ivanalvarado/template/di/module/ViewModelModule.kt b/app/src/main/java/com/ivanalvarado/template/di/module/ViewModelModule.kt deleted file mode 100644 index 34f6546..0000000 --- a/app/src/main/java/com/ivanalvarado/template/di/module/ViewModelModule.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.ivanalvarado.template.di.module - -import androidx.lifecycle.ViewModel -import androidx.lifecycle.ViewModelProvider -import com.ivanalvarado.template.di.ViewModelFactory -import com.ivanalvarado.template.di.ViewModelKey -import com.ivanalvarado.template.viewmodel.ExampleViewModel -import dagger.Binds -import dagger.Module -import dagger.multibindings.IntoMap - -@Module -internal abstract class ViewModelModule { - - @Binds - internal abstract fun bindViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory - - /* - * This method basically says - * inject this object into a Map using the @IntoMap annotation, - * with the MovieListViewModel.class as key, - * and a Provider that will build a MovieListViewModel - * object. - * - * */ - @Binds - @IntoMap - @ViewModelKey(ExampleViewModel::class) - protected abstract fun exampleListViewModel(exampleListViewModel: ExampleViewModel): ViewModel -} \ No newline at end of file diff --git a/app/src/main/java/com/ivanalvarado/template/viewmodel/ExampleViewModel.kt b/app/src/main/java/com/ivanalvarado/template/viewmodel/ExampleViewModel.kt index 435951a..f28cf53 100644 --- a/app/src/main/java/com/ivanalvarado/template/viewmodel/ExampleViewModel.kt +++ b/app/src/main/java/com/ivanalvarado/template/viewmodel/ExampleViewModel.kt @@ -2,8 +2,10 @@ package com.ivanalvarado.template.viewmodel import androidx.lifecycle.ViewModel import com.ivanalvarado.template.repository.ExampleRepository +import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject +@HiltViewModel class ExampleViewModel @Inject constructor(private val exampleRepository: ExampleRepository) : ViewModel() {