diff --git a/app/build.gradle b/app/build.gradle
index 0e043fc..038d9f5 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -14,6 +14,9 @@ apply from: 'jacoco.gradle'
apply from: 'lint.gradle'
apply from: 'build-scripts.gradle'
+apply plugin: 'kotlin-kapt'
+apply plugin: 'dagger.hilt.android.plugin'
+
repositories {
mavenCentral()
maven { url 'https://maven.google.com' }
@@ -171,12 +174,13 @@ ext {
lottie_version = "4.2.0"
core_version = "1.6.0"
desugar_jdk_version = "1.1.5"
+ hilt_version = "2.38.1"
+
// Compose
activity_compose_version = "1.3.1"
constraintlayout_compose_version = "1.0.0-rc01"
viewmodel_compose_version = "1.0.0-alpha07"
}
-
jacoco {
toolVersion = "0.8.7"
}
@@ -208,6 +212,8 @@ dependencies {
implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
implementation "com.squareup.retrofit2:converter-moshi:$retrofit_version"
+ implementation "com.squareup.retrofit2:converter-gson:$retrofit_version"
+
// Http Logging Interceptor
implementation "com.squareup.okhttp3:okhttp:$ok_http_version"
implementation "com.squareup.okhttp3:logging-interceptor:$ok_http_version"
@@ -292,6 +298,10 @@ dependencies {
implementation "androidx.lifecycle:lifecycle-viewmodel-compose:$viewmodel_compose_version"
implementation "com.google.android.material:compose-theme-adapter:$compose_version"
+ // Hilt: Official Google Dependency Injection Library
+ implementation "com.google.dagger:hilt-android:$hilt_version"
+ kapt "com.google.dagger:hilt-android-compiler:$hilt_version"
+
// Test
// Unit testing framework
androidTestImplementation "com.android.support.test:rules:$test_rule_version"
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 04b2829..9a52df3 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -32,11 +32,14 @@
android:networkSecurityConfig="@xml/network_security_config"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
- android:theme="@style/Theme.AndroidBase.Main">
-
+ android:theme="@style/Theme.AndroidBase.Main" >
+
-
+
diff --git a/app/src/main/java/com/rocketinsights/android/RocketApplication.kt b/app/src/main/java/com/rocketinsights/android/RocketApplication.kt
index 3de415c..adb280a 100644
--- a/app/src/main/java/com/rocketinsights/android/RocketApplication.kt
+++ b/app/src/main/java/com/rocketinsights/android/RocketApplication.kt
@@ -1,14 +1,17 @@
package com.rocketinsights.android
import android.app.Application
-import com.rocketinsights.android.di.initKoin
+import androidx.viewbinding.BuildConfig
+import com.rocketinsights.android.di.koin.initKoin
import com.rocketinsights.android.work.messages.MessagesUpdateScheduler
+import dagger.hilt.android.HiltAndroidApp
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.koin.android.ext.android.inject
import timber.log.Timber
+@HiltAndroidApp
class RocketApplication : Application() {
private val applicationScope = CoroutineScope(Dispatchers.Default)
diff --git a/app/src/main/java/com/rocketinsights/android/di/hilt/AppModule.kt b/app/src/main/java/com/rocketinsights/android/di/hilt/AppModule.kt
new file mode 100644
index 0000000..50eef66
--- /dev/null
+++ b/app/src/main/java/com/rocketinsights/android/di/hilt/AppModule.kt
@@ -0,0 +1,19 @@
+package com.rocketinsights.android.di.hilt
+
+import android.content.Context
+import com.rocketinsights.android.RocketApplication
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.android.qualifiers.ApplicationContext
+import dagger.hilt.components.SingletonComponent
+import javax.inject.Singleton
+
+@Module
+@InstallIn(SingletonComponent::class)
+object AppModule {
+ @Singleton
+ @Provides
+ fun provideApplication(@ApplicationContext app: Context): RocketApplication =
+ app as RocketApplication
+}
diff --git a/app/src/main/java/com/rocketinsights/android/di/hilt/HiltNetworkModule.kt b/app/src/main/java/com/rocketinsights/android/di/hilt/HiltNetworkModule.kt
new file mode 100644
index 0000000..4e13cad
--- /dev/null
+++ b/app/src/main/java/com/rocketinsights/android/di/hilt/HiltNetworkModule.kt
@@ -0,0 +1,57 @@
+package com.rocketinsights.android.di.hilt
+
+import com.google.gson.GsonBuilder
+import com.rocketinsights.android.models.recipe_hilt.RecipeDtoMapper
+import com.rocketinsights.android.network.hiltexample.RecipeService
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+import okhttp3.Interceptor
+import okhttp3.OkHttpClient
+import retrofit2.Retrofit
+import retrofit2.converter.gson.GsonConverterFactory
+import javax.inject.Singleton
+
+@Module
+@InstallIn(SingletonComponent::class)
+object HiltNetworkModule {
+
+ private var okHttpClient: OkHttpClient? = null
+
+ @Singleton
+ @Provides
+ fun providesRecipeMapper(): RecipeDtoMapper = RecipeDtoMapper()
+
+ @Singleton
+ @Provides
+ fun provideRecipeService(): RecipeService {
+ return Retrofit.Builder()
+ .baseUrl("https://food2fork.ca/api/recipe/")
+ .addConverterFactory(GsonConverterFactory.create(GsonBuilder().create()))
+ .client(buildClientHttp())
+ .build()
+ .create(RecipeService::class.java)
+ }
+
+ private fun buildClientHttp(): OkHttpClient? {
+ okHttpClient = OkHttpClient.Builder()
+ .addInterceptor(buildInterceptor())
+ .build()
+
+ return okHttpClient
+ }
+
+ private fun buildInterceptor() = Interceptor { chain ->
+ var request = chain.request()
+
+ request = request.newBuilder()
+ .addHeader(
+ "Authorization",
+ "Token 9c8b06d329136da358c2d00e76946b0111ce2c48"
+ )
+ .build()
+
+ chain.proceed(request = request)
+ }
+}
diff --git a/app/src/main/java/com/rocketinsights/android/di/hilt/RepositoryModule.kt b/app/src/main/java/com/rocketinsights/android/di/hilt/RepositoryModule.kt
new file mode 100644
index 0000000..e533985
--- /dev/null
+++ b/app/src/main/java/com/rocketinsights/android/di/hilt/RepositoryModule.kt
@@ -0,0 +1,28 @@
+package com.rocketinsights.android.di.hilt
+
+import com.rocketinsights.android.models.recipe_hilt.RecipeDtoMapper
+import com.rocketinsights.android.network.hiltexample.RecipeService
+import com.rocketinsights.android.repos.RecipeRepository
+import com.rocketinsights.android.repos.RecipeRepositoryImplementation
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+import javax.inject.Singleton
+
+@Module
+@InstallIn(SingletonComponent::class)
+object RepositoryModule {
+
+ @Singleton
+ @Provides
+ fun provideRecipeRepository(
+ recipeService: RecipeService,
+ recipeMapper: RecipeDtoMapper,
+ ): RecipeRepository {
+ return RecipeRepositoryImplementation(
+ recipeService = recipeService,
+ mapper = recipeMapper
+ )
+ }
+}
diff --git a/app/src/main/java/com/rocketinsights/android/di/KoinUtils.kt b/app/src/main/java/com/rocketinsights/android/di/koin/KoinUtils.kt
similarity index 99%
rename from app/src/main/java/com/rocketinsights/android/di/KoinUtils.kt
rename to app/src/main/java/com/rocketinsights/android/di/koin/KoinUtils.kt
index fb0c482..bfe2917 100644
--- a/app/src/main/java/com/rocketinsights/android/di/KoinUtils.kt
+++ b/app/src/main/java/com/rocketinsights/android/di/koin/KoinUtils.kt
@@ -1,4 +1,4 @@
-package com.rocketinsights.android.di
+package com.rocketinsights.android.di.koin
import android.app.Application
import android.app.NotificationManager
diff --git a/app/src/main/java/com/rocketinsights/android/models/recipe_hilt/Recipe.kt b/app/src/main/java/com/rocketinsights/android/models/recipe_hilt/Recipe.kt
new file mode 100644
index 0000000..2c212ed
--- /dev/null
+++ b/app/src/main/java/com/rocketinsights/android/models/recipe_hilt/Recipe.kt
@@ -0,0 +1,13 @@
+package com.rocketinsights.android.models.recipe_hilt
+
+data class Recipe(
+ val id: Int,
+ val title: String,
+ val publisher: String,
+ val featuredImage: String,
+ val rating: Int = 0,
+ val sourceUrl: String,
+ val ingredients: List,
+ val dateAdded: String,
+ val dateUpdated: String
+)
diff --git a/app/src/main/java/com/rocketinsights/android/models/recipe_hilt/RecipeDto.kt b/app/src/main/java/com/rocketinsights/android/models/recipe_hilt/RecipeDto.kt
new file mode 100644
index 0000000..b4bfbcb
--- /dev/null
+++ b/app/src/main/java/com/rocketinsights/android/models/recipe_hilt/RecipeDto.kt
@@ -0,0 +1,19 @@
+package com.rocketinsights.android.models.recipe_hilt
+
+import com.google.gson.annotations.SerializedName
+
+data class RecipeDto(
+ var pk: Int,
+ var title: String,
+ var publisher: String,
+ @SerializedName("featured_image")
+ var featuredImage: String,
+ var rating: Int = 0,
+ @SerializedName("source_url")
+ var sourceUrl: String,
+ var ingredients: List = emptyList(),
+ @SerializedName("date_added")
+ var dateAdded: String,
+ @SerializedName("date_updated")
+ var dateUpdated: String,
+)
diff --git a/app/src/main/java/com/rocketinsights/android/models/recipe_hilt/RecipeDtoMapper.kt b/app/src/main/java/com/rocketinsights/android/models/recipe_hilt/RecipeDtoMapper.kt
new file mode 100644
index 0000000..fe59dc9
--- /dev/null
+++ b/app/src/main/java/com/rocketinsights/android/models/recipe_hilt/RecipeDtoMapper.kt
@@ -0,0 +1,19 @@
+package com.rocketinsights.android.models.recipe_hilt
+
+class RecipeDtoMapper {
+ fun mapToDomainModel(model: RecipeDto): Recipe {
+ return Recipe(
+ id = model.pk,
+ title = model.title,
+ featuredImage = model.featuredImage,
+ rating = model.rating,
+ publisher = model.publisher,
+ sourceUrl = model.sourceUrl,
+ ingredients = model.ingredients,
+ dateAdded = model.dateAdded,
+ dateUpdated = model.dateUpdated,
+ )
+ }
+
+ fun toDomainList(initial: List): List = initial.map { mapToDomainModel(it) }
+}
diff --git a/app/src/main/java/com/rocketinsights/android/network/hiltexample/RecipeSearchResponse.kt b/app/src/main/java/com/rocketinsights/android/network/hiltexample/RecipeSearchResponse.kt
new file mode 100644
index 0000000..de99856
--- /dev/null
+++ b/app/src/main/java/com/rocketinsights/android/network/hiltexample/RecipeSearchResponse.kt
@@ -0,0 +1,8 @@
+package com.rocketinsights.android.network.hiltexample
+
+import com.rocketinsights.android.models.recipe_hilt.RecipeDto
+
+data class RecipeSearchResponse(
+ val count: Int,
+ val results: List,
+)
diff --git a/app/src/main/java/com/rocketinsights/android/network/hiltexample/RecipeService.kt b/app/src/main/java/com/rocketinsights/android/network/hiltexample/RecipeService.kt
new file mode 100644
index 0000000..9660347
--- /dev/null
+++ b/app/src/main/java/com/rocketinsights/android/network/hiltexample/RecipeService.kt
@@ -0,0 +1,13 @@
+package com.rocketinsights.android.network.hiltexample
+
+import retrofit2.http.GET
+import retrofit2.http.Query
+
+interface RecipeService {
+
+ @GET("search")
+ suspend fun searchRecipe(
+ @Query("page") page: Int,
+ @Query("query") query: String
+ ): RecipeSearchResponse
+}
diff --git a/app/src/main/java/com/rocketinsights/android/repos/RecipeRepository.kt b/app/src/main/java/com/rocketinsights/android/repos/RecipeRepository.kt
new file mode 100644
index 0000000..d1deecd
--- /dev/null
+++ b/app/src/main/java/com/rocketinsights/android/repos/RecipeRepository.kt
@@ -0,0 +1,7 @@
+package com.rocketinsights.android.repos
+
+import com.rocketinsights.android.models.recipe_hilt.Recipe
+
+interface RecipeRepository {
+ suspend fun search(page: Int, query: String): List?
+}
diff --git a/app/src/main/java/com/rocketinsights/android/repos/RecipeRepositoryImplementation.kt b/app/src/main/java/com/rocketinsights/android/repos/RecipeRepositoryImplementation.kt
new file mode 100644
index 0000000..df73112
--- /dev/null
+++ b/app/src/main/java/com/rocketinsights/android/repos/RecipeRepositoryImplementation.kt
@@ -0,0 +1,14 @@
+package com.rocketinsights.android.repos
+
+import com.rocketinsights.android.models.recipe_hilt.Recipe
+import com.rocketinsights.android.models.recipe_hilt.RecipeDtoMapper
+import com.rocketinsights.android.network.hiltexample.RecipeService
+
+class RecipeRepositoryImplementation(
+ private val recipeService: RecipeService,
+ private val mapper: RecipeDtoMapper,
+) : RecipeRepository {
+
+ override suspend fun search(page: Int, query: String): List =
+ mapper.toDomainList(recipeService.searchRecipe(page = page, query = query).results)
+}
diff --git a/app/src/main/java/com/rocketinsights/android/ui/HiltActivity.kt b/app/src/main/java/com/rocketinsights/android/ui/HiltActivity.kt
new file mode 100644
index 0000000..63d3852
--- /dev/null
+++ b/app/src/main/java/com/rocketinsights/android/ui/HiltActivity.kt
@@ -0,0 +1,22 @@
+package com.rocketinsights.android.ui
+
+import android.os.Bundle
+import androidx.activity.viewModels
+import androidx.appcompat.app.AppCompatActivity
+import com.rocketinsights.android.viewmodels.HiltListViewModel
+import dagger.hilt.android.AndroidEntryPoint
+
+@AndroidEntryPoint
+class HiltActivity : AppCompatActivity() {
+
+ private val viewModel: HiltListViewModel by viewModels()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // TODO: Show data with JetpackCompose in next PR
+ viewModel.liveRecipe.observe(this) { recipe ->
+ // do nothing for now
+ }
+ }
+}
diff --git a/app/src/main/java/com/rocketinsights/android/ui/MainFragment.kt b/app/src/main/java/com/rocketinsights/android/ui/MainFragment.kt
index c1898ea..2eaf86c 100644
--- a/app/src/main/java/com/rocketinsights/android/ui/MainFragment.kt
+++ b/app/src/main/java/com/rocketinsights/android/ui/MainFragment.kt
@@ -1,5 +1,6 @@
package com.rocketinsights.android.ui
+import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Bundle
@@ -126,6 +127,11 @@ class MainFragment : BaseFragment(R.layout.fragment_main) {
setFadeThroughTransition()
item.onNavDestinationSelected(findNavController())
}
+ R.id.hilt_activity -> {
+ setFadeThroughTransition()
+ Intent(this.context, HiltActivity::class.java).also { startActivity(it) }
+ true
+ }
else -> super.onOptionsItemSelected(item)
}
}
diff --git a/app/src/main/java/com/rocketinsights/android/viewmodels/HiltListViewModel.kt b/app/src/main/java/com/rocketinsights/android/viewmodels/HiltListViewModel.kt
new file mode 100644
index 0000000..84daba1
--- /dev/null
+++ b/app/src/main/java/com/rocketinsights/android/viewmodels/HiltListViewModel.kt
@@ -0,0 +1,35 @@
+package com.rocketinsights.android.viewmodels
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.rocketinsights.android.models.recipe_hilt.Recipe
+import com.rocketinsights.android.repos.RecipeRepository
+import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.launch
+import javax.inject.Inject
+
+@HiltViewModel
+class HiltListViewModel
+@Inject
+constructor(
+ private val repository: RecipeRepository
+) : ViewModel() {
+
+ private val recipes: MutableLiveData> = MutableLiveData()
+ val liveRecipe: LiveData> = recipes
+
+ init {
+ viewModelScope.launch {
+ val result = repository.search(
+ page = 1,
+ query = "beef carrot potato onion"
+ )
+
+ result?.apply {
+ recipes.value = this
+ }
+ }
+ }
+}
diff --git a/app/src/main/res/menu/main_menu.xml b/app/src/main/res/menu/main_menu.xml
index 764025a..8d3f4c2 100644
--- a/app/src/main/res/menu/main_menu.xml
+++ b/app/src/main/res/menu/main_menu.xml
@@ -29,6 +29,9 @@
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index b36d68a..9d62f0c 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -109,4 +109,7 @@
Contacts
Read contacts permission not granted
+
+
+ Hilt example
diff --git a/build.gradle b/build.gradle
index f23d4ed..c1eda9d 100644
--- a/build.gradle
+++ b/build.gradle
@@ -9,6 +9,7 @@ buildscript {
navigation_version = "2.3.5"
google_services_version = "4.3.10"
compose_version = "1.0.3"
+ hilt_version = "2.38.1"
}
repositories {
gradlePluginPortal()
@@ -22,6 +23,7 @@ buildscript {
classpath "com.google.gms:google-services:$google_services_version"
classpath "org.owasp:dependency-check-gradle:$dependency_check_version"
classpath "com.github.ben-manes:gradle-versions-plugin:$gradle_versions_version"
+ classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}