diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 7cc318e..eb1c9cf 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -3,11 +3,11 @@ import java.util.Properties plugins { // 커스텀 플러그인 적용 id("barrion.android.application") - id("barrion.android.application.compose") // 새로운 Compose 플러그인 사용 - id("org.jetbrains.kotlin.plugin.compose") // Compose 컴파일러 플러그인 추가 - id("barrion.hilt") // 의존성 주입 - id("barrion.network") // 네트워킹 - id("barrion.imageloading") // 이미지 로딩 + id("barrion.android.application.compose") + id("org.jetbrains.kotlin.plugin.compose") + id("barrion.hilt") + id("barrion.network") + id("barrion.imageloading") } val properties = @@ -24,13 +24,21 @@ android { targetSdk = 34 versionCode = 1 versionName = "1.0" + + // 리소스 최적화 설정 + resourceConfigurations.addAll(listOf("en", "ko")) } + + // 동적 기능 모듈 관련 설정 추가 + // 온보딩 모듈이 동적 기능 모듈로 구성되어 있다면 사용 + // dynamicFeatures += setOf(":feature:onboarding") + buildFeatures { compose = true } composeOptions { - kotlinCompilerExtensionVersion = "1.5.10" // libs.versions.toml에 정의된 값 또는 직접 명시 + kotlinCompilerExtensionVersion = "1.5.10" } buildTypes { @@ -43,16 +51,15 @@ android { } } - // Java 버전 오버라이드 (플러그인에서 17 사용, 앱에서 11로 변경) compileOptions { sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 } + kotlinOptions { jvmTarget = "11" } - // 빌드 기능 설정 (필요한 경우) buildFeatures { viewBinding = true dataBinding = true @@ -61,17 +68,21 @@ android { } dependencies { - // 앱 특화 의존성만 추가 (플러그인에서 처리하지 않는 의존성) + // 기존 의존성 유지 + implementation(project(":core:ui")) + implementation(project(":presentation")) + implementation(project(":feature:onboarding")) + + implementation("androidx.core:core-splashscreen:1.0.1") + implementation("androidx.navigation:navigation-compose:2.7.5") implementation(libs.balloon) implementation(libs.core.splashscreen) - // 테스트 의존성 (테스트 플러그인이 없는 경우) testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.espresso.core) } - //import java.util.Properties // //plugins { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 6fc3779..fcbf187 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -3,6 +3,7 @@ xmlns:tools="http://schemas.android.com/tools"> - - - - - - - - + + + + + + + - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/ic_launcher-playstore.png b/app/src/main/ic_launcher-playstore.png new file mode 100644 index 0000000..061c585 Binary files /dev/null and b/app/src/main/ic_launcher-playstore.png differ diff --git a/app/src/main/java/com/example/barrion/BarrionApplication.kt b/app/src/main/java/com/example/barrion/BarrionApplication.kt new file mode 100644 index 0000000..5367e77 --- /dev/null +++ b/app/src/main/java/com/example/barrion/BarrionApplication.kt @@ -0,0 +1,19 @@ +// app/src/main/java/com/example/barrion/BarrionApplication.kt + +package com.example.barrion + +import android.app.Application +import dagger.hilt.android.HiltAndroidApp + +/** + * 앱의 애플리케이션 클래스 + * Hilt 의존성 주입을 위한 진입점 역할을 합니다. + * + * @HiltAndroidApp 어노테이션은 Hilt의 코드 생성을 트리거하고 + * 애플리케이션 수준의 의존성 컨테이너를 생성합니다. + */ +@HiltAndroidApp +class BarrionApplication : Application() { + // 앱 시작 시 초기화가 필요한 코드를 여기에 추가할 수 있습니다. + // 예: 로깅 설정, 크래시 리포팅 도구 초기화, 글로벌 설정 등 +} \ No newline at end of file diff --git a/app/src/main/java/com/example/barrion/MainActivity.kt b/app/src/main/java/com/example/barrion/MainActivity.kt index fe0aff2..2f7d4fb 100644 --- a/app/src/main/java/com/example/barrion/MainActivity.kt +++ b/app/src/main/java/com/example/barrion/MainActivity.kt @@ -1,50 +1,86 @@ +// app/src/main/java/com/example/barrion/MainActivity.kt + package com.example.barrion import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge +import androidx.activity.viewModels import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable +import androidx.compose.material3.Surface import androidx.compose.ui.Modifier -import androidx.compose.ui.tooling.preview.Preview -import com.example.barrion.ui.theme.BarrionTheme +import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen +import androidx.lifecycle.lifecycleScope +import com.example.presentation.splash.SplashViewModel +import androidx.navigation.compose.rememberNavController +import com.example.barrion.navigation.BarrionNavHost +import com.example.ui.theme.BarrionTheme +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.launch +/** + * 앱의 메인 액티비티 + * 앱의 시작점이며 Compose 기반 UI를 설정합니다. + * + * @AndroidEntryPoint 어노테이션은 Hilt의 의존성 주입을 이 액티비티에 적용합니다. + */ +@AndroidEntryPoint class MainActivity : ComponentActivity() { + + // Hilt를 통해 SplashViewModel 의존성 주입 + private val viewModel: SplashViewModel by viewModels() + override fun onCreate(savedInstanceState: Bundle?) { + // 스플래시 스크린 설정 - Android 12+ 스플래시 화면 API 사용 + val splashScreen = installSplashScreen() + + // 뷰모델의 초기화 상태에 따라 스플래시 화면 표시 시간 조절 + // isInitialized.value가 true가 될 때까지 스플래시 화면 유지 + splashScreen.setKeepOnScreenCondition { + !viewModel.isInitialized.value + } + super.onCreate(savedInstanceState) + + // 엣지투엣지 디스플레이 활성화 - 전체 화면 사용 enableEdgeToEdge() + + // 앱 초기화 상태 관찰 및 처리 + lifecycleScope.launch { + // 뷰모델의 초기화 상태를 관찰하여 필요한 작업 수행 + viewModel.isInitialized.collectLatest { isInitialized -> + if (isInitialized) { + // 초기화가 완료된 후 수행할 작업 + // 예: 로그인 상태 확인, 데이터 프리로드 등 + } + } + } + + // Compose UI 설정 setContent { + // 앱의 테마 적용 BarrionTheme { - Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding -> - Greeting( - name = "A222", - modifier = Modifier.padding(innerPadding), - ) + // 스캐폴드를 사용하여 기본 레이아웃 구성 + Scaffold { innerPadding -> + Surface( + modifier = Modifier + .fillMaxSize() + .padding(innerPadding), + color = MaterialTheme.colorScheme.background + ) { + // 네비게이션 컨트롤러 생성 + val navController = rememberNavController() + + // 앱의 메인 네비게이션 설정 + BarrionNavHost(navController = navController) + } } } } } -} - -@Composable -fun Greeting( - name: String, - modifier: Modifier = Modifier, -) { - Text( - text = "Hello $name!", - modifier = modifier, - ) -} - -@Preview(showBackground = true) -@Composable -fun GreetingPreview() { - BarrionTheme { - Greeting("Android") - } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/example/barrion/navigation/BarrionNavHost.kt b/app/src/main/java/com/example/barrion/navigation/BarrionNavHost.kt new file mode 100644 index 0000000..1a56b91 --- /dev/null +++ b/app/src/main/java/com/example/barrion/navigation/BarrionNavHost.kt @@ -0,0 +1,89 @@ +// app/src/main/java/com/example/barrion/navigation/BarrionNavHost.kt + +package com.example.barrion.navigation + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.navigation.NavHostController +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable +import com.example.onboarding.presentation.OnboardingScreen + +/** + * 앱의 메인 네비게이션 호스트 + * 모든 화면 간의 이동을 관리합니다. + * + * @param navController 화면 전환을 위한 네비게이션 컨트롤러 + */ +@Composable +fun BarrionNavHost(navController: NavHostController) { + // 앱의 모든 화면 간 네비게이션을 설정 + NavHost( + navController = navController, + startDestination = NavRoutes.Onboarding.route // 시작 화면을 온보딩으로 설정 + ) { + // 온보딩 화면 라우트 + composable(route = NavRoutes.Onboarding.route) { + OnboardingScreen( + onNavigateToHome = { + // 홈 화면으로 이동하면서 온보딩 화면은 백스택에서 제거 + navController.navigate(NavRoutes.Home.route) { + popUpTo(NavRoutes.Onboarding.route) { inclusive = true } + } + } + ) + } + + // 홈 화면 라우트 (메뉴 관리 화면) + composable(route = NavRoutes.Home.route) { + // 현재 구현 예정인 화면 - 구현 후 주석 해제 + // MenuScreen( + // onNavigateToOrder = { navController.navigate(NavRoutes.Order.route) }, + // onNavigateToSales = { navController.navigate(NavRoutes.Sales.route) }, + // onNavigateToStaff = { navController.navigate(NavRoutes.Staff.route) } + // ) + + // 임시로 개발 중 메시지 표시 + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center + ) { + Text( + text = "앱 개발 중...", + style = MaterialTheme.typography.headlineMedium + ) + } + } + + // 주문 관리 화면 라우트 + composable(route = NavRoutes.Order.route) { + // 현재 구현 예정인 화면 - 구현 후 주석 해제 + // OrderScreen( + // onNavigateBack = { navController.popBackStack() } + // ) + } + + // 매출 화면 라우트 + composable(route = NavRoutes.Sales.route) { + // 현재 구현 예정인 화면 - 구현 후 주석 해제 + // SalesScreen( + // onNavigateBack = { navController.popBackStack() } + // ) + } + + // 직원 관리 화면 라우트 + composable(route = NavRoutes.Staff.route) { + // 현재 구현 예정인 화면 - 구현 후 주석 해제 + // StaffScreen( + // onNavigateBack = { navController.popBackStack() } + // ) + } + + // 추가 화면들.. + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/barrion/navigation/NavRoutes.kt b/app/src/main/java/com/example/barrion/navigation/NavRoutes.kt new file mode 100644 index 0000000..676d908 --- /dev/null +++ b/app/src/main/java/com/example/barrion/navigation/NavRoutes.kt @@ -0,0 +1,55 @@ +// app/src/main/java/com/example/barrion/navigation/NavRoutes.kt + +package com.example.barrion.navigation + +/** + * 앱의 네비게이션 경로를 정의하는 sealed class + * 모든 화면 경로를 한 곳에서 관리하여 일관성을 유지하고 오타를 방지합니다. + */ +sealed class NavRoutes(val route: String) { + /** + * 온보딩 화면 경로 - 앱 최초 실행 시 표시되는 화면 + * 사용자 가이드 및 초기 설정을 포함합니다. + */ + object Onboarding : NavRoutes("onboarding") + + /** + * 홈 화면 경로 - 앱의 메인 화면 + * 메뉴 관리 기능을 제공하는 화면입니다. + */ + object Home : NavRoutes("home") + + /** + * 주문 화면 경로 - 주문 관리 기능 + * 주문 목록 확인, 주문 처리 등의 기능을 제공합니다. + */ + object Order : NavRoutes("order") + + /** + * 매출 화면 경로 - 매출 관리 및 통계 + * 매출 현황, 통계, 리포트 등을 제공합니다. + */ + object Sales : NavRoutes("sales") + + /** + * 직원 관리 화면 경로 - 직원 정보 및 관리 + * 직원 목록, 근태 관리, 권한 설정 등을 제공합니다. + */ + object Staff : NavRoutes("staff") + + // 파라미터가 있는 경로 예시: + // /** + // * 주문 상세 화면 경로 - 특정 주문의 상세 정보 + // * {orderId} 파라미터를 통해 특정 주문 정보를 로드합니다. + // * 사용 예시: navController.navigate("order/12345") + // */ + // object OrderDetail : NavRoutes("order/{orderId}") + + // /** + // * URL에서 경로 파라미터를 추출하는 헬퍼 함수 + // * 사용 예시: val orderId = OrderDetail.getOrderId(savedStateHandle) + // */ + // fun getOrderId(savedStateHandle: SavedStateHandle): String { + // return checkNotNull(savedStateHandle["orderId"]) + // } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/barrion/ui/theme/Color.kt b/app/src/main/java/com/example/barrion/ui/theme/Color.kt deleted file mode 100644 index c450ded..0000000 --- a/app/src/main/java/com/example/barrion/ui/theme/Color.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.example.barrion.ui.theme - -import androidx.compose.ui.graphics.Color - -val Purple80 = Color(0xFFD0BCFF) -val PurpleGrey80 = Color(0xFFCCC2DC) -val Pink80 = Color(0xFFEFB8C8) - -val Purple40 = Color(0xFF6650a4) -val PurpleGrey40 = Color(0xFF625b71) -val Pink40 = Color(0xFF7D5260) diff --git a/app/src/main/java/com/example/barrion/ui/theme/Theme.kt b/app/src/main/java/com/example/barrion/ui/theme/Theme.kt deleted file mode 100644 index 48e023b..0000000 --- a/app/src/main/java/com/example/barrion/ui/theme/Theme.kt +++ /dev/null @@ -1,59 +0,0 @@ -package com.example.barrion.ui.theme - -import android.os.Build -import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.darkColorScheme -import androidx.compose.material3.dynamicDarkColorScheme -import androidx.compose.material3.dynamicLightColorScheme -import androidx.compose.material3.lightColorScheme -import androidx.compose.runtime.Composable -import androidx.compose.ui.platform.LocalContext - -private val DarkColorScheme = - darkColorScheme( - primary = Purple80, - secondary = PurpleGrey80, - tertiary = Pink80, - ) - -private val LightColorScheme = - lightColorScheme( - primary = Purple40, - secondary = PurpleGrey40, - tertiary = Pink40, - /* Other default colors to override - background = Color(0xFFFFFBFE), - surface = Color(0xFFFFFBFE), - onPrimary = Color.White, - onSecondary = Color.White, - onTertiary = Color.White, - onBackground = Color(0xFF1C1B1F), - onSurface = Color(0xFF1C1B1F), - */ - ) - -@Composable -fun BarrionTheme( - darkTheme: Boolean = isSystemInDarkTheme(), - // Dynamic color is available on Android 12+ - dynamicColor: Boolean = true, - content: @Composable () -> Unit, -) { - val colorScheme = - when { - dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { - val context = LocalContext.current - if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) - } - - darkTheme -> DarkColorScheme - else -> LightColorScheme - } - - MaterialTheme( - colorScheme = colorScheme, - typography = Typography, - content = content, - ) -} diff --git a/app/src/main/java/com/example/barrion/ui/theme/Type.kt b/app/src/main/java/com/example/barrion/ui/theme/Type.kt deleted file mode 100644 index 15523d4..0000000 --- a/app/src/main/java/com/example/barrion/ui/theme/Type.kt +++ /dev/null @@ -1,36 +0,0 @@ -package com.example.barrion.ui.theme - -import androidx.compose.material3.Typography -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.font.FontFamily -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.sp - -// Set of Material typography styles to start with -val Typography = - Typography( - bodyLarge = - TextStyle( - fontFamily = FontFamily.Default, - fontWeight = FontWeight.Normal, - fontSize = 16.sp, - lineHeight = 24.sp, - letterSpacing = 0.5.sp, - ), - /* Other default text styles to override - titleLarge = TextStyle( - fontFamily = FontFamily.Default, - fontWeight = FontWeight.Normal, - fontSize = 22.sp, - lineHeight = 28.sp, - letterSpacing = 0.sp - ), - labelSmall = TextStyle( - fontFamily = FontFamily.Default, - fontWeight = FontWeight.Medium, - fontSize = 11.sp, - lineHeight = 16.sp, - letterSpacing = 0.5.sp - ) - */ - ) diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml deleted file mode 100644 index 2b068d1..0000000 --- a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..ec320dd --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml index 6f3b755..7353dbd 100644 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -1,6 +1,5 @@ - - - + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml index 6f3b755..7353dbd 100644 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -1,6 +1,5 @@ - - - + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp index c209e78..33c45d8 100644 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher.webp and b/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp index b2dfe3d..d4623b7 100644 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp index 4f0f1d6..a80fae6 100644 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher.webp and b/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp index 62b611d..20aaaf8 100644 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp index 948a307..264eede 100644 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp and b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp index 1b9a695..4f49b7f 100644 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp index 28d4b77..bae0255 100644 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp index 9287f50..e4f5793 100644 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp index aa7d642..e11fda8 100644 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp index 9126ae3..3d4931b 100644 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index f8c6127..795cdcf 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -7,4 +7,6 @@ #FF018786 #FF000000 #FFFFFFFF + + #2142FF \ No newline at end of file diff --git a/app/src/main/res/values/ic_launcher_background.xml b/app/src/main/res/values/ic_launcher_background.xml new file mode 100644 index 0000000..aff99b4 --- /dev/null +++ b/app/src/main/res/values/ic_launcher_background.xml @@ -0,0 +1,4 @@ + + + #2142FF + \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 9184aef..503c841 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -1,5 +1,22 @@ - + + + - \ No newline at end of file diff --git a/core/common/src/main/java/com/example/common/MyClass.kt b/core/common/src/main/java/com/example/common/MyClass.kt deleted file mode 100644 index f4eec00..0000000 --- a/core/common/src/main/java/com/example/common/MyClass.kt +++ /dev/null @@ -1,4 +0,0 @@ -package com.example.common - -class MyClass { -} \ No newline at end of file diff --git a/core/ui/src/main/java/com/example/ui/components/buttons/BarrionActionButtons.kt b/core/ui/src/main/java/com/example/ui/components/buttons/BarrionActionButtons.kt new file mode 100644 index 0000000..b30d282 --- /dev/null +++ b/core/ui/src/main/java/com/example/ui/components/buttons/BarrionActionButtons.kt @@ -0,0 +1,261 @@ +// core/ui/src/main/java/com/example/ui/components/buttons/BarrionActionButtons.kt +package com.example.ui.components.buttons + +import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedButton +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import com.example.ui.theme.barrionColors + +/** + * 장 버튼 - 전체 너비를 차지하는 기본 액션 버튼 + * + * @param text 버튼에 표시할 텍스트 + * @param onClick 버튼 클릭 시 실행될 콜백 + * @param modifier 추가 Modifier (기본 크기: 너비 260dp, 높이 40dp) + * @param enabled 버튼 활성화 여부 + */ +@Composable +fun BarrionFullButton( + text: String, + onClick: () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true +) { + Button( + onClick = onClick, + modifier = modifier + .width(260.dp) + .height(40.dp), + enabled = enabled, + shape = RoundedCornerShape(12.dp), + colors = ButtonDefaults.buttonColors( + containerColor = MaterialTheme.barrionColors.primaryBlue, + contentColor = MaterialTheme.barrionColors.white, + disabledContainerColor = MaterialTheme.barrionColors.grayMedium, + disabledContentColor = MaterialTheme.barrionColors.white + ), + contentPadding = PaddingValues(horizontal = 16.dp) + ) { + Text( + text = text, + style = MaterialTheme.typography.labelLarge, + textAlign = TextAlign.Center + ) + } +} + +/** + * 단 버튼 - 좌/우 버튼 쌍으로 구성된 네비게이션 버튼 + * + * @param leftText 왼쪽 버튼에 표시할 텍스트 + * @param rightText 오른쪽 버튼에 표시할 텍스트 + * @param onLeftClick 왼쪽 버튼 클릭 시 실행될 콜백 + * @param onRightClick 오른쪽 버튼 클릭 시 실행될 콜백 + * @param rightEnabled 오른쪽 버튼 활성화 여부 (특정 조건 충족 여부) + * @param modifier 추가 Modifier + */ +@Composable +fun BarrionNavigationButtons( + leftText: String, + rightText: String, + onLeftClick: () -> Unit, + onRightClick: () -> Unit, + rightEnabled: Boolean = true, + modifier: Modifier = Modifier +) { + androidx.compose.foundation.layout.Row( + modifier = modifier.fillMaxWidth(), + horizontalArrangement = androidx.compose.foundation.layout.Arrangement.spacedBy(8.dp) + ) { + // 왼쪽 버튼 (아웃라인 스타일) + OutlinedButton( + onClick = onLeftClick, + modifier = Modifier + .weight(1f) + .height(49.dp), + shape = RoundedCornerShape(12.dp), + border = BorderStroke(1.5.dp, MaterialTheme.barrionColors.primaryBlue), + colors = ButtonDefaults.outlinedButtonColors( + containerColor = MaterialTheme.barrionColors.white, + contentColor = MaterialTheme.barrionColors.primaryBlue + ) + ) { + Text( + text = leftText, + style = MaterialTheme.typography.labelLarge, + textAlign = TextAlign.Center + ) + } + + // 오른쪽 버튼 (채워진 스타일) + Button( + onClick = onRightClick, + modifier = Modifier + .weight(1f) + .height(49.dp), + enabled = rightEnabled, + shape = RoundedCornerShape(12.dp), + colors = ButtonDefaults.buttonColors( + containerColor = MaterialTheme.barrionColors.primaryBlue, + contentColor = MaterialTheme.barrionColors.white, + disabledContainerColor = MaterialTheme.barrionColors.grayMedium, + disabledContentColor = MaterialTheme.barrionColors.white + ) + ) { + Text( + text = rightText, + style = MaterialTheme.typography.labelLarge, + textAlign = TextAlign.Center + ) + } + } +} + +/** + * 단일 아웃라인 버튼 - 네비게이션 버튼 쌍의 왼쪽 스타일과 동일한 단일 버튼 + * + * @param text 버튼에 표시할 텍스트 + * @param onClick 버튼 클릭 시 실행될 콜백 + * @param modifier 추가 Modifier + * @param enabled 버튼 활성화 여부 + */ +@Composable +fun BarrionOutlineActionButton( + text: String, + onClick: () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true +) { + OutlinedButton( + onClick = onClick, + modifier = modifier + .width(157.dp) + .height(49.dp), + enabled = enabled, + shape = RoundedCornerShape(12.dp), + border = BorderStroke(1.5.dp, MaterialTheme.barrionColors.primaryBlue), + colors = ButtonDefaults.outlinedButtonColors( + containerColor = MaterialTheme.barrionColors.white, + contentColor = MaterialTheme.barrionColors.primaryBlue, + disabledContainerColor = MaterialTheme.barrionColors.white, + disabledContentColor = MaterialTheme.barrionColors.primaryBlue.copy(alpha = 0.5f) + ) + ) { + Text( + text = text, + style = MaterialTheme.typography.labelLarge, + textAlign = TextAlign.Center + ) + } +} + +/** + * 단일 필드 버튼 - 네비게이션 버튼 쌍의 오른쪽 스타일과 동일한 단일 버튼 + * + * @param text 버튼에 표시할 텍스트 + * @param onClick 버튼 클릭 시 실행될 콜백 + * @param modifier 추가 Modifier + * @param enabled 버튼 활성화 여부 + */ +@Composable +fun BarrionFilledActionButton( + text: String, + onClick: () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true +) { + Button( + onClick = onClick, + modifier = modifier + .width(157.dp) + .height(49.dp), + enabled = enabled, + shape = RoundedCornerShape(12.dp), + colors = ButtonDefaults.buttonColors( + containerColor = MaterialTheme.barrionColors.primaryBlue, + contentColor = MaterialTheme.barrionColors.white, + disabledContainerColor = MaterialTheme.barrionColors.grayMedium, + disabledContentColor = MaterialTheme.barrionColors.white + ) + ) { + Text( + text = text, + style = MaterialTheme.typography.labelLarge, + textAlign = TextAlign.Center + ) + } +} + +/** + * 알림 다이얼로그 버튼 쌍 - 이미지 5와 같은 다이얼로그에서 사용되는 버튼 쌍 + * + * @param leftText 왼쪽 버튼에 표시할 텍스트 (예: "아니오") + * @param rightText 오른쪽 버튼에 표시할 텍스트 (예: "예") + * @param onLeftClick 왼쪽 버튼 클릭 시 실행될 콜백 + * @param onRightClick 오른쪽 버튼 클릭 시 실행될 콜백 + * @param modifier 추가 Modifier + */ +@Composable +fun BarrionDialogButtons( + leftText: String, + rightText: String, + onLeftClick: () -> Unit, + onRightClick: () -> Unit, + modifier: Modifier = Modifier +) { + androidx.compose.foundation.layout.Row( + modifier = modifier.fillMaxWidth(), + horizontalArrangement = androidx.compose.foundation.layout.Arrangement.spacedBy(8.dp) + ) { + // 왼쪽 버튼 (아웃라인 스타일) + OutlinedButton( + onClick = onLeftClick, + modifier = Modifier + .weight(1f) + .height(49.dp), + shape = RoundedCornerShape(12.dp), + border = BorderStroke(1.5.dp, MaterialTheme.barrionColors.primaryBlue), + colors = ButtonDefaults.outlinedButtonColors( + containerColor = MaterialTheme.barrionColors.white, + contentColor = MaterialTheme.barrionColors.primaryBlue + ) + ) { + Text( + text = leftText, + style = MaterialTheme.typography.labelLarge, + textAlign = TextAlign.Center + ) + } + + // 오른쪽 버튼 (채워진 스타일) + Button( + onClick = onRightClick, + modifier = Modifier + .weight(1f) + .height(49.dp), + shape = RoundedCornerShape(12.dp), + colors = ButtonDefaults.buttonColors( + containerColor = MaterialTheme.barrionColors.primaryBlue, + contentColor = MaterialTheme.barrionColors.white + ) + ) { + Text( + text = rightText, + style = MaterialTheme.typography.labelLarge, + textAlign = TextAlign.Center + ) + } + } +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/example/ui/components/buttons/BarrionButton.kt b/core/ui/src/main/java/com/example/ui/components/buttons/BarrionButton.kt new file mode 100644 index 0000000..b26c689 --- /dev/null +++ b/core/ui/src/main/java/com/example/ui/components/buttons/BarrionButton.kt @@ -0,0 +1,166 @@ +// core/ui/src/main/java/com/example/ui/components/buttons/BarrionButton.kt +package com.example.ui.components.buttons + +import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedButton +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.example.ui.theme.BarrionTheme +import com.example.ui.theme.CornerRadius +import com.example.ui.theme.barrionColors + +/** + * 아웃라인 스타일 버튼 (테두리가 있는 흰색 배경) + */ +@Composable +fun BarrionOutlinedButton( + text: String, + onClick: () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + fullWidth: Boolean = true +) { + val buttonModifier = if (fullWidth) { + modifier.fillMaxWidth().height(56.dp) + } else { + modifier.height(56.dp) + } + + OutlinedButton( + onClick = onClick, + modifier = buttonModifier, + enabled = enabled, + shape = RoundedCornerShape(CornerRadius.XLarge), + border = BorderStroke(1.dp, MaterialTheme.barrionColors.primaryBlue), + colors = ButtonDefaults.outlinedButtonColors( + containerColor = MaterialTheme.barrionColors.white, + contentColor = MaterialTheme.barrionColors.primaryBlue, + disabledContentColor = MaterialTheme.barrionColors.primaryBlue.copy(alpha = 0.5f), + disabledContainerColor = MaterialTheme.barrionColors.white + ), + contentPadding = PaddingValues(horizontal = 16.dp) + ) { + Text( + text = text, + style = MaterialTheme.typography.labelLarge, + textAlign = TextAlign.Center + ) + } +} + +/** + * 주요 액션용 파란색 배경 버튼 + */ +@Composable +fun BarrionPrimaryButton( + text: String, + onClick: () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + fullWidth: Boolean = true +) { + val buttonModifier = if (fullWidth) { + modifier.fillMaxWidth().height(56.dp) + } else { + modifier.height(56.dp) + } + + Button( + onClick = onClick, + modifier = buttonModifier, + enabled = enabled, + shape = RoundedCornerShape(CornerRadius.XLarge), + colors = ButtonDefaults.buttonColors( + containerColor = MaterialTheme.barrionColors.primaryBlue, + contentColor = MaterialTheme.barrionColors.white, + disabledContainerColor = MaterialTheme.barrionColors.primaryBlue.copy(alpha = 0.5f), + disabledContentColor = MaterialTheme.barrionColors.white + ), + contentPadding = PaddingValues(horizontal = 16.dp) + ) { + Text( + text = text, + style = MaterialTheme.typography.labelLarge, + textAlign = TextAlign.Center + ) + } +} + +/** + * 보조 액션용 회색 배경 버튼 + */ +@Composable +fun BarrionSecondaryButton( + text: String, + onClick: () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + fullWidth: Boolean = true +) { + val buttonModifier = if (fullWidth) { + modifier.fillMaxWidth().height(56.dp) + } else { + modifier.height(56.dp) + } + + Button( + onClick = onClick, + modifier = buttonModifier, + enabled = enabled, + shape = RoundedCornerShape(CornerRadius.XLarge), + colors = ButtonDefaults.buttonColors( + containerColor = MaterialTheme.barrionColors.grayMedium, + contentColor = MaterialTheme.barrionColors.white, + disabledContainerColor = MaterialTheme.barrionColors.grayMedium.copy(alpha = 0.5f), + disabledContentColor = MaterialTheme.barrionColors.white + ), + contentPadding = PaddingValues(horizontal = 16.dp) + ) { + Text( + text = text, + style = MaterialTheme.typography.labelLarge, + textAlign = TextAlign.Center + ) + } +} + +// BarrionButton.kt 파일에 추가 +@Preview(showBackground = true) +@Composable +fun BarrionButtonPreview() { + BarrionTheme { + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + BarrionOutlinedButton( + text = "아웃라인 버튼", + onClick = { } + ) + + BarrionPrimaryButton( + text = "기본 버튼", + onClick = { } + ) + + BarrionSecondaryButton( + text = "보조 버튼", + onClick = { } + ) + } + } +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/example/ui/components/textfields/BarrionTextField.kt b/core/ui/src/main/java/com/example/ui/components/textfields/BarrionTextField.kt new file mode 100644 index 0000000..ef5469c --- /dev/null +++ b/core/ui/src/main/java/com/example/ui/components/textfields/BarrionTextField.kt @@ -0,0 +1,232 @@ +// core/ui/src/main/java/com/example/ui/components/textfields/BarrionTextField.kt +package com.example.ui.components.textfields + +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.text.BasicTextField +import androidx.compose.foundation.text.KeyboardActions +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.text.input.VisualTransformation +import androidx.compose.ui.unit.dp +import com.example.ui.theme.barrionColors + +/** + * 텍스트 필드 상태를 정의하는 Enum + */ +enum class BarrionTextFieldState { + NORMAL, // 기본 상태 (회색 테두리) + FOCUSED, // 포커스된 상태 (파란색 테두리) + ERROR, // 오류 상태 (빨간색 테두리) + DISABLED // 비활성화 상태 (회색 배경) +} + +/** + * 커스텀 텍스트 필드 컴포넌트 + * + * @param value 현재 입력된 텍스트 값 + * @param onValueChange 텍스트 값 변경 시 호출되는 콜백 + * @param modifier Modifier + * @param title 텍스트 필드 위에 표시되는 제목 (null일 경우 표시하지 않음) + * @param placeholder 입력 전 표시되는 플레이스홀더 텍스트 + * @param fieldState 텍스트 필드의 현재 상태 + * @param enabled 텍스트 필드 활성화 여부 + * @param readOnly 읽기 전용 모드 여부 + * @param keyboardOptions 키보드 옵션 + * @param keyboardActions 키보드 액션 + * @param singleLine 한 줄 입력 모드 여부 + * @param maxLines 최대 줄 수 + * @param visualTransformation 입력 값의 시각적 변환 (비밀번호 마스킹 등) + */ +@Composable +fun BarrionTextField( + value: String, + onValueChange: (String) -> Unit, + modifier: Modifier = Modifier, + title: String? = null, + placeholder: String = "", + fieldState: BarrionTextFieldState = BarrionTextFieldState.NORMAL, + enabled: Boolean = true, + readOnly: Boolean = false, + keyboardOptions: KeyboardOptions = KeyboardOptions.Default, + keyboardActions: KeyboardActions = KeyboardActions.Default, + singleLine: Boolean = true, + maxLines: Int = if (singleLine) 1 else Int.MAX_VALUE, + visualTransformation: VisualTransformation = VisualTransformation.None +) { + val interactionSource = remember { MutableInteractionSource() } + + // 상태에 따른 색상 및 스타일 설정 + val borderColor = when (fieldState) { + BarrionTextFieldState.FOCUSED -> MaterialTheme.barrionColors.primaryBlue + BarrionTextFieldState.ERROR -> MaterialTheme.barrionColors.error + else -> MaterialTheme.barrionColors.grayMediumLight + } + + val backgroundColor = when (fieldState) { + BarrionTextFieldState.DISABLED -> MaterialTheme.barrionColors.grayVeryLight + else -> Color.White + } + + val textColor = when { + !enabled -> MaterialTheme.barrionColors.grayMedium + fieldState == BarrionTextFieldState.ERROR -> MaterialTheme.barrionColors.error + else -> MaterialTheme.barrionColors.grayBlack + } + + Column(modifier = modifier.fillMaxWidth()) { + // 제목이 있는 경우 표시 + if (title != null) { + Text( + text = title, + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.barrionColors.grayDark, + modifier = Modifier.padding(bottom = 4.dp) + ) + } + + // 텍스트 필드 + BasicTextField( + value = value, + onValueChange = onValueChange, + modifier = Modifier + .fillMaxWidth() + .clip(RoundedCornerShape(12.dp)) + .border( + width = 1.dp, + color = borderColor, + shape = RoundedCornerShape(12.dp) + ) + .background(backgroundColor), + enabled = enabled && !readOnly, + readOnly = readOnly, + textStyle = MaterialTheme.typography.bodyLarge.copy(color = textColor), + keyboardOptions = keyboardOptions, + keyboardActions = keyboardActions, + singleLine = singleLine, + maxLines = maxLines, + visualTransformation = visualTransformation, + interactionSource = interactionSource, + cursorBrush = SolidColor(MaterialTheme.barrionColors.primaryBlue), + decorationBox = { innerTextField -> + Box( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 12.dp) + ) { + // 플레이스홀더 텍스트 (입력값이 비어있을 때만 표시) + if (value.isEmpty() && !readOnly) { + Text( + text = placeholder, + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.barrionColors.grayMedium + ) + } + + // 실제 입력 필드 + innerTextField() + } + } + ) + } +} + +/** + * 유효성 검사 기능이 포함된 텍스트 필드 + * + * @param value 현재 입력된 텍스트 값 + * @param onValueChange 텍스트 값 변경 시 호출되는 콜백 + * @param validator 입력 값의 유효성을 검사하는 함수, true를 반환하면 유효한 값 + * @param title 텍스트 필드 위에 표시되는 제목 + * @param placeholder 입력 전 표시되는 플레이스홀더 텍스트 + * @param errorMessage 유효성 검사 실패 시 표시되는 오류 메시지 + * @param modifier Modifier + * @param enabled 텍스트 필드 활성화 여부 + * @param keyboardOptions 키보드 옵션 + * @param keyboardActions 키보드 액션 + * @param singleLine 한 줄 입력 모드 여부 + * @param visualTransformation 입력 값의 시각적 변환 (비밀번호 마스킹 등) + */ +@Composable +fun BarrionValidatedTextField( + value: String, + onValueChange: (String) -> Unit, + validator: (String) -> Boolean, + title: String, + placeholder: String, + errorMessage: String, + modifier: Modifier = Modifier, + enabled: Boolean = true, + keyboardOptions: KeyboardOptions = KeyboardOptions.Default, + keyboardActions: KeyboardActions = KeyboardActions.Default, + singleLine: Boolean = true, + visualTransformation: VisualTransformation = VisualTransformation.None +) { + val isValid = if (value.isEmpty()) true else validator(value) + val fieldState = when { + value.isEmpty() -> BarrionTextFieldState.NORMAL + isValid -> BarrionTextFieldState.FOCUSED + else -> BarrionTextFieldState.ERROR + } + + Column(modifier = modifier.fillMaxWidth()) { + BarrionTextField( + value = value, + onValueChange = onValueChange, + title = title, + placeholder = placeholder, + fieldState = fieldState, + enabled = enabled, + keyboardOptions = keyboardOptions, + keyboardActions = keyboardActions, + singleLine = singleLine, + visualTransformation = visualTransformation + ) + + // 유효성 검사 실패 시 오류 메시지 표시 + if (fieldState == BarrionTextFieldState.ERROR) { + Text( + text = errorMessage, + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.barrionColors.error, + modifier = Modifier.padding(start = 4.dp, top = 4.dp) + ) + } + } +} + +/** + * 읽기 전용 텍스트 필드 (수정 불가능한 정보 표시용) + * + * @param value 표시할 텍스트 값 + * @param title 텍스트 필드 위에 표시되는 제목 + * @param modifier Modifier + */ +@Composable +fun BarrionReadOnlyTextField( + value: String, + title: String, + modifier: Modifier = Modifier +) { + BarrionTextField( + value = value, + onValueChange = { }, // 읽기 전용이므로 변경 불가 + title = title, + fieldState = BarrionTextFieldState.DISABLED, + readOnly = true, + modifier = modifier + ) +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/example/ui/theme/Color.kt b/core/ui/src/main/java/com/example/ui/theme/Color.kt new file mode 100644 index 0000000..5d12d65 --- /dev/null +++ b/core/ui/src/main/java/com/example/ui/theme/Color.kt @@ -0,0 +1,129 @@ +package com.example.ui.theme + +import androidx.compose.runtime.Immutable +import androidx.compose.runtime.staticCompositionLocalOf +import androidx.compose.ui.graphics.Color + +// 메인 블루 색상 +val BluePrimary = Color(0xFF1335C9) +val BlueSecondary = Color(0xFF2142FF) +val BlueTertiary = Color(0xFF4361FF) + +// 블루 변형 +val BlueDark = Color(0xFF0A1A5E) +val BlueMediumDark = Color(0xFF0E2894) +val BlueMedium = Color(0xFF1335C9) +val BlueBright = Color(0xFF2142FF) +val BlueLight = Color(0xFF4361FF) +val BlueLighter = Color(0xFF637BFF) +val BluePale = Color(0xFF8395FF) +val BlueVeryPale = Color(0xFFA3AFFF) +val BlueWhite = Color(0xFFE0E3FF) + +// 그레이 색상 +val GrayBlack = Color(0xFF1A1A1A) +val GrayVeryDark = Color(0xFF2E2E33) +val GrayDark = Color(0xFF474752) +val GrayMediumDark = Color(0xFF5F5F70) +val GrayMedium = Color(0xFF9696A3) +val GrayMediumLight = Color(0xFFB2B2BC) +val GrayLight = Color(0xFFCDCDD5) +val GrayVeryLight = Color(0xFFE8E8EC) +val GrayWhite = Color(0xFFF4F4F6) + +// 데이터 클래스로 색상 정의 (CompositionLocal 사용을 위한) +internal val BarrionColors = BarrionColor( + // 메인 블루 색상 + primaryBlue = BluePrimary, + secondaryBlue = BlueSecondary, + tertiaryBlue = BlueTertiary, + + // 블루 변형 + blueDark = BlueDark, + blueMediumDark = BlueMediumDark, + blueMedium = BlueMedium, + blueBright = BlueBright, + blueLight = BlueLight, + blueLighter = BlueLighter, + bluePale = BluePale, + blueVeryPale = BlueVeryPale, + blueWhite = BlueWhite, + + // 그레이 색상 + grayBlack = GrayBlack, + grayVeryDark = GrayVeryDark, + grayDark = GrayDark, + grayMediumDark = GrayMediumDark, + grayMedium = GrayMedium, + grayMediumLight = GrayMediumLight, + grayLight = GrayLight, + grayVeryLight = GrayVeryLight, + grayWhite = GrayWhite, + + // 기능 색상 (필요시 추가) + error = Color(0xFFFF3434), + success = Color(0xFF4CAF50), + warning = Color(0xFFFFC107), + info = Color(0xFF2196F3), + + // 기타 유틸리티 색상 + white = Color(0xFFFFFFFF), + black = Color(0xFF000000), + transparent = Color(0x00000000), + + // 투명도 변형 + blackOverlay30 = Color(0x4D000000), + blackOverlay60 = Color(0x99000000), + whiteOverlay30 = Color(0x4DFFFFFF) +) + +@Immutable +data class BarrionColor( + // 메인 블루 색상 + val primaryBlue: Color, + val secondaryBlue: Color, + val tertiaryBlue: Color, + + // 블루 변형 + val blueDark: Color, + val blueMediumDark: Color, + val blueMedium: Color, + val blueBright: Color, + val blueLight: Color, + val blueLighter: Color, + val bluePale: Color, + val blueVeryPale: Color, + val blueWhite: Color, + + // 그레이 색상 + val grayBlack: Color, + val grayVeryDark: Color, + val grayDark: Color, + val grayMediumDark: Color, + val grayMedium: Color, + val grayMediumLight: Color, + val grayLight: Color, + val grayVeryLight: Color, + val grayWhite: Color, + + // 기능 색상 + val error: Color, + val success: Color, + val warning: Color, + val info: Color, + + // 기타 유틸리티 색상 + val white: Color, + val black: Color, + val transparent: Color, + + // 투명도 변형 + val blackOverlay30: Color, + val blackOverlay60: Color, + val whiteOverlay30: Color +) + +// CompositionLocal을 통해 앱 전체에서 색상에 접근할 수 있도록 설정 +internal val LocalBarrionColor = staticCompositionLocalOf { + BarrionColors +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/example/ui/theme/Dimensions.kt b/core/ui/src/main/java/com/example/ui/theme/Dimensions.kt new file mode 100644 index 0000000..f50d7f5 --- /dev/null +++ b/core/ui/src/main/java/com/example/ui/theme/Dimensions.kt @@ -0,0 +1,39 @@ +package com.example.ui.theme + +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp + +// 간격 정의 +object Spacing { + val XXSmall = 2.dp + val XSmall = 4.dp + val Small = 8.dp + val Medium = 16.dp + val Large = 24.dp + val XLarge = 32.dp + val XXLarge = 48.dp +} + +// 텍스트 크기 정의 +object TextSize { + val Small = 12.sp + val Medium = 14.sp + val Large = 16.sp + val XLarge = 20.sp + val XXLarge = 24.sp +} + +// 모서리 둥글기 정의 +object CornerRadius { + val Small = 4.dp + val Medium = 8.dp + val Large = 16.dp + val XLarge = 24.dp +} + +// 그림자 높이 정의 +object Elevation { + val Small = 2.dp + val Medium = 4.dp + val Large = 8.dp +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/example/ui/theme/Theme.kt b/core/ui/src/main/java/com/example/ui/theme/Theme.kt new file mode 100644 index 0000000..da791f4 --- /dev/null +++ b/core/ui/src/main/java/com/example/ui/theme/Theme.kt @@ -0,0 +1,114 @@ +package com.example.ui.theme + +import android.app.Activity +import android.os.Build +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.dynamicDarkColorScheme +import androidx.compose.material3.dynamicLightColorScheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.SideEffect +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.toArgb +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalView +import androidx.core.view.WindowCompat + +// 라이트 테마를 위한 색상 스키마 정의 +private val LightColorScheme = lightColorScheme( + primary = BluePrimary, + secondary = BlueSecondary, + tertiary = BlueTertiary, + + // 배경 및 표면 색상 + background = GrayVeryLight, + surface = GrayWhite, + surfaceVariant = GrayLight, + + // 텍스트 및 아이콘 색상 + onPrimary = GrayWhite, // 기본 색상 위 텍스트/아이콘 + onSecondary = GrayWhite, + onTertiary = GrayWhite, + onBackground = GrayBlack, // 배경 위 텍스트/아이콘 + onSurface = GrayBlack, + onSurfaceVariant = GrayDark, + + // 기타 색상 + error = Color(0xFFFF3434), + onError = GrayWhite +) + +// 다크 테마를 위한 색상 스키마 정의 +private val DarkColorScheme = darkColorScheme( + primary = BlueBright, // 어두운 배경에서는 더 밝은 블루 + secondary = BlueLight, + tertiary = BlueLighter, + + // 배경 및 표면 색상 + background = GrayBlack, + surface = GrayVeryDark, + surfaceVariant = GrayDark, + + // 텍스트 및 아이콘 색상 + onPrimary = GrayWhite, + onSecondary = GrayWhite, + onTertiary = GrayWhite, + onBackground = GrayWhite, + onSurface = GrayWhite, + onSurfaceVariant = GrayMediumLight, + + // 기타 색상 + error = Color(0xFFFF5252), + onError = GrayWhite +) + +@Composable +fun BarrionTheme( + darkTheme: Boolean = isSystemInDarkTheme(), + // Dynamic color is available on Android 12+ + dynamicColor: Boolean = false, // 커스텀 색상을 우선하기 위해 기본값 false로 변경 + content: @Composable () -> Unit, +) { + val colorScheme = when { + dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { + val context = LocalContext.current + if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) + } + darkTheme -> DarkColorScheme + else -> LightColorScheme + } + + // 상태 표시줄 색상 설정 + val view = LocalView.current + if (!view.isInEditMode) { + SideEffect { + val window = (view.context as Activity).window + + // 상태 표시줄 배경색을 하얀색으로 설정 + window.statusBarColor = Color.White.toArgb() + + // 상태 표시줄 아이콘 색상 설정 (라이트 테마에서는 어두운 아이콘) + // 다크 테마에서는 밝은 아이콘을, 라이트 테마에서는 어두운 아이콘을 사용 + WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = true + } + } + + // 커스텀 색상 시스템을 앱 전체에 제공 + CompositionLocalProvider( + LocalBarrionColor provides BarrionColors + ) { + MaterialTheme( + colorScheme = colorScheme, + typography = Typography, + content = content, + ) + } +} + +// 커스텀 색상에 쉽게 접근하기 위한 확장 속성 +val MaterialTheme.barrionColors: BarrionColor + @Composable + get() = LocalBarrionColor.current \ No newline at end of file diff --git a/core/ui/src/main/java/com/example/ui/theme/Type.kt b/core/ui/src/main/java/com/example/ui/theme/Type.kt new file mode 100644 index 0000000..9967af2 --- /dev/null +++ b/core/ui/src/main/java/com/example/ui/theme/Type.kt @@ -0,0 +1,124 @@ +package com.example.ui.theme + +import androidx.compose.material3.Typography +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.Font +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.sp +import com.example.ui.R + +// Pretendard 폰트 패밀리 정의 +val PretendardFamily = FontFamily( + Font(R.font.pretendard_thin, FontWeight.Thin), + Font(R.font.pretendard_extralight, FontWeight.ExtraLight), + Font(R.font.pretendard_light, FontWeight.Light), + Font(R.font.pretendard_regular, FontWeight.Normal), + Font(R.font.pretendard_medium, FontWeight.Medium), + Font(R.font.pretendard_semibold, FontWeight.SemiBold), + Font(R.font.pretendard_bold, FontWeight.Bold), + Font(R.font.pretendard_extrabold, FontWeight.ExtraBold), + Font(R.font.pretendard_black, FontWeight.Black) +) + +// dimens.xml의 텍스트 크기를 Compose에서 사용하기 위한 변수 +private val TextSizeSmall = 12.sp +private val TextSizeMedium = 14.sp +private val TextSizeLarge = 16.sp +private val TextSizeXLarge = 20.sp +private val TextSizeXXLarge = 24.sp + +// Typography 정의 +val Typography = Typography( + // 표제 스타일 + headlineLarge = TextStyle( + fontFamily = PretendardFamily, + fontWeight = FontWeight.Bold, + fontSize = TextSizeXXLarge, + lineHeight = 32.sp, + letterSpacing = (-0.5).sp + ), + headlineMedium = TextStyle( + fontFamily = PretendardFamily, + fontWeight = FontWeight.Bold, + fontSize = TextSizeXLarge, + lineHeight = 28.sp, + letterSpacing = (-0.5).sp + ), + headlineSmall = TextStyle( + fontFamily = PretendardFamily, + fontWeight = FontWeight.Bold, + fontSize = TextSizeLarge, + lineHeight = 24.sp, + letterSpacing = 0.sp + ), + + // 제목 스타일 + titleLarge = TextStyle( + fontFamily = PretendardFamily, + fontWeight = FontWeight.SemiBold, + fontSize = TextSizeXLarge, + lineHeight = 28.sp, + letterSpacing = (-0.25).sp + ), + titleMedium = TextStyle( + fontFamily = PretendardFamily, + fontWeight = FontWeight.SemiBold, + fontSize = TextSizeLarge, + lineHeight = 24.sp, + letterSpacing = 0.sp + ), + titleSmall = TextStyle( + fontFamily = PretendardFamily, + fontWeight = FontWeight.Medium, + fontSize = TextSizeMedium, + lineHeight = 20.sp, + letterSpacing = 0.sp + ), + + // 본문 스타일 + bodyLarge = TextStyle( + fontFamily = PretendardFamily, + fontWeight = FontWeight.Normal, + fontSize = TextSizeLarge, + lineHeight = 24.sp, + letterSpacing = 0.sp + ), + bodyMedium = TextStyle( + fontFamily = PretendardFamily, + fontWeight = FontWeight.Normal, + fontSize = TextSizeMedium, + lineHeight = 20.sp, + letterSpacing = 0.sp + ), + bodySmall = TextStyle( + fontFamily = PretendardFamily, + fontWeight = FontWeight.Normal, + fontSize = TextSizeSmall, + lineHeight = 16.sp, + letterSpacing = 0.1.sp + ), + + // 라벨 스타일 (버튼 등에 사용) + labelLarge = TextStyle( + fontFamily = PretendardFamily, + fontWeight = FontWeight.Medium, + fontSize = TextSizeMedium, + lineHeight = 20.sp, + letterSpacing = 0.sp + ), + labelMedium = TextStyle( + fontFamily = PretendardFamily, + fontWeight = FontWeight.Medium, + fontSize = TextSizeSmall, + lineHeight = 16.sp, + letterSpacing = 0.sp + ), + labelSmall = TextStyle( + fontFamily = PretendardFamily, + fontWeight = FontWeight.Medium, + fontSize = 10.sp, + lineHeight = 14.sp, + letterSpacing = 0.sp + ) +) \ No newline at end of file diff --git a/core/ui/src/main/res/drawable/combined_shape.xml b/core/ui/src/main/res/drawable/combined_shape.xml new file mode 100644 index 0000000..ff30a6d --- /dev/null +++ b/core/ui/src/main/res/drawable/combined_shape.xml @@ -0,0 +1,10 @@ + + + diff --git a/core/ui/src/main/res/font/pretendard_black.ttf b/core/ui/src/main/res/font/pretendard_black.ttf new file mode 100644 index 0000000..d0c1db8 Binary files /dev/null and b/core/ui/src/main/res/font/pretendard_black.ttf differ diff --git a/core/ui/src/main/res/font/pretendard_bold.ttf b/core/ui/src/main/res/font/pretendard_bold.ttf new file mode 100644 index 0000000..fb07fc6 Binary files /dev/null and b/core/ui/src/main/res/font/pretendard_bold.ttf differ diff --git a/core/ui/src/main/res/font/pretendard_extrabold.ttf b/core/ui/src/main/res/font/pretendard_extrabold.ttf new file mode 100644 index 0000000..9d5fe07 Binary files /dev/null and b/core/ui/src/main/res/font/pretendard_extrabold.ttf differ diff --git a/core/ui/src/main/res/font/pretendard_extralight.ttf b/core/ui/src/main/res/font/pretendard_extralight.ttf new file mode 100644 index 0000000..09e6542 Binary files /dev/null and b/core/ui/src/main/res/font/pretendard_extralight.ttf differ diff --git a/core/ui/src/main/res/font/pretendard_light.ttf b/core/ui/src/main/res/font/pretendard_light.ttf new file mode 100644 index 0000000..2e8541d Binary files /dev/null and b/core/ui/src/main/res/font/pretendard_light.ttf differ diff --git a/core/ui/src/main/res/font/pretendard_medium.ttf b/core/ui/src/main/res/font/pretendard_medium.ttf new file mode 100644 index 0000000..1db67c6 Binary files /dev/null and b/core/ui/src/main/res/font/pretendard_medium.ttf differ diff --git a/core/ui/src/main/res/font/pretendard_regular.ttf b/core/ui/src/main/res/font/pretendard_regular.ttf new file mode 100644 index 0000000..01147e9 Binary files /dev/null and b/core/ui/src/main/res/font/pretendard_regular.ttf differ diff --git a/core/ui/src/main/res/font/pretendard_semibold.ttf b/core/ui/src/main/res/font/pretendard_semibold.ttf new file mode 100644 index 0000000..9f2690f Binary files /dev/null and b/core/ui/src/main/res/font/pretendard_semibold.ttf differ diff --git a/core/ui/src/main/res/font/pretendard_thin.ttf b/core/ui/src/main/res/font/pretendard_thin.ttf new file mode 100644 index 0000000..fe9825f Binary files /dev/null and b/core/ui/src/main/res/font/pretendard_thin.ttf differ diff --git a/core/ui/src/main/res/values/colors.xml b/core/ui/src/main/res/values/colors.xml new file mode 100644 index 0000000..87e7c1a --- /dev/null +++ b/core/ui/src/main/res/values/colors.xml @@ -0,0 +1,31 @@ + + + + + #1335C9 + #2142FF + #4361FF + + + #0A1A5E + #0E2894 + #1335C9 + #2142FF + #4361FF + #637BFF + #8395FF + #A3AFFF + #E0E3FF + + + #1A1A1A + #2E2E33 + #474752 + #5F5F70 + #9696A3 + #B2B2BC + #CDCDD5 + #E8E8EC + #F4F4F6 + + \ No newline at end of file diff --git a/core/ui/src/main/res/values/dimens.xml b/core/ui/src/main/res/values/dimens.xml new file mode 100644 index 0000000..07ef8f6 --- /dev/null +++ b/core/ui/src/main/res/values/dimens.xml @@ -0,0 +1,18 @@ + + + + 2dp + 4dp + 8dp + 16dp + 24dp + 32dp + 48dp + + + 12sp + 14sp + 16sp + 20sp + 24sp + \ No newline at end of file diff --git a/core/ui/src/main/res/values/themes.xml b/core/ui/src/main/res/values/themes.xml new file mode 100644 index 0000000..a6b3dae --- /dev/null +++ b/core/ui/src/main/res/values/themes.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/feature/onboarding/build.gradle.kts b/feature/onboarding/build.gradle.kts index f17605f..535c45d 100644 --- a/feature/onboarding/build.gradle.kts +++ b/feature/onboarding/build.gradle.kts @@ -1,14 +1,75 @@ plugins { - id("barrion.android.feature") - // 필요에 따라 추가 플러그인 적용 + id("com.android.library") + id("org.jetbrains.kotlin.android") + + // Compose 컴파일러 플러그인 + id("org.jetbrains.kotlin.plugin.compose") + + // 필요한 Hilt와 이미지 로딩 플러그인 id("barrion.hilt") id("barrion.imageloading") } android { - namespace = "com.example.feature.auth" // 각 모듈에 맞는 네임스페이스 사용 + namespace = "com.example.onboarding" + + // 컴파일 SDK 버전 추가 (앱 모듈과 같은 버전 사용) + compileSdk = 34 // 이 부분이 누락되었습니다 + + defaultConfig { + minSdk = 21 // 앱 모듈과 일치 + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + + buildFeatures { + compose = true + } + + composeOptions { + kotlinCompilerExtensionVersion = "1.5.10" + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + + kotlinOptions { + jvmTarget = "11" + } } dependencies { - // 모듈 특화 의존성만 추가 + // 코어 UI 모듈 의존성 + implementation(project(":core:ui")) + + // Compose 기본 의존성 + implementation("androidx.compose.ui:ui") + implementation("androidx.compose.material3:material3") + implementation("androidx.compose.ui:ui-tooling-preview") + debugImplementation("androidx.compose.ui:ui-tooling") + + // Material 아이콘 확장 의존성 + implementation("androidx.compose.material:material-icons-extended:1.5.4") + + // 다른 필요한 의존성 + implementation("androidx.core:core-ktx:1.12.0") + implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.7.0") + + // 테스트 의존성 + testImplementation("junit:junit:4.13.2") + androidTestImplementation("androidx.test.ext:junit:1.1.5") + androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") } \ No newline at end of file diff --git a/feature/onboarding/src/main/java/com/example/onboarding/presentation/OnboardingScreen.kt b/feature/onboarding/src/main/java/com/example/onboarding/presentation/OnboardingScreen.kt new file mode 100644 index 0000000..44a9662 --- /dev/null +++ b/feature/onboarding/src/main/java/com/example/onboarding/presentation/OnboardingScreen.kt @@ -0,0 +1,231 @@ +// features/onboarding/src/main/java/com/example/onboarding/presentation/OnboardingScreen.kt + +package com.example.onboarding.presentation + +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.pager.HorizontalPager +import androidx.compose.foundation.pager.rememberPagerState +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import com.example.onboarding.R +import com.example.ui.components.buttons.BarrionFullButton +import com.example.ui.components.buttons.BarrionNavigationButtons +import com.example.ui.components.buttons.BarrionPrimaryButton +import com.example.ui.theme.barrionColors +import kotlinx.coroutines.launch + +/** + * 온보딩 페이지 데이터 클래스 + * 각 온보딩 페이지의 제목, 설명, 이미지 리소스 ID를 포함합니다. + */ +data class OnboardingPage( + val title: String, // 페이지 제목 + val description: String, // 페이지 설명 + val imageResId: Int // 이미지 리소스 ID +) + +/** + * 온보딩 화면 컴포저블 + * 4개의 온보딩 페이지로 구성되며, 수평 페이저를 통해 사용자가 페이지를 넘길 수 있습니다. + * 마지막 페이지에 도달하면 시작하기 버튼이 활성화됩니다. + * + * @param onNavigateToHome 시작하기 버튼 클릭 시 호출될 콜백 함수 (홈 화면으로 이동) + */ +@OptIn(ExperimentalFoundationApi::class) +@Composable +fun OnboardingScreen( + onNavigateToHome: () -> Unit +) { + // 온보딩 페이지 정의 - 4개의 화면으로 구성 + val pages = listOf( + OnboardingPage( + title = "매출 관리", + description = "오늘 우리 가게는 얼마나 벌었을까요?", + imageResId = R.drawable.onboarding_sale // 매출 관리 이미지 + ), + OnboardingPage( + title = "메뉴 관리", + description = "메뉴 관리, 이제 터치 몇 번으로 끝", + imageResId = R.drawable.onboarding_menu // 메뉴 관리 이미지 + ), + OnboardingPage( + title = "주문 관리", + description = "주문 현황을 한눈에 확인하세요.", + imageResId = R.drawable.onboarding_order // 주문 관리 이미지 + ), + OnboardingPage( + title = "직원 관리", + description = "직원 정보, 쉽고 빠르게 관리해요.", + imageResId = R.drawable.onboarding_staff // 직원 관리 이미지 + ) + ) + + // 페이저 상태 관리 + val pagerState = rememberPagerState(pageCount = { pages.size }) + val coroutineScope = rememberCoroutineScope() + + // 현재 마지막 페이지인지 확인 (시작하기 버튼 활성화 여부에 사용) + val isLastPage = pagerState.currentPage == pages.size - 1 + + // 전체 화면 컨테이너 + Box( + modifier = Modifier + .fillMaxSize() + .background(MaterialTheme.colorScheme.background) + ) { + Column( + modifier = Modifier + .fillMaxSize() + .padding(16.dp) + ) { + // 수평 페이저 - 온보딩 페이지들을 수평으로 스와이프하여 볼 수 있음 + HorizontalPager( + state = pagerState, + modifier = Modifier + .fillMaxWidth() + .weight(1f) + ) { page -> + // 현재 페이지 인덱스에 해당하는 온보딩 페이지 컨텐츠를 표시 + OnboardingPageContent(pages[page]) + } + + // 하단 여백 추가 + Spacer(modifier = Modifier.height(16.dp)) + + // 페이지 인디케이터 - 현재 몇 번째 페이지인지 표시하는 도트 + Row( + modifier = Modifier + .align(Alignment.CenterHorizontally) + .padding(bottom = 32.dp), + horizontalArrangement = Arrangement.Center + ) { + // 페이지 수만큼 도트 생성 + repeat(pages.size) { index -> + val isSelected = pagerState.currentPage == index + Box( + modifier = Modifier + .padding(horizontal = 4.dp) + .size(8.dp) + .clip(CircleShape) + .background( + // 현재 페이지는 파란색, 나머지는 회색으로 표시 + if (isSelected) MaterialTheme.barrionColors.primaryBlue + else MaterialTheme.barrionColors.grayLight + ) + ) + } + } + + // 시작하기 버튼 - 마지막 페이지에서만 활성화됨 + BarrionPrimaryButton( + text = "시작하기", + onClick = { + if (isLastPage) { + // 마지막 페이지면 홈 화면으로 이동 + onNavigateToHome() + } else { + // 아니면 다음 페이지로 이동 + coroutineScope.launch { + pagerState.animateScrollToPage(pagerState.currentPage + 1) + } + } + }, + modifier = Modifier + .padding(horizontal = 24.dp) + .padding(bottom = 40.dp), + enabled = isLastPage // 마지막 페이지에서만 활성화 + ) + + // 하단 추가 여백 + Spacer(modifier = Modifier.height(24.dp)) + } + } +} + +/** + * 각 온보딩 페이지의 콘텐츠를 표시하는 컴포저블 + * + * @param page 표시할 온보딩 페이지 데이터 + */ +@Composable +fun OnboardingPageContent(page: OnboardingPage) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + horizontalAlignment = Alignment.Start // 왼쪽 정렬 + ) { + // 상단 여백 - 제목과 설명을 아래로 내려 배치 + Spacer(modifier = Modifier.height(36.dp)) + + // 페이지 제목 + Text( + text = page.title, + style = MaterialTheme.typography.headlineLarge, + fontWeight = FontWeight.ExtraBold, + modifier = Modifier.padding(bottom = 8.dp) + ) + + // 페이지 설명 + Text( + text = page.description, + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.barrionColors.grayMediumDark, + modifier = Modifier.padding(bottom = 32.dp) + ) + + // 이미지 카드 - 해당 기능을 시각적으로, 보여주는 이미지 + if (page.imageResId != 0) { + Card( + modifier = Modifier + .fillMaxWidth() + .height(380.dp), // 이미지 높이 설정 + shape = RoundedCornerShape(16.dp), + elevation = CardDefaults.cardElevation(defaultElevation = 4.dp) // 그림자 효과 + ) { + Box( + modifier = Modifier + .fillMaxSize() + .clip(RoundedCornerShape(16.dp)) + ) { + Image( + painter = painterResource(id = page.imageResId), + contentDescription = page.title, + contentScale = ContentScale.Crop, // 이미지가 컨테이너를 꽉 채우도록 설정 + alignment = Alignment.TopCenter, // 이미지 상단이 보이도록 정렬 + modifier = Modifier.fillMaxSize() + ) + } + } + } else { + // 이미지가 없는 경우 대체 UI 표시 + Box( + modifier = Modifier + .fillMaxWidth() + .height(380.dp) + .clip(RoundedCornerShape(16.dp)) + .background(MaterialTheme.barrionColors.grayVeryLight), + contentAlignment = Alignment.Center + ) { + Text( + text = "${page.title} 이미지", + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.barrionColors.grayMedium + ) + } + } + } +} \ No newline at end of file diff --git a/feature/onboarding/src/main/res/drawable/onboarding_menu.png b/feature/onboarding/src/main/res/drawable/onboarding_menu.png new file mode 100644 index 0000000..8938f81 Binary files /dev/null and b/feature/onboarding/src/main/res/drawable/onboarding_menu.png differ diff --git a/feature/onboarding/src/main/res/drawable/onboarding_order.png b/feature/onboarding/src/main/res/drawable/onboarding_order.png new file mode 100644 index 0000000..bad2ca3 Binary files /dev/null and b/feature/onboarding/src/main/res/drawable/onboarding_order.png differ diff --git a/feature/onboarding/src/main/res/drawable/onboarding_sale.png b/feature/onboarding/src/main/res/drawable/onboarding_sale.png new file mode 100644 index 0000000..640e1e2 Binary files /dev/null and b/feature/onboarding/src/main/res/drawable/onboarding_sale.png differ diff --git a/feature/onboarding/src/main/res/drawable/onboarding_staff.png b/feature/onboarding/src/main/res/drawable/onboarding_staff.png new file mode 100644 index 0000000..c49f280 Binary files /dev/null and b/feature/onboarding/src/main/res/drawable/onboarding_staff.png differ diff --git a/presentation/build.gradle.kts b/presentation/build.gradle.kts index 0734033..f17605f 100644 --- a/presentation/build.gradle.kts +++ b/presentation/build.gradle.kts @@ -1,43 +1,14 @@ - plugins { - alias(libs.plugins.android.library) - alias(libs.plugins.kotlin.android) - } - - android { - namespace = "com.example.presentation" - compileSdk = 34 - - defaultConfig { - minSdk = 24 - - 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_11 - targetCompatibility = JavaVersion.VERSION_11 - } - kotlinOptions { - jvmTarget = "11" - } - } - - dependencies { - - implementation(libs.androidx.core.ktx) - implementation(libs.androidx.appcompat) - implementation(libs.material) - testImplementation(libs.junit) - androidTestImplementation(libs.androidx.junit) - androidTestImplementation(libs.androidx.espresso.core) - } \ No newline at end of file +plugins { + id("barrion.android.feature") + // 필요에 따라 추가 플러그인 적용 + id("barrion.hilt") + id("barrion.imageloading") +} + +android { + namespace = "com.example.feature.auth" // 각 모듈에 맞는 네임스페이스 사용 +} + +dependencies { + // 모듈 특화 의존성만 추가 +} \ No newline at end of file diff --git a/presentation/src/main/java/com/example/presentation/splash/SplashViewModel.kt b/presentation/src/main/java/com/example/presentation/splash/SplashViewModel.kt new file mode 100644 index 0000000..1d95d4d --- /dev/null +++ b/presentation/src/main/java/com/example/presentation/splash/SplashViewModel.kt @@ -0,0 +1,38 @@ +package com.example.presentation.splash + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch +import javax.inject.Inject + +/** + * 스플래시 화면 초기화를 관리하는 ViewModel + */ +class SplashViewModel @Inject constructor( + // 필요한 레포지토리나 유스케이스 의존성 주입 +) : ViewModel() { + + private val _isInitialized = MutableStateFlow(false) + val isInitialized: StateFlow = _isInitialized.asStateFlow() + + init { + initializeApp() + } + + private fun initializeApp() { + viewModelScope.launch { + // 1. 필요한 초기화 작업 수행 + // 예: 사용자 인증 상태 확인, 데이터 초기화 등 + + // 2. 최소 스플래시 화면 표시 시간 보장 + delay(5000) // 1.5초 지연 + + // 3. 초기화 완료 + _isInitialized.value = true + } + } +} \ No newline at end of file