Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Android]feat: 레시피 추천 목록 화면 구현 및 API 연동(#18) #20

Merged
merged 13 commits into from
Jun 15, 2024
Merged
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
6 changes: 6 additions & 0 deletions Android/.idea/deploymentTargetSelector.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions Android/.idea/gradle.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Android/.idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Android/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,9 @@ android {

dependencies {

// feature
// modules
implementation(project(":feature:main"))
implementation(project(":feature:reciperecommend"))

implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.activity.compose)
Expand Down
5 changes: 5 additions & 0 deletions Android/core/data-api/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
plugins {
alias(libs.plugins.android.library)
alias(libs.plugins.jetbrains.kotlin.android)
alias(libs.plugins.hilt)
alias(libs.plugins.org.jetbrains.kotlin.kapt)
}

android {
Expand Down Expand Up @@ -40,4 +42,7 @@ dependencies {
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)

implementation(libs.dagger.hilt.android)
kapt(libs.dagger.hilt.compiler)
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ package com.sundaegukbap.banchango.core.data.repository.api
import com.sundaegukbap.banchango.Recipe

interface RecipeRepository {
fun getRecipeRecommendation(): List<Recipe>
suspend fun getRecipeRecommendation(): Result<List<Recipe>>
suspend fun getRecipeDetail(id: Long): Result<Recipe>
}
68 changes: 68 additions & 0 deletions Android/core/data/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import com.android.build.gradle.internal.cxx.configure.gradleLocalProperties

plugins {
alias(libs.plugins.android.library)
alias(libs.plugins.jetbrains.kotlin.android)
alias(libs.plugins.hilt)
alias(libs.plugins.org.jetbrains.kotlin.kapt)
alias(libs.plugins.kotlin.serialization)
}

android {
namespace = "com.sundaegukbap.banchango.core.data"
compileSdk = 34

defaultConfig {
minSdk = 28
buildConfigField("String", "BASE_URL", getSecretKey("base_url"))

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro")
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "17"
}
buildFeatures {
buildConfig = true
}
}

dependencies {

// modules
implementation(project(":core:model"))
implementation(project(":core:data-api"))

implementation(libs.androidx.core.ktx)
implementation(libs.androidx.appcompat)
implementation(libs.material)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)

// hilt
implementation(libs.dagger.hilt.android)
kapt(libs.dagger.hilt.compiler)

// okhttp3
implementation(libs.okhttp3.okhttp)
implementation(libs.retrofit2.kotlinx.serialization.converter)
implementation(libs.kotlinx.serialization.json)
}

fun getSecretKey(propertyKey: String): String {
return gradleLocalProperties(rootDir, providers).getProperty(propertyKey)
}
3 changes: 3 additions & 0 deletions Android/core/data/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest />

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.sundaegukbap.banchango.core.data.api

import com.sundaegukbap.banchango.core.data.api.model.RecipeRecommendResponse
import retrofit2.Response
import retrofit2.http.GET
import retrofit2.http.Path

internal interface RecipeApi {
@GET("api/recipe/recommand/{userId}")
suspend fun getRecipeRecommendation(
@Path("userId") userId: Long
): Response<List<RecipeRecommendResponse>>

@GET("api/recipe/{userId}/{recipeId}")
suspend fun getRecipeDetail(
@Path("userId") userId: Long,
@Path("recipeId") recipeId: Long
): Response<RecipeRecommendResponse>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.sundaegukbap.banchango.core.data.api.model

import kotlinx.serialization.Serializable

@Serializable
data class RecipeRecommendsResponse(
val recipeRecommends: List<RecipeRecommendResponse>
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.sundaegukbap.banchango.core.data.api.model

import kotlinx.serialization.Serializable

@Serializable
data class RecipeRecommendResponse(
val id: Long,
val name: String,
val introduction: String,
val image: String,
val link: String,
val have: List<String>,
val need: List<String>,
val servings: Int,
val cookingTime: Int,
val isBookmarked: Boolean,
val difficulty: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.sundaegukbap.banchango.core.data.di

import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
import com.sundaegukbap.banchango.core.data.BuildConfig
import com.sundaegukbap.banchango.core.data.api.RecipeApi
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import kotlinx.serialization.json.Json
import okhttp3.MediaType.Companion.toMediaType
import retrofit2.Retrofit
import javax.inject.Qualifier
import javax.inject.Singleton

@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class BaseUrlQualifier

@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class DefaultRetrofitQualifier

@Module
@InstallIn(SingletonComponent::class)
internal object ApiModule {

@Provides
@Singleton
fun provideRetrofitConverterFactory(): retrofit2.Converter.Factory {
val json = Json {
ignoreUnknownKeys = true
}
return json.asConverterFactory("application/json".toMediaType())
}

@Provides
@Singleton
@DefaultRetrofitQualifier
fun providesDefaultRetrofit(
@BaseUrlQualifier baseUrl: String,
converterFactory: retrofit2.Converter.Factory,
): Retrofit = Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(converterFactory)
.build()

@Provides
@Singleton
@BaseUrlQualifier
fun providesBaseUrl(): String = BuildConfig.BASE_URL

@Provides
@Singleton
fun providesRecipeRecommendApi(
@DefaultRetrofitQualifier retrofit: Retrofit
): RecipeApi = retrofit.create(RecipeApi::class.java)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.sundaegukbap.banchango.core.data.di

import com.sundaegukbap.banchango.core.data.repository.FakeRecipeRepository
import com.sundaegukbap.banchango.core.data.repository.api.RecipeRepository
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent

@InstallIn(SingletonComponent::class)
@Module
internal abstract class RepositoryModule {

@Binds
abstract fun bindsRecipeRepository(recipeRepository: FakeRecipeRepository): RecipeRepository
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.sundaegukbap.banchango.core.data.mapper

import com.sundaegukbap.banchango.Recipe
import com.sundaegukbap.banchango.core.data.api.model.RecipeRecommendResponse

internal fun List<RecipeRecommendResponse>.toData(): List<Recipe> = map { it.toData() }

internal fun RecipeRecommendResponse.toData(): Recipe {
return Recipe(
id = id,
name = name,
introduction = introduction,
image = image,
link = link,
have = have,
need = need,
servings = servings,
cookingTime = cookingTime,
isBookmarked = isBookmarked,
difficulty = difficulty,
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.sundaegukbap.banchango.core.data.repository

import com.sundaegukbap.banchango.Recipe
import com.sundaegukbap.banchango.core.data.api.RecipeApi
import com.sundaegukbap.banchango.core.data.mapper.toData
import com.sundaegukbap.banchango.core.data.repository.api.RecipeRepository
import javax.inject.Inject

internal class DefaultRecipeRepository @Inject constructor(
private val recipeApi: RecipeApi,
) : RecipeRepository {
override suspend fun getRecipeRecommendation(): Result<List<Recipe>> {
return runCatching {
val response = recipeApi.getRecipeRecommendation(1)
if (response.isSuccessful) {
if (response.body() == null) {
throw IllegalStateException("Response body is null")
}
response.body()!!.toData()
} else {
throw IllegalStateException("Response is not successful")
}
}
}

override suspend fun getRecipeDetail(id: Long): Result<Recipe> {
return runCatching {
val response = recipeApi.getRecipeDetail(1, id.toLong())
if (response.isSuccessful) {
if (response.body() == null) {
throw IllegalStateException("Response body is null")
}
response.body()!!.toData()
} else {
throw IllegalStateException("Response is not successful")
}
}
}
}
Loading
Loading