Skip to content
This repository was archived by the owner on Aug 8, 2022. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ plugins {
id 'com.android.application'
id 'kotlin-android'
id 'com.google.gms.google-services'
id 'kotlin-kapt'
}

android {
Expand Down Expand Up @@ -87,4 +88,6 @@ dependencies {
implementation "io.ktor:ktor-client-cio:$ktor_version"
implementation "io.ktor:ktor-client-content-negotiation:$ktor_version"
implementation "io.ktor:ktor-serialization-gson:$ktor_version"
implementation "androidx.room:room-runtime:$room_version"
kapt("androidx.room:room-compiler:$room_version")
}
3 changes: 2 additions & 1 deletion app/src/main/java/com/cookit/AppModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ package com.cookit

import com.cookit.service.IRecipeService
import com.cookit.service.KtorRecipeService
import org.koin.android.ext.koin.androidApplication
import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.dsl.module

val appModule = module {
viewModel { MainViewModel(get()) }
single<IRecipeService> { KtorRecipeService() }
single<IRecipeService> { KtorRecipeService(androidApplication()) }
}
12 changes: 4 additions & 8 deletions app/src/main/java/com/cookit/MainViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import com.cookit.dto.Photo
import com.cookit.dto.Recipe
import com.cookit.dto.User
import com.cookit.service.IRecipeService
import com.cookit.service.KtorRecipeService
import com.cookit.service.TextFieldIngredientMapService
import com.google.firebase.firestore.FirebaseFirestore
import com.google.firebase.firestore.FirebaseFirestoreSettings
Expand All @@ -24,12 +23,12 @@ import kotlinx.coroutines.launch
* Class for the primary view model
* Used to supply [MutableLiveData] to views
*/
class MainViewModel(var recipeService: IRecipeService = KtorRecipeService()) : ViewModel() {
class MainViewModel(var recipeService: IRecipeService) : ViewModel() {

val photos: ArrayList<Photo> = ArrayList()
var user: User? = null
val NEW_RECIPE = "New Recipe"
val recipes: MutableLiveData<ArrayList<Recipe>> = MutableLiveData<ArrayList<Recipe>>()
val recipes = recipeService.getRecipeDAO().getAllRecipes()
val userRecipes: MutableLiveData<ArrayList<Recipe>> = MutableLiveData<ArrayList<Recipe>>()
var selectedRecipe by mutableStateOf(Recipe())
val ingredientMapper = TextFieldIngredientMapService()
Expand Down Expand Up @@ -70,9 +69,7 @@ class MainViewModel(var recipeService: IRecipeService = KtorRecipeService()) : V

internal fun fetchRecipes() {
viewModelScope.launch {
val innerRecipeList = recipeService.fetchRecipes()
val innerRecipes: ArrayList<Recipe> = innerRecipeList?.recipes ?: ArrayList()
recipes.postValue(innerRecipes)
recipeService.fetchRecipes()
}
}

Expand Down Expand Up @@ -176,7 +173,6 @@ class MainViewModel(var recipeService: IRecipeService = KtorRecipeService()) : V
}
else -> return listOf()
}
}
return listOf()
} ?: return listOf()
}
}
29 changes: 0 additions & 29 deletions app/src/main/java/com/cookit/RetrofitClientInstance.kt

This file was deleted.

17 changes: 17 additions & 0 deletions app/src/main/java/com/cookit/dao/IRecipeDAO.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.cookit.dao

import androidx.lifecycle.LiveData
import androidx.room.*
import com.cookit.dto.Recipe

@Dao
interface IRecipeDAO {
@Query("SELECT * FROM recipe")
fun getAllRecipes(): LiveData<List<Recipe>>

@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertAll(recipes: ArrayList<Recipe>)

@Delete
fun delete(recipe: Recipe)
}
13 changes: 13 additions & 0 deletions app/src/main/java/com/cookit/dao/RecipeDatabase.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.cookit.dao

import androidx.room.Database
import androidx.room.RoomDatabase
import androidx.room.TypeConverters
import com.cookit.dto.Recipe
import com.cookit.service.StringMapConverter

@Database(entities = [Recipe::class], version = 1)
@TypeConverters(StringMapConverter::class)
abstract class RecipeDatabase : RoomDatabase() {
abstract fun recipeDao(): IRecipeDAO
}
25 changes: 15 additions & 10 deletions app/src/main/java/com/cookit/dto/Recipe.kt
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
package com.cookit.dto

import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.Ignore
import androidx.room.PrimaryKey
import com.google.gson.annotations.Expose
import com.google.gson.annotations.SerializedName

@Entity(tableName = "recipe")
data class Recipe(
@SerializedName("idMeal")var recipeID : String = "",
@SerializedName("strMeal")var name : String = "",
@SerializedName("strCategory")var category : String = "",
@SerializedName("strArea")var cuisine : String = "",
@SerializedName("strInstructions")var instructions : String = "",
@SerializedName("strMealThumb")var imageURL : String = "",
@SerializedName("strYoutube")var youtubeURL : String = "",
@Expose var ingredients : MutableMap<String, String> = LinkedHashMap(),
@Transient var fireStoreID: String = "",
) {
@PrimaryKey @ColumnInfo(name = "id") @SerializedName("idMeal") var recipeID: String = "",
@SerializedName("strMeal") var name: String = "",
@SerializedName("strCategory") var category: String = "",
@SerializedName("strArea") var cuisine: String = "",
@SerializedName("strInstructions") var instructions: String = "",
@ColumnInfo(name = "image_url") @SerializedName("strMealThumb") var imageURL: String = "",
@ColumnInfo(name = "youtube_url") @SerializedName("strYoutube") var youtubeURL: String = "",
@Expose var ingredients: MutableMap<String, String> = LinkedHashMap(),
@Ignore @Transient var fireStoreID: String = "",
) {
override fun toString(): String {
return name
}
Expand Down
35 changes: 34 additions & 1 deletion app/src/main/java/com/cookit/service/KtorRecipeService.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
package com.cookit.service

import android.app.Application
import android.content.ContentValues.TAG
import android.util.Log
import androidx.room.Room
import com.cookit.KtorClientInstance
import com.cookit.dao.IRecipeDAO
import com.cookit.dao.RecipeDatabase
import com.cookit.dto.Recipe
import com.cookit.dto.RecipeList
import io.ktor.client.call.*
import io.ktor.client.request.*
Expand All @@ -12,18 +19,44 @@ private val CALLS = mapOf("allRecipes" to "9973533/search.php?s=")

interface IRecipeService {
suspend fun fetchRecipes(call: String = "allRecipes"): RecipeList?
fun getRecipeDAO(): IRecipeDAO
}

class KtorRecipeService : IRecipeService {
class KtorRecipeService(val application: Application) : IRecipeService {
lateinit var db: RecipeDatabase

override suspend fun fetchRecipes(call: String): RecipeList? {
return withContext(Dispatchers.IO) {
val response = KtorClientInstance.ktorInstance?.get(BASE_URL + CALLS[call])
?: throw Exception("Error!, ktorClient was null for some reason!")
if (response.status.value in 200..299) {
updateRecipes(response.body())
return@withContext response.body()
} else {
throw Exception("Failed to get recipes. Server Response: ${response.status.value}")
}
}
}

private fun updateRecipes(recipeList: RecipeList?) {
try {
val recipes = recipeList?.recipes ?: arrayListOf()
recipes.let {
val recipeDao = getRecipeDAO()
recipeDao.insertAll(recipes)
}
} catch (e: Exception) {
Log.e(TAG, "Error saving recipes")
}
}

override fun getRecipeDAO(): IRecipeDAO {
if (!this::db.isInitialized) {
db = Room.databaseBuilder(
application,
RecipeDatabase::class.java, "recipe-db"
).build()
}
return db.recipeDao()
}
}
27 changes: 27 additions & 0 deletions app/src/main/java/com/cookit/service/StringMapConverter.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.cookit.service

import androidx.room.TypeConverter
import java.util.*

class StringMapConverter {
@TypeConverter
fun fromStringMap(value: Map<String, String>): String {
val sortedMap = TreeMap(value)
return sortedMap.keys.joinToString(separator = ",").plus("<divider>")
.plus(sortedMap.values.joinToString(separator = ","))
}

@TypeConverter
fun toStringMap(value: String): Map<String, String> {
return value.split("<divider>").run {
val keys = getOrNull(0)?.split(",")?.map { it }
val values = getOrNull(1)?.split(",")?.map { it }

val res = hashMapOf<String, String>()
keys?.forEachIndexed { index, s ->
res[s] = values?.getOrNull(index) ?: ""
}
res
}
}
}
15 changes: 14 additions & 1 deletion app/src/test/java/com/cookit/RecipeIntegrationTest.kt
Original file line number Diff line number Diff line change
@@ -1,17 +1,30 @@
package com.cookit

import android.app.Application
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import com.cookit.dto.RecipeList
import com.cookit.service.KtorRecipeService
import junit.framework.Assert.assertNotNull
import junit.framework.Assert.assertTrue
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TestRule
import org.mockito.Mock
import org.mockito.MockitoAnnotations


class RecipeIntegrationTest {

@Mock
private lateinit var application: Application

@Before
fun init() {
MockitoAnnotations.initMocks(this)
}

@get:Rule
var rule: TestRule = InstantTaskExecutorRule()

Expand All @@ -35,7 +48,7 @@ class RecipeIntegrationTest {


private fun givenRecipeServiceIsInitialized() {
recipeService = KtorRecipeService()
recipeService = KtorRecipeService(application)
}

private suspend fun whenRecipeDataIsParsed() {
Expand Down
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ buildscript {
compose_version = '1.0.1'
koin_version = "3.1.5"
ktor_version = "2.0.1"
room_version = "2.4.2"
}
repositories {
google()
Expand Down