diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml
new file mode 100644
index 0000000..0c0c338
--- /dev/null
+++ b/.idea/deploymentTargetDropDown.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
index ae388c2..753085c 100644
--- a/.idea/gradle.xml
+++ b/.idea/gradle.xml
@@ -4,6 +4,18 @@
+
diff --git a/.idea/migrations.xml b/.idea/migrations.xml
new file mode 100644
index 0000000..f8051a6
--- /dev/null
+++ b/.idea/migrations.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index cae9627..ed1235e 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -1,23 +1,20 @@
+@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed
plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
+ alias(libs.plugins.pohahang.android.application)
+ alias(libs.plugins.pohahang.android.application.compose)
+ alias(libs.plugins.pohahang.android.feature)
+ alias(libs.plugins.pohahang.android.hilt)
}
android {
namespace = "com.koreatech.kwanhee_jo_compose_study"
- compileSdk = 34
defaultConfig {
applicationId = "com.koreatech.kwanhee_jo_compose_study"
- minSdk = 26
targetSdk = 34
versionCode = 1
versionName = "1.0"
- testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
- vectorDrawables {
- useSupportLibrary = true
- }
}
buildTypes {
@@ -29,57 +26,17 @@ android {
)
}
}
- compileOptions {
- sourceCompatibility = JavaVersion.VERSION_1_8
- targetCompatibility = JavaVersion.VERSION_1_8
- }
- kotlinOptions {
- jvmTarget = "1.8"
- }
- buildFeatures {
- compose = true
- }
- composeOptions {
- kotlinCompilerExtensionVersion = "1.4.3"
- }
- packaging {
- resources {
- excludes += "/META-INF/{AL2.0,LGPL2.1}"
- }
- }
}
dependencies {
-
- implementation("androidx.core:core-ktx:1.9.0")
- implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.7.0")
- implementation("androidx.activity:activity-compose:1.8.2")
- implementation(platform("androidx.compose:compose-bom:2023.03.00"))
- implementation("androidx.compose.ui:ui")
- implementation("androidx.compose.ui:ui-graphics")
- implementation("androidx.compose.ui:ui-tooling-preview")
- implementation("androidx.compose.material3:material3")
- testImplementation("junit:junit:4.13.2")
- androidTestImplementation("androidx.test.ext:junit:1.1.5")
- androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
-
- androidTestImplementation(platform("androidx.compose:compose-bom:2023.03.00"))
- androidTestImplementation("androidx.compose.ui:ui-test-junit4")
- debugImplementation("androidx.compose.ui:ui-tooling")
- debugImplementation("androidx.compose.ui:ui-test-manifest")
-
- // orbit
- implementation("org.orbit-mvi:orbit-core:6.1.1")
- implementation("org.orbit-mvi:orbit-viewmodel:6.1.1")
- implementation("org.orbit-mvi:orbit-compose:6.1.1")
- testImplementation("org.orbit-mvi:orbit-test:6.1.1")
-
- // viewmodel
- implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.6.1")
-
- // Navigation Compose
- implementation ("androidx.navigation:navigation-compose:2.4.2")
-
- // coil
- implementation("io.coil-kt:coil-compose:2.6.0")
-}
\ No newline at end of file
+ implementation(project(mapOf("path" to ":core:network")))
+ implementation(project(mapOf("path" to ":core:data")))
+
+ implementation(libs.androidx.lifecycle.runtime.compose)
+ implementation(libs.hilt.navigation.compose)
+ implementation(libs.androidx.navigation.compose)
+ testImplementation(libs.junit4)
+ testImplementation(libs.orbit.test)
+ androidTestImplementation(libs.androidx.test.ext)
+ androidTestImplementation(libs.androidx.test.espresso)
+}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index f574e81..881f0c7 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -5,6 +5,7 @@
-// NavHost(
-// navController = navController,
-// startDestination = Screen.Login.route,
-// modifier = Modifier.padding(paddingValues)
-// ) {
-// composable(route = Screen.Login.route) { entry ->
-// val loginViewModel: LoginViewModel = viewModel()
-// val loginState by loginViewModel.collectAsState()
-//
-// entry.savedStateHandle.apply {
-// loginViewModel.updateLoginSavedStateHandle(
-// id = get(ID) ?: "",
-// password = get(PASSWORD) ?: "",
-// nickname = get(NICKNAME) ?: ""
-// )
-// }
-//
-// loginViewModel.collectSideEffect {
-// when (it) {
-// is LoginSideEffect.MoveToHomePage -> {
-// navController.navigate(route = Screen.BottomNavItem.Home.route) {
-// popUpTo(navController.graph.id) {
-// inclusive = true
-// }
-// }
-// navController.currentBackStackEntry?.savedStateHandle?.apply {
-// set(ID, loginViewModel.id)
-// set(PASSWORD, loginViewModel.password)
-// set(NICKNAME, loginViewModel.nickname)
-// }
-// }
-//
-// is LoginSideEffect.MoveToSignUpPage -> {
-// navController.navigate(route = Screen.Signup.route)
-// }
-// }
-// }
-//
-// LoginPage(
-// onClickLoginButton = { id, password ->
-// loginViewModel.login(id, password)
-// }
-// ) {
-// loginViewModel.signUp()
-// }
-// }
-// composable(route = Screen.Signup.route) {
-// val signUpViewModel: SignupViewModel = viewModel()
-// val signUpState by signUpViewModel.collectAsState()
-//
-// signUpViewModel.collectSideEffect {
-// when (it) {
-// is SignupSideEffect.MoveToLoginPage -> {
-// navController.previousBackStackEntry?.savedStateHandle?.apply {
-// set(ID, it.id)
-// set(PASSWORD, it.password)
-// set(NICKNAME, it.nickname)
-// }
-// navController.popBackStack()
-// }
-// }
-// }
-//
-// SignupPage { id, password, nickname ->
-// signUpViewModel.signup(id, password, nickname)
-// }
-// }
-// composable(
-// route = Screen.BottomNavItem.Home.route
-// ) {
-// val homeViewModel: HomeViewModel = composableActivityViewModel()
-// val homeState by homeViewModel.collectAsState()
-//
-// it.savedStateHandle.apply {
-// if (get(ID)?.isNotEmpty() == true) {
-// homeViewModel.updateHomeData(
-// id = get(ID) ?: "",
-// password = get(PASSWORD) ?: "",
-// nickname = get(NICKNAME) ?: ""
-// )
-// }
-// }
-//
-// homeViewModel.collectSideEffect {
-// when (it) { }
-// }
-//
-// HomePage(
-// context = context,
-// id = homeViewModel.id,
-// password = homeViewModel.password,
-// nickname = homeViewModel.nickname,
-// modifier = Modifier
-// .padding(16.dp)
-// .fillMaxSize()
-// )
-// }
-// composable(Screen.BottomNavItem.Search.route) {
-// val homeViewModel: HomeViewModel = composableActivityViewModel()
-// SearchPage()
-// }
-// composable(Screen.BottomNavItem.Setting.route) {
-// SettingPage()
-// }
-// }
-//
-// }
}
}
}
diff --git a/app/src/main/java/com/koreatech/kwanhee_jo_compose_study/PohahangApplication.kt b/app/src/main/java/com/koreatech/kwanhee_jo_compose_study/PohahangApplication.kt
new file mode 100644
index 0000000..8464937
--- /dev/null
+++ b/app/src/main/java/com/koreatech/kwanhee_jo_compose_study/PohahangApplication.kt
@@ -0,0 +1,11 @@
+package com.koreatech.kwanhee_jo_compose_study
+
+import android.app.Application
+import dagger.hilt.android.HiltAndroidApp
+
+@HiltAndroidApp
+class PohahangApplication: Application() {
+ override fun onCreate() {
+ super.onCreate()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/koreatech/kwanhee_jo_compose_study/composable/AccountComposable.kt b/app/src/main/java/com/koreatech/kwanhee_jo_compose_study/composable/AccountComposable.kt
index b5a391d..c198d4a 100644
--- a/app/src/main/java/com/koreatech/kwanhee_jo_compose_study/composable/AccountComposable.kt
+++ b/app/src/main/java/com/koreatech/kwanhee_jo_compose_study/composable/AccountComposable.kt
@@ -1,16 +1,16 @@
package com.koreatech.kwanhee_jo_compose_study.composable
-import android.util.Log
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalFocusManager
+import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavHostController
import androidx.navigation.compose.composable
import com.koreatech.kwanhee_jo_compose_study.Constants
-import com.koreatech.kwanhee_jo_compose_study.Screen
+import com.koreatech.kwanhee_jo_compose_study.data.LoginData
+import com.koreatech.kwanhee_jo_compose_study.ui.Screen
import com.koreatech.kwanhee_jo_compose_study.utils.clearStackNavigate
-import com.koreatech.kwanhee_jo_compose_study.utils.setArgumentCurrentStack
import com.koreatech.kwanhee_jo_compose_study.utils.showToast
import com.koreatech.kwanhee_jo_compose_study.view.login.LoginPage
import com.koreatech.kwanhee_jo_compose_study.view.login.LoginSideEffect
@@ -27,18 +27,15 @@ fun NavGraphBuilder.accountComposable(navController: NavHostController) {
) {
val focusManager = LocalFocusManager.current
val context = LocalContext.current
- val loginViewModel: LoginViewModel = viewModel()
+ val loginViewModel: LoginViewModel = hiltViewModel()
// val loginState by loginViewModel.collectAsState()
loginViewModel.collectSideEffect {
when (it) {
is LoginSideEffect.MoveToHomePage -> {
navController.clearStackNavigate(Screen.BottomNavItem.Home.route)
- navController.setArgumentCurrentStack(
- mapOf(Constants.ID to it.id),
- mapOf(Constants.PASSWORD to it.password),
- mapOf(Constants.NICKNAME to it.nickname),
- )
+ val loginData = LoginData(it.id, it.password, it.nickname)
+ navController.currentBackStackEntry?.savedStateHandle?.set("loginData", loginData)
}
is LoginSideEffect.MoveToSignUpPage -> navController.navigate(route = Screen.Signup.route)
@@ -68,7 +65,6 @@ fun NavGraphBuilder.accountComposable(navController: NavHostController) {
val context = LocalContext.current
val focusManager = LocalFocusManager.current
val signupViewModel: SignupViewModel = viewModel()
- // val signupState by signupViewModel.collectAsState()
signupViewModel.collectSideEffect {
when (it) {
diff --git a/app/src/main/java/com/koreatech/kwanhee_jo_compose_study/composable/HomeComposable.kt b/app/src/main/java/com/koreatech/kwanhee_jo_compose_study/composable/HomeComposable.kt
index 48f7668..a7689c0 100644
--- a/app/src/main/java/com/koreatech/kwanhee_jo_compose_study/composable/HomeComposable.kt
+++ b/app/src/main/java/com/koreatech/kwanhee_jo_compose_study/composable/HomeComposable.kt
@@ -1,15 +1,20 @@
package com.koreatech.kwanhee_jo_compose_study.composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
import androidx.compose.ui.platform.LocalContext
+import androidx.hilt.navigation.compose.hiltViewModel
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavHostController
import androidx.navigation.compose.composable
-import com.koreatech.kwanhee_jo_compose_study.Constants
-import com.koreatech.kwanhee_jo_compose_study.Screen
+import com.koreatech.kwanhee_jo_compose_study.data.LoginData
+import com.koreatech.kwanhee_jo_compose_study.ui.Screen
import com.koreatech.kwanhee_jo_compose_study.view.composableActivityViewModel
import com.koreatech.kwanhee_jo_compose_study.view.home.HomePage
import com.koreatech.kwanhee_jo_compose_study.view.home.HomeViewModel
import com.koreatech.kwanhee_jo_compose_study.view.navi.serach.SearchPage
+import com.koreatech.kwanhee_jo_compose_study.view.navi.serach.SearchViewModel
import com.koreatech.kwanhee_jo_compose_study.view.navi.setting.SettingPage
fun NavGraphBuilder.homeComposable(navController: NavHostController) {
@@ -18,37 +23,26 @@ fun NavGraphBuilder.homeComposable(navController: NavHostController) {
) {
val context = LocalContext.current
val homeViewModel: HomeViewModel = composableActivityViewModel()
- // val homeState by homeViewModel.collectAsState()
- it.savedStateHandle.apply {
- val id = get(Constants.ID)
- val password = get(Constants.PASSWORD)
- val nickname = get(Constants.NICKNAME)
- if (id != null || password != null || nickname != null) {
- homeViewModel.updateUserData(
- id ?: "", password ?: "", nickname ?: ""
- )
- HomePage(
- context = context,
- id = id ?: "",
- password = password ?: "",
- nickname = nickname ?: ""
- )
- } else {
- HomePage(
- context = context,
- id = homeViewModel.id,
- password = homeViewModel.password,
- nickname = homeViewModel.nickname
- )
- }
+ val state by homeViewModel.loginData.collectAsState()
+ val loginData = it.savedStateHandle.get("loginData")
+ if (loginData != null) {
+ homeViewModel.updateUserData(loginData)
}
+ HomePage(
+ context = context,
+ id = state.id,
+ password = state.password,
+ nickname = state.nickname
+ )
}
composable(route = Screen.BottomNavItem.Search.route) {
- SearchPage()
- }
- composable(route = Screen.BottomNavItem.Setting.route) {
- SettingPage()
+ val context = LocalContext.current
+ val searchViewModel: SearchViewModel = hiltViewModel()
+ val cats by searchViewModel.cats.collectAsStateWithLifecycle()
+
+ SearchPage(context, cats)
}
+ composable(route = Screen.BottomNavItem.Setting.route) { SettingPage() }
}
diff --git a/app/src/main/java/com/koreatech/kwanhee_jo_compose_study/data/LoginData.kt b/app/src/main/java/com/koreatech/kwanhee_jo_compose_study/data/LoginData.kt
new file mode 100644
index 0000000..3c50301
--- /dev/null
+++ b/app/src/main/java/com/koreatech/kwanhee_jo_compose_study/data/LoginData.kt
@@ -0,0 +1,12 @@
+package com.koreatech.kwanhee_jo_compose_study.data
+
+import android.os.Parcelable
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+data class LoginData(
+ val id: String = "",
+ val password: String ="",
+ val nickname: String =""
+): Parcelable
+
diff --git a/app/src/main/java/com/koreatech/kwanhee_jo_compose_study/Screen.kt b/app/src/main/java/com/koreatech/kwanhee_jo_compose_study/ui/Screen.kt
similarity index 92%
rename from app/src/main/java/com/koreatech/kwanhee_jo_compose_study/Screen.kt
rename to app/src/main/java/com/koreatech/kwanhee_jo_compose_study/ui/Screen.kt
index 6f06199..65044d8 100644
--- a/app/src/main/java/com/koreatech/kwanhee_jo_compose_study/Screen.kt
+++ b/app/src/main/java/com/koreatech/kwanhee_jo_compose_study/ui/Screen.kt
@@ -1,8 +1,9 @@
-package com.koreatech.kwanhee_jo_compose_study
+package com.koreatech.kwanhee_jo_compose_study.ui
import androidx.navigation.NamedNavArgument
import androidx.navigation.NavType
import androidx.navigation.navArgument
+import com.koreatech.kwanhee_jo_compose_study.R
sealed class Screen(
val route: String,
diff --git a/app/src/main/java/com/koreatech/kwanhee_jo_compose_study/SoptApp.kt b/app/src/main/java/com/koreatech/kwanhee_jo_compose_study/ui/SoptApp.kt
similarity index 85%
rename from app/src/main/java/com/koreatech/kwanhee_jo_compose_study/SoptApp.kt
rename to app/src/main/java/com/koreatech/kwanhee_jo_compose_study/ui/SoptApp.kt
index ecfd27b..6017ef4 100644
--- a/app/src/main/java/com/koreatech/kwanhee_jo_compose_study/SoptApp.kt
+++ b/app/src/main/java/com/koreatech/kwanhee_jo_compose_study/ui/SoptApp.kt
@@ -1,4 +1,4 @@
-package com.koreatech.kwanhee_jo_compose_study
+package com.koreatech.kwanhee_jo_compose_study.ui
import androidx.compose.runtime.Composable
import androidx.navigation.compose.rememberNavController
diff --git a/app/src/main/java/com/koreatech/kwanhee_jo_compose_study/view/SoptNavHost.kt b/app/src/main/java/com/koreatech/kwanhee_jo_compose_study/view/SoptNavHost.kt
index 9bf7e83..19e2f0a 100644
--- a/app/src/main/java/com/koreatech/kwanhee_jo_compose_study/view/SoptNavHost.kt
+++ b/app/src/main/java/com/koreatech/kwanhee_jo_compose_study/view/SoptNavHost.kt
@@ -13,7 +13,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.currentBackStackEntryAsState
-import com.koreatech.kwanhee_jo_compose_study.Screen
+import com.koreatech.kwanhee_jo_compose_study.ui.Screen
import com.koreatech.kwanhee_jo_compose_study.composable.accountComposable
import com.koreatech.kwanhee_jo_compose_study.composable.homeComposable
import com.koreatech.kwanhee_jo_compose_study.view.navi.BottomNavigationBar
diff --git a/app/src/main/java/com/koreatech/kwanhee_jo_compose_study/view/home/HomeState.kt b/app/src/main/java/com/koreatech/kwanhee_jo_compose_study/view/home/HomeState.kt
index 998c2ca..c81e6af 100644
--- a/app/src/main/java/com/koreatech/kwanhee_jo_compose_study/view/home/HomeState.kt
+++ b/app/src/main/java/com/koreatech/kwanhee_jo_compose_study/view/home/HomeState.kt
@@ -1,7 +1,10 @@
package com.koreatech.kwanhee_jo_compose_study.view.home
+import com.koreatech.data.model.response.CatResponseDto
import com.koreatech.kwanhee_jo_compose_study.common.UiStatus
+
data class HomeState(
- val status: UiStatus = UiStatus.Loading
+ val status: UiStatus = UiStatus.Loading,
+ val cats: List = emptyList()
)
\ No newline at end of file
diff --git a/app/src/main/java/com/koreatech/kwanhee_jo_compose_study/view/home/HomeViewModel.kt b/app/src/main/java/com/koreatech/kwanhee_jo_compose_study/view/home/HomeViewModel.kt
index c2804d1..f7f17a6 100644
--- a/app/src/main/java/com/koreatech/kwanhee_jo_compose_study/view/home/HomeViewModel.kt
+++ b/app/src/main/java/com/koreatech/kwanhee_jo_compose_study/view/home/HomeViewModel.kt
@@ -3,27 +3,38 @@ package com.koreatech.kwanhee_jo_compose_study.view.home
import android.util.Log
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
-import com.koreatech.kwanhee_jo_compose_study.Constants.ID
-import com.koreatech.kwanhee_jo_compose_study.Constants.NICKNAME
-import com.koreatech.kwanhee_jo_compose_study.Constants.PASSWORD
+import androidx.lifecycle.viewModelScope
+import com.koreatech.core.network.repository.PohahangRepository
+import com.koreatech.data.model.response.CatResponseDto
+import com.koreatech.kwanhee_jo_compose_study.data.LoginData
+import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.CancellableContinuation
+import kotlinx.coroutines.CancellationException
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
import org.orbitmvi.orbit.Container
import org.orbitmvi.orbit.ContainerHost
import org.orbitmvi.orbit.syntax.simple.intent
+import org.orbitmvi.orbit.syntax.simple.reduce
import org.orbitmvi.orbit.viewmodel.container
+import javax.inject.Inject
-class HomeViewModel (
- savedStateHandle: SavedStateHandle
-): ContainerHost, ViewModel() {
- override val container: Container
- = container(HomeState())
+@HiltViewModel
+class HomeViewModel @Inject constructor(
+ private val pohahangRepository: PohahangRepository,
+) : ContainerHost, ViewModel() {
+ override val container: Container = container(HomeState())
- var id : String = ""
- var password: String = ""
- var nickname: String = ""
- fun updateUserData(id: String, password: String, nickname: String) {
- this.id = id
- this.password = password
- this.nickname = nickname
+
+
+ val loginData = MutableStateFlow(LoginData())
+
+ fun updateUserData(loginData: LoginData) {
+ this.loginData.value = loginData
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/koreatech/kwanhee_jo_compose_study/view/login/LoginViewModel.kt b/app/src/main/java/com/koreatech/kwanhee_jo_compose_study/view/login/LoginViewModel.kt
index 4c3b106..07bc3ad 100644
--- a/app/src/main/java/com/koreatech/kwanhee_jo_compose_study/view/login/LoginViewModel.kt
+++ b/app/src/main/java/com/koreatech/kwanhee_jo_compose_study/view/login/LoginViewModel.kt
@@ -1,31 +1,59 @@
package com.koreatech.kwanhee_jo_compose_study.view.login
-import android.util.Log
-import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
-import com.koreatech.kwanhee_jo_compose_study.Constants.ID
-import com.koreatech.kwanhee_jo_compose_study.Constants.NICKNAME
-import com.koreatech.kwanhee_jo_compose_study.Constants.PASSWORD
+import androidx.lifecycle.viewModelScope
+import com.koreatech.core.network.repository.PohahangRepository
+import com.koreatech.data.model.request.LoginRequestDto
+import com.koreatech.data.model.response.CatResponseDto
import com.koreatech.kwanhee_jo_compose_study.common.UiStatus
+import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
import org.orbitmvi.orbit.Container
import org.orbitmvi.orbit.ContainerHost
import org.orbitmvi.orbit.syntax.simple.intent
import org.orbitmvi.orbit.syntax.simple.postSideEffect
import org.orbitmvi.orbit.syntax.simple.reduce
import org.orbitmvi.orbit.viewmodel.container
+import javax.inject.Inject
-class LoginViewModel() : ContainerHost, ViewModel() {
+@HiltViewModel
+class LoginViewModel @Inject constructor(
+) : ContainerHost, ViewModel() {
override val container: Container = container(LoginState())
- fun login(inputId: String, receiveId: String, inputPassword: String, receivePassword: String, receiveNickname: String) {
+
+
+ fun login(
+ inputId: String,
+ receiveId: String,
+ inputPassword: String,
+ receivePassword: String,
+ receiveNickname: String,
+ ) {
intent {
when {
- (inputId.isEmpty() && inputPassword.isEmpty()) -> postSideEffect(LoginSideEffect.ShowToast("로그인 실패"))
+ (inputId.isEmpty() && inputPassword.isEmpty()) -> postSideEffect(
+ LoginSideEffect.ShowToast(
+ "로그인 실패"
+ )
+ )
+
inputId != receiveId -> postSideEffect(LoginSideEffect.ShowToast("아이디 실패"))
inputPassword != receivePassword -> postSideEffect(LoginSideEffect.ShowToast("비밀번호 실패"))
else -> {
reduce { state.copy(status = UiStatus.Success) }
- postSideEffect(LoginSideEffect.MoveToHomePage(inputId, inputPassword, receiveNickname))
+ postSideEffect(
+ LoginSideEffect.MoveToHomePage(
+ inputId,
+ inputPassword,
+ receiveNickname
+ )
+ )
}
}
}
diff --git a/app/src/main/java/com/koreatech/kwanhee_jo_compose_study/view/navi/BottomNavigationBar.kt b/app/src/main/java/com/koreatech/kwanhee_jo_compose_study/view/navi/BottomNavigationBar.kt
index 9b439ce..6bc9ae7 100644
--- a/app/src/main/java/com/koreatech/kwanhee_jo_compose_study/view/navi/BottomNavigationBar.kt
+++ b/app/src/main/java/com/koreatech/kwanhee_jo_compose_study/view/navi/BottomNavigationBar.kt
@@ -7,7 +7,7 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.painterResource
import androidx.navigation.NavHostController
-import com.koreatech.kwanhee_jo_compose_study.Screen
+import com.koreatech.kwanhee_jo_compose_study.ui.Screen
import com.koreatech.kwanhee_jo_compose_study.view.currentRoute
@Composable
diff --git a/app/src/main/java/com/koreatech/kwanhee_jo_compose_study/view/navi/serach/SearchPage.kt b/app/src/main/java/com/koreatech/kwanhee_jo_compose_study/view/navi/serach/SearchPage.kt
index 5e1cb8f..7be7863 100644
--- a/app/src/main/java/com/koreatech/kwanhee_jo_compose_study/view/navi/serach/SearchPage.kt
+++ b/app/src/main/java/com/koreatech/kwanhee_jo_compose_study/view/navi/serach/SearchPage.kt
@@ -1,10 +1,71 @@
package com.koreatech.kwanhee_jo_compose_study.view.navi.serach
+import android.content.Context
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.wrapContentHeight
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.grid.GridCells
+import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
+import androidx.compose.foundation.lazy.grid.items
+import androidx.compose.foundation.lazy.items
+import androidx.compose.foundation.lazy.staggeredgrid.LazyVerticalStaggeredGrid
+import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells
+import androidx.compose.foundation.lazy.staggeredgrid.items
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import coil.compose.AsyncImage
+import coil.request.ImageRequest
+import com.koreatech.data.model.response.CatResponseDto
@Composable
-fun SearchPage(modifier: Modifier = Modifier) {
- Text(text = "Search")
+fun SearchPage(
+ context: Context,
+ cats: List,
+ modifier: Modifier = Modifier,
+) {
+ Column(
+ modifier = modifier.fillMaxSize()
+ ) {
+ Text(
+ text = "고양이 보실?",
+ fontSize = 24.sp,
+ fontWeight = FontWeight.Bold,
+ textAlign = TextAlign.Center,
+ modifier = Modifier.fillMaxWidth()
+ )
+ Spacer(modifier = Modifier.height(10.dp))
+ LazyVerticalStaggeredGrid(
+ columns = StaggeredGridCells.Adaptive(100.dp),
+ verticalItemSpacing = 4.dp,
+ horizontalArrangement = Arrangement.spacedBy(4.dp),
+ content = {
+ items(cats) {
+ AsyncImage(
+ model = ImageRequest.Builder(context)
+ .data(it.url)
+ .crossfade(true)
+ .build(),
+ contentScale = ContentScale.Crop,
+ contentDescription = null,
+ modifier = Modifier
+ .fillMaxWidth()
+ .wrapContentHeight()
+ )
+ }
+ },
+ modifier = Modifier.fillMaxSize()
+ )
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/koreatech/kwanhee_jo_compose_study/view/navi/serach/SearchViewModel.kt b/app/src/main/java/com/koreatech/kwanhee_jo_compose_study/view/navi/serach/SearchViewModel.kt
new file mode 100644
index 0000000..fba02bd
--- /dev/null
+++ b/app/src/main/java/com/koreatech/kwanhee_jo_compose_study/view/navi/serach/SearchViewModel.kt
@@ -0,0 +1,30 @@
+package com.koreatech.kwanhee_jo_compose_study.view.navi.serach
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.koreatech.core.network.repository.PohahangRepository
+import com.koreatech.data.model.response.CatResponseDto
+import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.CancellationException
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.launch
+import javax.inject.Inject
+
+@HiltViewModel
+class SearchViewModel @Inject constructor(
+ private val pohahangRepository: PohahangRepository,
+) : ViewModel() {
+ val _cats: MutableStateFlow> = MutableStateFlow(emptyList())
+ val cats = _cats.asStateFlow()
+
+ init {
+ viewModelScope.launch {
+ try {
+ _cats.emit(pohahangRepository.getCats())
+ } catch (e: CancellationException) {
+
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/build-logic/convention/.gitignore b/build-logic/convention/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/build-logic/convention/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/build-logic/convention/build.gradle.kts b/build-logic/convention/build.gradle.kts
new file mode 100644
index 0000000..de53fc9
--- /dev/null
+++ b/build-logic/convention/build.gradle.kts
@@ -0,0 +1,39 @@
+@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed
+plugins {
+ `kotlin-dsl`
+}
+
+java {
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
+}
+
+dependencies {
+ compileOnly(libs.android.gradlePlugin)
+ compileOnly(libs.kotlin.gradlePlugin)
+}
+
+gradlePlugin {
+ plugins {
+ register("AndroidApplicationConventionPlugin") {
+ id = "pohahang.android.application"
+ implementationClass = "AndroidApplicationConventionPlugin"
+ }
+ register("AndroidApplicationComposeConventionPlugin") {
+ id = "pohahang.android.application.compose"
+ implementationClass = "AndroidApplicationComposeConventionPlugin"
+ }
+ register("AndroidFeatureConventionPlugin") {
+ id = "pohahang.android.plugin.feature"
+ implementationClass = "AndroidFeatureConventionPlugin"
+ }
+ register("AndroidHiltConventionPlugin") {
+ id = "pohahang.android.plugin.hilt"
+ implementationClass = "AndroidHiltConventionPlugin"
+ }
+ register("AndroidLibraryConventionPlugin") {
+ id = "pohahang.android.library"
+ implementationClass = "AndroidLibraryConventionPlugin"
+ }
+ }
+}
\ No newline at end of file
diff --git a/build-logic/convention/src/main/java/AndroidApplicationComposeConventionPlugin.kt b/build-logic/convention/src/main/java/AndroidApplicationComposeConventionPlugin.kt
new file mode 100644
index 0000000..af42186
--- /dev/null
+++ b/build-logic/convention/src/main/java/AndroidApplicationComposeConventionPlugin.kt
@@ -0,0 +1,19 @@
+import com.android.build.gradle.internal.dsl.BaseAppModuleExtension
+import com.koreatech.convention.configureAndroidCompose
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.kotlin.dsl.getByType
+
+internal class AndroidApplicationComposeConventionPlugin: Plugin {
+ override fun apply(target: Project) {
+ with(target) {
+ with(pluginManager) {
+ apply("com.android.application")
+ apply("org.jetbrains.kotlin.android")
+ apply("kotlin-parcelize")
+ }
+ val extension = extensions.getByType()
+ configureAndroidCompose(extension)
+ }
+ }
+}
\ No newline at end of file
diff --git a/build-logic/convention/src/main/java/AndroidApplicationConventionPlugin.kt b/build-logic/convention/src/main/java/AndroidApplicationConventionPlugin.kt
new file mode 100644
index 0000000..7e68526
--- /dev/null
+++ b/build-logic/convention/src/main/java/AndroidApplicationConventionPlugin.kt
@@ -0,0 +1,19 @@
+import com.android.build.api.dsl.ApplicationExtension
+import com.koreatech.convention.configureAndroidProject
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.kotlin.dsl.configure
+
+internal class AndroidApplicationConventionPlugin : Plugin {
+ override fun apply(target: Project) {
+ with(target) {
+ with(pluginManager) {
+ apply("com.android.application")
+ apply("org.jetbrains.kotlin.android")
+ }
+ extensions.configure {
+ configureAndroidProject(this)
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/build-logic/convention/src/main/java/AndroidFeatureConventionPlugin.kt b/build-logic/convention/src/main/java/AndroidFeatureConventionPlugin.kt
new file mode 100644
index 0000000..0bc5943
--- /dev/null
+++ b/build-logic/convention/src/main/java/AndroidFeatureConventionPlugin.kt
@@ -0,0 +1,21 @@
+import com.koreatech.convention.implementation
+import com.koreatech.convention.libs
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.kotlin.dsl.dependencies
+
+internal class AndroidFeatureConventionPlugin: Plugin {
+ override fun apply(target: Project) {
+ with(target) {
+ dependencies {
+ implementation(libs.findLibrary("androidx.core.ktx").get())
+ implementation(libs.findLibrary("androidx.lifecycle.runtime.ktx").get())
+ implementation(libs.findLibrary("androidx.activity.compose").get())
+ implementation(libs.findLibrary("androidx.lifecycle.viewmodel.compose").get())
+ implementation(libs.findLibrary("androidx.navigation.compose").get())
+ implementation(libs.findLibrary("coil.compose").get())
+ implementation(libs.findBundle("orbit").get())
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/build-logic/convention/src/main/java/AndroidHiltConventionPlugin.kt b/build-logic/convention/src/main/java/AndroidHiltConventionPlugin.kt
new file mode 100644
index 0000000..8a3d9a5
--- /dev/null
+++ b/build-logic/convention/src/main/java/AndroidHiltConventionPlugin.kt
@@ -0,0 +1,21 @@
+import com.koreatech.convention.implementation
+import com.koreatech.convention.kapt
+import com.koreatech.convention.libs
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.kotlin.dsl.dependencies
+
+internal class AndroidHiltConventionPlugin: Plugin {
+ override fun apply(target: Project) {
+ with(target) {
+ with(pluginManager) {
+ apply("dagger.hilt.android.plugin")
+ apply("kotlin-kapt")
+ }
+ dependencies {
+ implementation(libs.findLibrary("hilt.android").get())
+ kapt(libs.findLibrary("hilt.compiler").get())
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/build-logic/convention/src/main/java/AndroidLibraryConventionPlugin.kt b/build-logic/convention/src/main/java/AndroidLibraryConventionPlugin.kt
new file mode 100644
index 0000000..7ffe3ee
--- /dev/null
+++ b/build-logic/convention/src/main/java/AndroidLibraryConventionPlugin.kt
@@ -0,0 +1,20 @@
+import com.android.build.api.dsl.LibraryExtension
+import com.koreatech.convention.configureAndroidProject
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.kotlin.dsl.configure
+
+class AndroidLibraryConventionPlugin: Plugin {
+ override fun apply(target: Project) {
+ with(target) {
+ with(pluginManager) {
+ apply("com.android.library")
+ apply("org.jetbrains.kotlin.android")
+ }
+ extensions.configure {
+ configureAndroidProject(this)
+ defaultConfig.targetSdk = 34
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/build-logic/convention/src/main/java/com/koreatech/convention/AndroidCompose.kt b/build-logic/convention/src/main/java/com/koreatech/convention/AndroidCompose.kt
new file mode 100644
index 0000000..130379b
--- /dev/null
+++ b/build-logic/convention/src/main/java/com/koreatech/convention/AndroidCompose.kt
@@ -0,0 +1,36 @@
+package com.koreatech.convention
+
+import com.android.build.api.dsl.CommonExtension
+import org.gradle.api.Project
+import org.gradle.api.artifacts.VersionCatalogsExtension
+import org.gradle.kotlin.dsl.dependencies
+import org.gradle.kotlin.dsl.getByType
+
+internal fun Project.configureAndroidCompose(
+ commonExtension: CommonExtension<*, *, *, *, *>,
+) {
+ val libs = extensions.getByType().named("libs")
+
+ commonExtension.apply {
+ composeOptions {
+ kotlinCompilerExtensionVersion =
+ libs.findVersion("composeCompilerVersion").get().requiredVersion
+ }
+ buildFeatures {
+ compose = true
+ }
+
+ packaging {
+ resources {
+ excludes += "/META-INF/{AL2.0,LGPL2.1}"
+ }
+ }
+ }
+ dependencies {
+ api(platform(libs.findLibrary("androidx.compose.bom").get()))
+ implementation(libs.findBundle("compose").get())
+ debugImplementation(libs.findLibrary("androidx.compose.ui.test.manifest").get())
+ androidTestImplementation(libs.findLibrary("androidx.compose.ui.test").get())
+ }
+}
+
diff --git a/build-logic/convention/src/main/java/com/koreatech/convention/DependencyHandlerExtensions.kt b/build-logic/convention/src/main/java/com/koreatech/convention/DependencyHandlerExtensions.kt
new file mode 100644
index 0000000..5cbbbc6
--- /dev/null
+++ b/build-logic/convention/src/main/java/com/koreatech/convention/DependencyHandlerExtensions.kt
@@ -0,0 +1,26 @@
+package com.koreatech.convention
+
+import org.gradle.api.artifacts.dsl.DependencyHandler
+
+fun DependencyHandler.kapt(dependency: Any) {
+ add("kapt", dependency)
+}
+fun DependencyHandler.implementation(dependency: Any) {
+ add("implementation", dependency)
+}
+
+fun DependencyHandler.debugImplementation(dependency: Any) {
+ add("debugImplementation", dependency)
+}
+
+fun DependencyHandler.api(dependency: Any) {
+ add("api", dependency)
+}
+
+fun DependencyHandler.androidTestImplementation(dependency: Any) {
+ add("androidTestImplementation", dependency)
+}
+
+fun DependencyHandler.testImplementation(dependency: Any) {
+ add("testImplementation", dependency)
+}
\ No newline at end of file
diff --git a/build-logic/convention/src/main/java/com/koreatech/convention/KotlinAndroid.kt b/build-logic/convention/src/main/java/com/koreatech/convention/KotlinAndroid.kt
new file mode 100644
index 0000000..b604d9f
--- /dev/null
+++ b/build-logic/convention/src/main/java/com/koreatech/convention/KotlinAndroid.kt
@@ -0,0 +1,59 @@
+package com.koreatech.convention
+
+import com.android.build.api.dsl.CommonExtension
+import org.gradle.api.JavaVersion
+import org.gradle.api.Project
+import org.gradle.api.plugins.JavaPluginExtension
+import org.gradle.kotlin.dsl.configure
+import org.gradle.kotlin.dsl.provideDelegate
+import org.gradle.kotlin.dsl.withType
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+
+internal fun Project.configureAndroidProject(
+ commonExtension: CommonExtension<*, *, *, *, *>,
+) {
+ commonExtension.apply {
+
+ compileSdk = 34
+
+ defaultConfig {
+ minSdk = 26
+
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+ vectorDrawables.useSupportLibrary = true
+ }
+ buildFeatures {
+ buildConfig = true
+ }
+
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
+ }
+ }
+ configureKotlinJvm()
+}
+
+internal fun Project.configureKotlinJvm() {
+ extensions.configure {
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
+ }
+ configureKotlin()
+}
+
+private fun Project.configureKotlin() {
+ tasks.withType().configureEach {
+ kotlinOptions {
+ jvmTarget = JavaVersion.VERSION_17.toString()
+ // Treat all Kotlin warnings as errors (disabled by default)
+ // Override by setting warningsAsErrors=true in your ~/.gradle/gradle.properties
+ val warningsAsErrors: String? by project
+ allWarningsAsErrors = warningsAsErrors.toBoolean()
+ freeCompilerArgs = freeCompilerArgs + listOf(
+ // Enable experimental coroutines APIs, including Flow
+ "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/build-logic/convention/src/main/java/com/koreatech/convention/ProjectExtensions.kt b/build-logic/convention/src/main/java/com/koreatech/convention/ProjectExtensions.kt
new file mode 100644
index 0000000..3d359b7
--- /dev/null
+++ b/build-logic/convention/src/main/java/com/koreatech/convention/ProjectExtensions.kt
@@ -0,0 +1,9 @@
+package com.koreatech.convention
+
+import org.gradle.api.Project
+import org.gradle.api.artifacts.VersionCatalog
+import org.gradle.api.artifacts.VersionCatalogsExtension
+import org.gradle.kotlin.dsl.getByType
+
+val Project.libs
+ get(): VersionCatalog = extensions.getByType().named("libs")
\ No newline at end of file
diff --git a/build-logic/settings.gradle.kts b/build-logic/settings.gradle.kts
new file mode 100644
index 0000000..6225785
--- /dev/null
+++ b/build-logic/settings.gradle.kts
@@ -0,0 +1,14 @@
+dependencyResolutionManagement {
+ repositories {
+ google()
+ mavenCentral()
+ }
+ versionCatalogs {
+ create("libs") {
+ from(files("../gradle/libs.versions.toml"))
+ }
+ }
+}
+
+rootProject.name = "build-logic"
+include(":convention")
\ No newline at end of file
diff --git a/build.gradle.kts b/build.gradle.kts
index 5873551..ddd5032 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -1,5 +1,15 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
+buildscript {
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
plugins {
- id("com.android.application") version "8.1.4" apply false
- id("org.jetbrains.kotlin.android") version "1.8.10" apply false
+ alias(libs.plugins.android.application) apply false
+ alias(libs.plugins.kotlin.anroid) apply false
+ alias(libs.plugins.hilt) apply false
+ alias(libs.plugins.org.jetbrains.kotlin.jvm) apply false
+ alias(libs.plugins.com.android.library) apply false
+ alias(libs.plugins.org.jetbrains.kotlin.serialization) apply false
}
\ No newline at end of file
diff --git a/core/data/.gitignore b/core/data/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/core/data/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/core/data/build.gradle.kts b/core/data/build.gradle.kts
new file mode 100644
index 0000000..5321258
--- /dev/null
+++ b/core/data/build.gradle.kts
@@ -0,0 +1,26 @@
+@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed
+plugins {
+ alias(libs.plugins.pohahang.android.library)
+ alias(libs.plugins.pohahang.android.hilt)
+}
+
+android {
+ namespace = "com.koreatech.core.data"
+
+ buildTypes {
+ release {
+ isMinifyEnabled = false
+ proguardFiles(
+ getDefaultProguardFile("proguard-android-optimize.txt"),
+ "proguard-rules.pro"
+ )
+ }
+ }
+}
+
+dependencies {
+ implementation(libs.kotlinx.serialization.json)
+ implementation(libs.retrofit.core)
+ implementation(libs.retrofit.kotlin.serialization)
+ implementation(libs.okhttp.logging)
+}
\ No newline at end of file
diff --git a/core/data/consumer-rules.pro b/core/data/consumer-rules.pro
new file mode 100644
index 0000000..e69de29
diff --git a/core/data/proguard-rules.pro b/core/data/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /dev/null
+++ b/core/data/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/core/data/src/androidTest/java/com/koreatech/data/ExampleInstrumentedTest.kt b/core/data/src/androidTest/java/com/koreatech/data/ExampleInstrumentedTest.kt
new file mode 100644
index 0000000..5b60238
--- /dev/null
+++ b/core/data/src/androidTest/java/com/koreatech/data/ExampleInstrumentedTest.kt
@@ -0,0 +1,24 @@
+package com.koreatech.data
+
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.ext.junit.runners.AndroidJUnit4
+
+import org.junit.Test
+import org.junit.runner.RunWith
+
+import org.junit.Assert.*
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+@RunWith(AndroidJUnit4::class)
+class ExampleInstrumentedTest {
+ @Test
+ fun useAppContext() {
+ // Context of the app under test.
+ val appContext = InstrumentationRegistry.getInstrumentation().targetContext
+ assertEquals("com.koreatech.data.test", appContext.packageName)
+ }
+}
\ No newline at end of file
diff --git a/core/data/src/main/AndroidManifest.xml b/core/data/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..a5918e6
--- /dev/null
+++ b/core/data/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/core/data/src/main/java/com/koreatech/data/model/request/LoginRequestDto.kt b/core/data/src/main/java/com/koreatech/data/model/request/LoginRequestDto.kt
new file mode 100644
index 0000000..57e7e1e
--- /dev/null
+++ b/core/data/src/main/java/com/koreatech/data/model/request/LoginRequestDto.kt
@@ -0,0 +1,9 @@
+package com.koreatech.data.model.request
+
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class LoginRequestDto(
+ val username: String,
+ val password: String,
+)
diff --git a/core/data/src/main/java/com/koreatech/data/model/request/SignupRequestDto.kt b/core/data/src/main/java/com/koreatech/data/model/request/SignupRequestDto.kt
new file mode 100644
index 0000000..6778320
--- /dev/null
+++ b/core/data/src/main/java/com/koreatech/data/model/request/SignupRequestDto.kt
@@ -0,0 +1,11 @@
+package com.koreatech.data.model.request
+
+import kotlinx.serialization.Serializable
+
+
+@Serializable
+data class SignupRequestDto(
+ val username: String,
+ val password: String,
+ val nickname: String,
+)
diff --git a/core/data/src/main/java/com/koreatech/data/model/response/CatResponseDto.kt b/core/data/src/main/java/com/koreatech/data/model/response/CatResponseDto.kt
new file mode 100644
index 0000000..ffe812e
--- /dev/null
+++ b/core/data/src/main/java/com/koreatech/data/model/response/CatResponseDto.kt
@@ -0,0 +1,15 @@
+package com.koreatech.data.model.response
+
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+
+data class CatResponseDto(
+ @SerialName("id")
+ val id: String,
+ @SerialName("url")
+ val url: String,
+ @SerialName("width")
+ val width: Int,
+ @SerialName("height")
+ val height: Int,
+)
diff --git a/core/data/src/test/java/com/koreatech/data/ExampleUnitTest.kt b/core/data/src/test/java/com/koreatech/data/ExampleUnitTest.kt
new file mode 100644
index 0000000..4016eda
--- /dev/null
+++ b/core/data/src/test/java/com/koreatech/data/ExampleUnitTest.kt
@@ -0,0 +1,17 @@
+package com.koreatech.data
+
+import org.junit.Test
+
+import org.junit.Assert.*
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+class ExampleUnitTest {
+ @Test
+ fun addition_isCorrect() {
+ assertEquals(4, 2 + 2)
+ }
+}
\ No newline at end of file
diff --git a/core/network/.gitignore b/core/network/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/core/network/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/core/network/build.gradle.kts b/core/network/build.gradle.kts
new file mode 100644
index 0000000..587cd63
--- /dev/null
+++ b/core/network/build.gradle.kts
@@ -0,0 +1,40 @@
+@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed
+plugins {
+ alias(libs.plugins.pohahang.android.library)
+ alias(libs.plugins.pohahang.android.hilt)
+ id("org.jetbrains.kotlin.plugin.serialization")
+}
+
+android {
+ namespace = "com.koreatech.core.network"
+
+
+ defaultConfig {
+ buildConfigField("String", "base_url", getPropertyKey("base_url"))
+ }
+ buildTypes {
+ release {
+ isMinifyEnabled = false
+ proguardFiles(
+ getDefaultProguardFile("proguard-android-optimize.txt"),
+ "proguard-rules.pro"
+ )
+ }
+ }
+}
+
+dependencies {
+ implementation(libs.kotlinx.serialization.json)
+ implementation(libs.retrofit.core)
+ implementation(libs.retrofit.kotlin.serialization)
+ implementation(libs.okhttp.logging)
+ implementation(libs.gson)
+ implementation(project(mapOf("path" to ":core:data")))
+}
+
+fun getPropertyKey(propertyKey: String): String {
+ val nullableProperty: String? =
+ com.android.build.gradle.internal.cxx.configure.gradleLocalProperties(rootDir)
+ .getProperty(propertyKey)
+ return nullableProperty ?: "null"
+}
\ No newline at end of file
diff --git a/core/network/consumer-rules.pro b/core/network/consumer-rules.pro
new file mode 100644
index 0000000..e69de29
diff --git a/core/network/proguard-rules.pro b/core/network/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /dev/null
+++ b/core/network/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/core/network/src/androidTest/java/com/koreatech/core/network/ExampleInstrumentedTest.kt b/core/network/src/androidTest/java/com/koreatech/core/network/ExampleInstrumentedTest.kt
new file mode 100644
index 0000000..c87f316
--- /dev/null
+++ b/core/network/src/androidTest/java/com/koreatech/core/network/ExampleInstrumentedTest.kt
@@ -0,0 +1,24 @@
+package com.koreatech.core.network
+
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.ext.junit.runners.AndroidJUnit4
+
+import org.junit.Test
+import org.junit.runner.RunWith
+
+import org.junit.Assert.*
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+@RunWith(AndroidJUnit4::class)
+class ExampleInstrumentedTest {
+ @Test
+ fun useAppContext() {
+ // Context of the app under test.
+ val appContext = InstrumentationRegistry.getInstrumentation().targetContext
+ assertEquals("com.koreatech.core.network.test", appContext.packageName)
+ }
+}
\ No newline at end of file
diff --git a/core/network/src/main/AndroidManifest.xml b/core/network/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..a5918e6
--- /dev/null
+++ b/core/network/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/core/network/src/main/java/com/koreatech/core/network/NetworkModule.kt b/core/network/src/main/java/com/koreatech/core/network/NetworkModule.kt
new file mode 100644
index 0000000..04940f5
--- /dev/null
+++ b/core/network/src/main/java/com/koreatech/core/network/NetworkModule.kt
@@ -0,0 +1,32 @@
+package com.koreatech.core.network
+
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+import kotlinx.serialization.json.Json
+import okhttp3.Call
+import okhttp3.OkHttpClient
+import okhttp3.logging.HttpLoggingInterceptor
+import javax.inject.Singleton
+
+@Module
+@InstallIn(SingletonComponent::class)
+internal object NetworkModule {
+ @Provides
+ @Singleton
+ fun providesNetworkJson(): Json = Json {
+ ignoreUnknownKeys = true
+ }
+
+ @Provides
+ @Singleton
+ fun okHttpCallFactory(): Call.Factory = OkHttpClient.Builder()
+ .addInterceptor(
+ HttpLoggingInterceptor().apply {
+ if (BuildConfig.DEBUG) {
+ setLevel(HttpLoggingInterceptor.Level.BODY)
+ }
+ }
+ ).build()
+}
\ No newline at end of file
diff --git a/core/network/src/main/java/com/koreatech/core/network/NetworkResponse.kt b/core/network/src/main/java/com/koreatech/core/network/NetworkResponse.kt
new file mode 100644
index 0000000..97d139e
--- /dev/null
+++ b/core/network/src/main/java/com/koreatech/core/network/NetworkResponse.kt
@@ -0,0 +1,8 @@
+package com.koreatech.core.network
+
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class NetworkResponse(
+ val data: T
+)
diff --git a/core/network/src/main/java/com/koreatech/core/network/PohahangApi.kt b/core/network/src/main/java/com/koreatech/core/network/PohahangApi.kt
new file mode 100644
index 0000000..5bba680
--- /dev/null
+++ b/core/network/src/main/java/com/koreatech/core/network/PohahangApi.kt
@@ -0,0 +1,26 @@
+package com.koreatech.core.network
+
+import com.koreatech.data.model.request.LoginRequestDto
+import com.koreatech.data.model.request.SignupRequestDto
+import com.koreatech.data.model.response.CatResponseDto
+import retrofit2.http.Body
+import retrofit2.http.GET
+import retrofit2.http.POST
+import retrofit2.http.Query
+
+interface PohahangApi {
+ @POST("/api/v1/members")
+ suspend fun signup(
+ @Body signupRequestDto: SignupRequestDto
+ ): NetworkResponse
+
+ @POST("/api/v1/members/sign-in")
+ suspend fun login(
+ @Body loginRequestDto: LoginRequestDto
+ ): NetworkResponse
+
+ @GET("/v1/images/search")
+ suspend fun getCats(
+ @Query("limit") limit: Int
+ ): List
+}
\ No newline at end of file
diff --git a/core/network/src/main/java/com/koreatech/core/network/RetrofitNetwork.kt b/core/network/src/main/java/com/koreatech/core/network/RetrofitNetwork.kt
new file mode 100644
index 0000000..9337f50
--- /dev/null
+++ b/core/network/src/main/java/com/koreatech/core/network/RetrofitNetwork.kt
@@ -0,0 +1,40 @@
+package com.koreatech.core.network
+
+import com.koreatech.core.network.repository.PohahangRepository
+import com.koreatech.data.model.request.LoginRequestDto
+import com.koreatech.data.model.request.SignupRequestDto
+import com.koreatech.data.model.response.CatResponseDto
+import kotlinx.serialization.json.Json
+import okhttp3.Call
+import retrofit2.Retrofit
+import retrofit2.converter.gson.GsonConverterFactory
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+internal class RetrofitNetwork @Inject constructor(
+ networkJson: Json,
+ okHttpCallFactory: dagger.Lazy,
+) : PohahangRepository {
+ private val retrofitApi = Retrofit.Builder()
+ .baseUrl("https://api.thecatapi.com/")
+ .callFactory { okHttpCallFactory.get().newCall(it) }
+// .addConverterFactory(
+// networkJson.asConverterFactory("application/json".toMediaType())
+// )
+ .addConverterFactory(GsonConverterFactory.create())
+ .build()
+ .create(PohahangApi::class.java)
+
+ override suspend fun signup(signupRequestDto: SignupRequestDto) {
+ retrofitApi.signup(signupRequestDto)
+ }
+
+ override suspend fun login(loginRequestDto: LoginRequestDto) {
+ retrofitApi.login(loginRequestDto)
+ }
+
+ override suspend fun getCats(): List =
+ retrofitApi.getCats(100)
+
+}
\ No newline at end of file
diff --git a/core/network/src/main/java/com/koreatech/core/network/di/RepositoryModule.kt b/core/network/src/main/java/com/koreatech/core/network/di/RepositoryModule.kt
new file mode 100644
index 0000000..b6b53fc
--- /dev/null
+++ b/core/network/src/main/java/com/koreatech/core/network/di/RepositoryModule.kt
@@ -0,0 +1,27 @@
+package com.koreatech.core.network.di
+
+import android.util.Log
+import com.koreatech.core.network.RetrofitNetwork
+import com.koreatech.core.network.repository.PohahangRepository
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+import kotlinx.serialization.json.Json
+import okhttp3.Call
+import javax.inject.Singleton
+
+@Module
+@InstallIn(SingletonComponent::class)
+object RepositoryModule {
+ @Provides
+ @Singleton
+ fun providesAuthRepository(
+ networkJson: Json,
+ okHttpCallFactory: dagger.Lazy,
+ ): PohahangRepository {
+ return RetrofitNetwork(
+ networkJson, okHttpCallFactory
+ )
+ }
+}
diff --git a/core/network/src/main/java/com/koreatech/core/network/repository/PohahangRepository.kt b/core/network/src/main/java/com/koreatech/core/network/repository/PohahangRepository.kt
new file mode 100644
index 0000000..644dc68
--- /dev/null
+++ b/core/network/src/main/java/com/koreatech/core/network/repository/PohahangRepository.kt
@@ -0,0 +1,12 @@
+package com.koreatech.core.network.repository
+
+import com.koreatech.data.model.request.LoginRequestDto
+import com.koreatech.data.model.request.SignupRequestDto
+import com.koreatech.data.model.response.CatResponseDto
+import kotlinx.coroutines.flow.Flow
+
+interface PohahangRepository {
+ suspend fun signup(signupRequestDto: SignupRequestDto)
+ suspend fun login(loginRequestDto: LoginRequestDto)
+ suspend fun getCats(): List
+}
\ No newline at end of file
diff --git a/core/network/src/test/java/com/koreatech/core/network/ExampleUnitTest.kt b/core/network/src/test/java/com/koreatech/core/network/ExampleUnitTest.kt
new file mode 100644
index 0000000..7010f52
--- /dev/null
+++ b/core/network/src/test/java/com/koreatech/core/network/ExampleUnitTest.kt
@@ -0,0 +1,17 @@
+package com.koreatech.core.network
+
+import org.junit.Test
+
+import org.junit.Assert.*
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+class ExampleUnitTest {
+ @Test
+ fun addition_isCorrect() {
+ assertEquals(4, 2 + 2)
+ }
+}
\ No newline at end of file
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
new file mode 100644
index 0000000..ddbf1dc
--- /dev/null
+++ b/gradle/libs.versions.toml
@@ -0,0 +1,104 @@
+[versions]
+androidGradlePlugin = "8.1.4"
+androidxCore = "1.9.0"
+androidxLifecycle = "2.7.0"
+androidxActivity = "1.8.2"
+androidxNavigation = "2.5.3"
+androidxComposeBom = "2023.03.00"
+
+androidxTestExt = "1.1.5"
+androidxEspresso = "3.5.1"
+
+kotlinxSerializationJson = "1.6.3"
+kotlinxCoroutinesCoreVersion = "1.6.3"
+
+kotlinVersion = "1.8.10"
+composeCompilerVersion = "1.4.3"
+
+orbitVersion = "6.1.1"
+coilCompose = "2.6.0"
+
+hiltVersion = "2.44"
+hiltNavigationCompose = "1.0.0"
+okhttp = "4.12.0"
+retrofit = "2.9.0"
+retrofitKotlinxSerializationJson = "1.0.0"
+gson = "2.9.0"
+
+junit4 = "4.13.2"
+org-jetbrains-kotlin-jvm = "1.8.10"
+org-jetbrains-kotlin-jvm1810 = "1.8.10"
+org-jetbrains-kotlin-serialization = "1.8.0"
+espresso-core = "3.5.1"
+appcompat = "1.6.1"
+material = "1.11.0"
+
+javaxInjectVersion = "1"
+
+[libraries]
+androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "androidxCore" }
+androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "androidxLifecycle" }
+androidx-lifecycle-runtime-compose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "androidxLifecycle"}
+androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "androidxActivity" }
+androidx-lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "androidxLifecycle" }
+androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "androidxNavigation" }
+androidx-test-ext = { group = "androidx.test.ext", name = "junit", version.ref = "androidxTestExt" }
+androidx-test-espresso = { group = "androidx.test.espresso", name = "espresso-cor", version.ref = "androidxEspresso" }
+
+androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "androidxComposeBom" }
+androidx-compose-ui = { module = "androidx.compose.ui:ui" }
+androidx-compose-ui-graphics = { module = "androidx.compose.ui:ui-graphics" }
+androidx-compose-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview" }
+androidx-compose-material3 = { module = "androidx.compose.material3:material3" }
+androidx-compose-ui-test = { module = "androidx.compose.ui:ui-test-junit4" }
+androidx-compose-ui-test-manifest = { module = "androidx.compose.ui:ui-test-manifest" }
+
+kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" }
+kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinxCoroutinesCoreVersion" }
+
+junit4 = { group = "junit", name = "junit", version.ref = "junit4" }
+
+orbit-core = { group = "org.orbit-mvi", name = "orbit-core", version.ref = "orbitVersion" }
+orbit-viewmodel = { group = "org.orbit-mvi", name = "orbit-viewmodel", version.ref = "orbitVersion" }
+orbit-compose = { group = "org.orbit-mvi", name = "orbit-compose", version.ref = "orbitVersion" }
+orbit-test = { group = "org.orbit-mvi", name = "orbit-test", version.ref = "orbitVersion" }
+
+coil-compose = { group = "io.coil-kt", name = "coil-compose", version.ref = "coilCompose" }
+
+hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hiltVersion" }
+hilt-compiler = { group = "com.google.dagger", name = "hilt-compiler", version.ref = "hiltVersion" }
+hilt-navigation-compose = { group = "androidx.hilt", name = "hilt-navigation-compose", version.ref = "hiltNavigationCompose" }
+
+okhttp-logging = { group = "com.squareup.okhttp3", name = "logging-interceptor", version.ref = "okhttp" }
+retrofit-core = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" }
+retrofit-kotlin-serialization = { group = "com.jakewharton.retrofit", name = "retrofit2-kotlinx-serialization-converter", version.ref = "retrofitKotlinxSerializationJson" }
+gson = {group = "com.squareup.retrofit2", name = "converter-gson", version.ref = "gson"}
+
+javax-inject = { module = "javax.inject:javax.inject", version.ref = "javaxInjectVersion" }
+
+# Dependencies build-logic
+android-gradlePlugin = { group = "com.android.tools.build", name = "gradle", version.ref = "androidGradlePlugin" }
+kotlin-gradlePlugin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version.ref = "kotlinVersion" }
+espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espresso-core" }
+appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
+material = { group = "com.google.android.material", name = "material", version.ref = "material" }
+
+
+[plugins]
+android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" }
+kotlin-anroid = { id = "org.jetbrains.kotlin.android", version.ref = "kotlinVersion" }
+hilt = { id = "com.google.dagger.hilt.android", version.ref = "hiltVersion" }
+org-jetbrains-kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "org-jetbrains-kotlin-jvm1810" }
+com-android-library = { id = "com.android.library", version.ref = "androidGradlePlugin" }
+org-jetbrains-kotlin-serialization = { id ="org.jetbrains.kotlin.plugin.serialization", version.ref = "org-jetbrains-kotlin-serialization"}
+
+#Plugins defined
+pohahang-android-application = { id = "pohahang.android.application", version = "unspecified" }
+pohahang-android-application-compose = { id = "pohahang.android.application.compose", version = "unspecified" }
+pohahang-android-feature = { id = "pohahang.android.plugin.feature", version = "unspecified" }
+pohahang-android-hilt = { id = "pohahang.android.plugin.hilt", version = "unspecified" }
+pohahang-android-library = { id = "pohahang.android.library", version = "unspecified" }
+
+[bundles]
+orbit = ["orbit-core", "orbit-viewmodel", "orbit-compose"]
+compose = ["androidx-compose-bom", "androidx-compose-ui", "androidx-compose-ui-graphics", "androidx-compose-ui-tooling-preview", "androidx-compose-material3"]
\ No newline at end of file
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 57b89b7..da28159 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -1,4 +1,5 @@
pluginManagement {
+ includeBuild("build-logic")
repositories {
google()
mavenCentral()
@@ -15,3 +16,6 @@ dependencyResolutionManagement {
rootProject.name = "kwanhee-jo-compose-study"
include(":app")
+include(":core")
+include(":core:network")
+include(":core:data")