diff --git a/core/designsystem/src/main/kotlin/com/nexters/ilab/android/core/designsystem/theme/Color.kt b/core/designsystem/src/main/kotlin/com/nexters/ilab/android/core/designsystem/theme/Color.kt index 2d774b6d..6a54a15b 100644 --- a/core/designsystem/src/main/kotlin/com/nexters/ilab/android/core/designsystem/theme/Color.kt +++ b/core/designsystem/src/main/kotlin/com/nexters/ilab/android/core/designsystem/theme/Color.kt @@ -42,3 +42,4 @@ val Gray900 = Color(0xFF121212) val SystemGreen = Color(0xFF4FCF6B) val SystemRed = Color(0xFFFF584E) +val Kakao = Color(0xFFFBD300) diff --git a/core/designsystem/src/main/kotlin/com/nexters/ilab/android/core/designsystem/theme/Theme.kt b/core/designsystem/src/main/kotlin/com/nexters/ilab/android/core/designsystem/theme/Theme.kt index 45c85494..55ff2475 100644 --- a/core/designsystem/src/main/kotlin/com/nexters/ilab/android/core/designsystem/theme/Theme.kt +++ b/core/designsystem/src/main/kotlin/com/nexters/ilab/android/core/designsystem/theme/Theme.kt @@ -1,6 +1,5 @@ package com.nexters.ilab.android.core.designsystem.theme -import android.app.Activity import android.os.Build import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.material3.MaterialTheme @@ -9,12 +8,7 @@ import androidx.compose.material3.dynamicDarkColorScheme import androidx.compose.material3.dynamicLightColorScheme import androidx.compose.material3.lightColorScheme import androidx.compose.runtime.Composable -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 DarkColorScheme = darkColorScheme( primary = Purple80, @@ -53,27 +47,6 @@ fun ILabTheme( darkTheme -> DarkColorScheme else -> LightColorScheme } - val view = LocalView.current - - if (!view.isInEditMode) { - SideEffect { - val window = (view.context as Activity).window - - window.statusBarColor = if (darkTheme) Color.Black.toArgb() else Color.White.toArgb() - window.navigationBarColor = if (darkTheme) Color.Black.toArgb() else Color.White.toArgb() - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - // remove unnecessary black screen from bars - window.isNavigationBarContrastEnforced = false - } - - val windowsInsetsController = WindowCompat.getInsetsController(window, view) - - // status bar's icon always visible - windowsInsetsController.isAppearanceLightStatusBars = !darkTheme - windowsInsetsController.isAppearanceLightNavigationBars = !darkTheme - } - } MaterialTheme( colorScheme = colorScheme, diff --git a/core/designsystem/src/main/res/drawable/bg_login_screen.png b/core/designsystem/src/main/res/drawable/bg_login_screen.png new file mode 100644 index 00000000..1f63d648 Binary files /dev/null and b/core/designsystem/src/main/res/drawable/bg_login_screen.png differ diff --git a/core/designsystem/src/main/res/drawable/ic_ilab_logo_110.xml b/core/designsystem/src/main/res/drawable/ic_ilab_logo_110.xml new file mode 100644 index 00000000..3ab37fde --- /dev/null +++ b/core/designsystem/src/main/res/drawable/ic_ilab_logo_110.xml @@ -0,0 +1,25 @@ + + + + + + + + + + diff --git a/core/designsystem/src/main/res/drawable/ic_kakao.xml b/core/designsystem/src/main/res/drawable/ic_kakao.xml new file mode 100644 index 00000000..8edda505 --- /dev/null +++ b/core/designsystem/src/main/res/drawable/ic_kakao.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + diff --git a/core/designsystem/src/main/res/values/strings.xml b/core/designsystem/src/main/res/values/strings.xml index 34043295..0557195f 100644 --- a/core/designsystem/src/main/res/values/strings.xml +++ b/core/designsystem/src/main/res/values/strings.xml @@ -4,6 +4,7 @@ 카카오 로그인 + 반가워요! 아이랩에서 나만의\n프로필을 만들어보세요! diff --git a/core/ui/src/main/kotlin/com/nexters/ilab/core/ui/component/Image.kt b/core/ui/src/main/kotlin/com/nexters/ilab/core/ui/component/Image.kt index 5be39470..686f3487 100644 --- a/core/ui/src/main/kotlin/com/nexters/ilab/core/ui/component/Image.kt +++ b/core/ui/src/main/kotlin/com/nexters/ilab/core/ui/component/Image.kt @@ -1,6 +1,8 @@ package com.nexters.ilab.core.ui.component +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.width import androidx.compose.material.icons.Icons @@ -27,7 +29,7 @@ fun ExampleImage( if (LocalInspectionMode.current) { Icon( imageVector = Icons.Outlined.Person, - contentDescription = "Image Icon", + contentDescription = "Example Image Icon", modifier = Modifier .width(103.dp) .height(139.dp), @@ -56,7 +58,7 @@ fun NetworkImage( if (LocalInspectionMode.current) { Icon( imageVector = Icons.Outlined.Person, - contentDescription = "Image Icon", + contentDescription = "Network Image Icon", modifier = Modifier .width(186.dp) .aspectRatio(1f), @@ -74,12 +76,34 @@ fun NetworkImage( } } +@Composable +fun BackgroundImage( + resId: Int, + contentDescription: String, + modifier: Modifier = Modifier, +) { + val context = LocalContext.current + + if (LocalInspectionMode.current) { + Box(modifier = Modifier.fillMaxSize()) + } else { + AsyncImage( + model = ImageRequest.Builder(context) + .data(resId) + .build(), + contentDescription = contentDescription, + contentScale = ContentScale.FillBounds, + modifier = modifier, + ) + } +} + @ComponentPreview @Composable fun ExampleImagePreview() { ExampleImage( resId = 0, - contentDescription = "Image Icon", + contentDescription = "Example Image Icon", ) } @@ -88,6 +112,15 @@ fun ExampleImagePreview() { fun NetworkImagePreview() { NetworkImage( imageUrl = "", - contentDescription = "Image Icon", + contentDescription = "Network Image Icon", + ) +} + +@ComponentPreview +@Composable +fun BackgroundImagePreview() { + BackgroundImage( + resId = 0, + contentDescription = "Background Image Icon", ) } diff --git a/feature/intro/build.gradle.kts b/feature/intro/build.gradle.kts index b5b9b956..a40185f0 100644 --- a/feature/intro/build.gradle.kts +++ b/feature/intro/build.gradle.kts @@ -12,5 +12,6 @@ dependencies { implementations( libs.androidx.core, libs.timber, + libs.system.ui.controller, ) } diff --git a/feature/intro/src/main/kotlin/com/nexters/ilab/android/feature/intro/IntroActivity.kt b/feature/intro/src/main/kotlin/com/nexters/ilab/android/feature/intro/IntroActivity.kt index e0890bb9..3408e922 100644 --- a/feature/intro/src/main/kotlin/com/nexters/ilab/android/feature/intro/IntroActivity.kt +++ b/feature/intro/src/main/kotlin/com/nexters/ilab/android/feature/intro/IntroActivity.kt @@ -4,10 +4,13 @@ import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.viewModels +import androidx.compose.runtime.DisposableEffect +import androidx.compose.ui.graphics.Color import com.nexters.ilab.android.core.designsystem.theme.ILabTheme import com.nexters.ilab.android.feature.navigator.LoginNavigator import com.nexters.ilab.android.feature.navigator.MainNavigator import dagger.hilt.android.AndroidEntryPoint +import tech.thdev.compose.exteions.system.ui.controller.rememberExSystemUiController import javax.inject.Inject @AndroidEntryPoint @@ -23,6 +26,18 @@ class IntroActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { + val systemUiController = rememberExSystemUiController() + + DisposableEffect(systemUiController) { + systemUiController.setSystemBarsColor( + color = Color.White, + darkIcons = true, + isNavigationBarContrastEnforced = false, + ) + + onDispose {} + } + ILabTheme { IntroRoute( navigateToLogin = { diff --git a/feature/login/build.gradle.kts b/feature/login/build.gradle.kts index aca99daa..42b979f5 100644 --- a/feature/login/build.gradle.kts +++ b/feature/login/build.gradle.kts @@ -11,7 +11,9 @@ android { dependencies { implementations( libs.androidx.core, + libs.androidx.activity.compose, libs.timber, libs.kakao.auth, + libs.system.ui.controller, ) } diff --git a/feature/login/src/main/kotlin/com/nexters/ilab/android/feature/login/LoginActivity.kt b/feature/login/src/main/kotlin/com/nexters/ilab/android/feature/login/LoginActivity.kt index 1ffcb75f..299020ff 100644 --- a/feature/login/src/main/kotlin/com/nexters/ilab/android/feature/login/LoginActivity.kt +++ b/feature/login/src/main/kotlin/com/nexters/ilab/android/feature/login/LoginActivity.kt @@ -3,10 +3,14 @@ package com.nexters.ilab.android.feature.login import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge import androidx.activity.viewModels +import androidx.compose.runtime.DisposableEffect +import androidx.compose.ui.graphics.Color import com.nexters.ilab.android.core.designsystem.theme.ILabTheme import com.nexters.ilab.android.feature.navigator.MainNavigator import dagger.hilt.android.AndroidEntryPoint +import tech.thdev.compose.exteions.system.ui.controller.rememberExSystemUiController import javax.inject.Inject @AndroidEntryPoint @@ -17,8 +21,22 @@ class LoginActivity : ComponentActivity() { lateinit var mainNavigator: MainNavigator override fun onCreate(savedInstanceState: Bundle?) { + enableEdgeToEdge() super.onCreate(savedInstanceState) + setContent { + val systemUiController = rememberExSystemUiController() + + DisposableEffect(systemUiController) { + systemUiController.setSystemBarsColor( + color = Color.Transparent, + darkIcons = true, + isNavigationBarContrastEnforced = false, + ) + + onDispose {} + } + ILabTheme { LoginRoute( navigateToHome = { diff --git a/feature/login/src/main/kotlin/com/nexters/ilab/android/feature/login/LoginScreen.kt b/feature/login/src/main/kotlin/com/nexters/ilab/android/feature/login/LoginScreen.kt index 1eb728bb..81fca7d9 100644 --- a/feature/login/src/main/kotlin/com/nexters/ilab/android/feature/login/LoginScreen.kt +++ b/feature/login/src/main/kotlin/com/nexters/ilab/android/feature/login/LoginScreen.kt @@ -2,21 +2,29 @@ package com.nexters.ilab.android.feature.login import android.widget.Toast import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column 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.material3.Button +import androidx.compose.foundation.layout.navigationBarsPadding +import androidx.compose.foundation.layout.padding import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource +import androidx.compose.ui.res.vectorResource import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.kakao.sdk.auth.model.OAuthToken @@ -24,6 +32,13 @@ import com.kakao.sdk.common.model.AuthError import com.kakao.sdk.user.UserApiClient import com.nexters.ilab.android.core.common.UiText import com.nexters.ilab.android.core.designsystem.R +import com.nexters.ilab.android.core.designsystem.theme.Contents1 +import com.nexters.ilab.android.core.designsystem.theme.Gray900 +import com.nexters.ilab.android.core.designsystem.theme.Kakao +import com.nexters.ilab.android.core.designsystem.theme.Subtitle1 +import com.nexters.ilab.core.ui.DevicePreview +import com.nexters.ilab.core.ui.component.BackgroundImage +import com.nexters.ilab.core.ui.component.ILabButton import timber.log.Timber @Composable @@ -91,17 +106,75 @@ internal fun LoginScreen( CircularProgressIndicator() } + Box(modifier = Modifier.fillMaxSize()) { + BackgroundImage( + resId = R.drawable.bg_login_screen, + contentDescription = "Background Image for Login Screen", + modifier = Modifier.fillMaxSize(), + ) + LoginContent( + onLoginClick = onLoginClick, + ) + } +} + +@Composable +fun LoginContent( + onLoginClick: () -> Unit, +) { Column( modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally, + horizontalAlignment = Alignment.Start, ) { - Text(text = "LoginScreen") - Spacer(modifier = Modifier.height(32.dp)) - Button( - onClick = onLoginClick, + Spacer(modifier = Modifier.weight(1f)) + Column( + modifier = Modifier.padding(horizontal = 21.dp), ) { - Text(text = stringResource(id = R.string.kakao_login)) + Icon( + imageVector = ImageVector.vectorResource(id = R.drawable.ic_ilab_logo_110), + contentDescription = "iLab Logo", + tint = Color.Unspecified, + ) + Spacer(modifier = Modifier.height(20.dp)) + Text( + text = stringResource(id = R.string.intro_description), + style = Contents1, + ) } + Spacer(modifier = Modifier.weight(2f)) + ILabButton( + onClick = onLoginClick, + modifier = Modifier + .fillMaxWidth() + .navigationBarsPadding() + .padding(start = 20.dp, end = 20.dp, bottom = 34.dp) + .height(60.dp), + containerColor = Kakao, + contentColor = Gray900, + text = { + Text( + text = stringResource(id = R.string.kakao_login), + fontSize = 18.sp, + style = Subtitle1, + ) + }, + leadingIcon = { + Icon( + imageVector = ImageVector.vectorResource(id = R.drawable.ic_kakao), + contentDescription = "Kakao Icon", + tint = Color.Unspecified, + ) + }, + ) } } + +@DevicePreview +@Composable +fun LoginScreenPreview() { + LoginScreen( + uiState = LoginState(), + onLoginClick = {}, + ) +} diff --git a/feature/main/build.gradle.kts b/feature/main/build.gradle.kts index 36d8b218..fef5d751 100644 --- a/feature/main/build.gradle.kts +++ b/feature/main/build.gradle.kts @@ -16,6 +16,8 @@ dependencies { projects.feature.uploadphoto, libs.androidx.core, + libs.androidx.activity.compose, libs.kotlinx.collections.immutable, + libs.system.ui.controller, ) } diff --git a/feature/main/src/main/kotlin/com/nexters/ilab/android/feature/main/MainActivity.kt b/feature/main/src/main/kotlin/com/nexters/ilab/android/feature/main/MainActivity.kt index 42a2a6ec..756a3e38 100644 --- a/feature/main/src/main/kotlin/com/nexters/ilab/android/feature/main/MainActivity.kt +++ b/feature/main/src/main/kotlin/com/nexters/ilab/android/feature/main/MainActivity.kt @@ -3,11 +3,14 @@ package com.nexters.ilab.android.feature.main import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge import androidx.activity.viewModels -import androidx.core.view.WindowCompat +import androidx.compose.runtime.DisposableEffect +import androidx.compose.ui.graphics.Color import com.nexters.ilab.android.core.designsystem.theme.ILabTheme import com.nexters.ilab.android.feature.navigator.LoginNavigator import dagger.hilt.android.AndroidEntryPoint +import tech.thdev.compose.exteions.system.ui.controller.rememberExSystemUiController import javax.inject.Inject @AndroidEntryPoint @@ -18,13 +21,24 @@ class MainActivity : ComponentActivity() { lateinit var loginNavigator: LoginNavigator override fun onCreate(savedInstanceState: Bundle?) { + enableEdgeToEdge() super.onCreate(savedInstanceState) setContent { // TODO isDarkTheme 를 dataStore 에서 가져와서 구독하는 방식으로 수정 val isDarkTheme = false val navigator: MainNavController = rememberMainNavController() - WindowCompat.setDecorFitsSystemWindows(window, false) + val systemUiController = rememberExSystemUiController() + + DisposableEffect(systemUiController) { + systemUiController.setSystemBarsColor( + color = Color.White, + darkIcons = true, + isNavigationBarContrastEnforced = false, + ) + + onDispose {} + } ILabTheme(darkTheme = isDarkTheme) { MainScreen( diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 736a51a3..c1e37908 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -44,6 +44,7 @@ ksp = "1.9.22-1.0.16" orbit-core = "4.6.1" kotest = "5.8.0" kakao-core = "2.19.0" +compose-extensions = "1.6.0-rc01" [libraries] @@ -78,6 +79,7 @@ androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-toolin androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" } androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "androidx-navigation" } androidx-hilt-navigation-compose = { group = "androidx.hilt", name = "hilt-navigation-compose", version.ref = "androidx-hilt-navigation-compose" } +system-ui-controller = { group = "tech.thdev", name = "extensions-compose-system-ui-controller", version.ref = "compose-extensions" } retrofit = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" } retrofit-kotlinx-serialization-converter = { group = "com.jakewharton.retrofit", name = "retrofit2-kotlinx-serialization-converter", version.ref = "kotlinx-serialization-converter" } @@ -97,8 +99,8 @@ coil-compose = { group = "io.coil-kt", name = "coil-compose", version.ref = "coi compose-shimmer = { group = "com.valentinilk.shimmer", name = "compose-shimmer", version.ref = "compose-shimmer" } kakao-auth = { group = "com.kakao.sdk", name = "v2-user", version.ref = "kakao-core" } -kotest-runner = { module = "io.kotest:kotest-runner-junit5-jvm", version.ref = "kotest" } -kotest-assertion = { module = "io.kotest:kotest-assertions-core-jvm", version.ref = "kotest" } +kotest-runner = { group = "io.kotest", name = "kotest-runner-junit5-jvm", version.ref = "kotest" } +kotest-assertion = { group = "io.kotest", name = "kotest-assertions-core-jvm", version.ref = "kotest" } [plugins]