diff --git a/Near/app/build.gradle.kts b/Near/app/build.gradle.kts index 1e85f397..0284dce0 100644 --- a/Near/app/build.gradle.kts +++ b/Near/app/build.gradle.kts @@ -99,6 +99,9 @@ dependencies { // Kakao Module implementation(libs.v2.all) + + // Splash Screen API + implementation(libs.androidx.core.splashscreen) } fun getProperty(propertyKey: String): String = gradleLocalProperties(rootDir, providers).getProperty(propertyKey) diff --git a/Near/app/src/main/AndroidManifest.xml b/Near/app/src/main/AndroidManifest.xml index b0ae0a68..09d49b30 100644 --- a/Near/app/src/main/AndroidManifest.xml +++ b/Near/app/src/main/AndroidManifest.xml @@ -9,7 +9,7 @@ android:allowBackup="true" android:dataExtractionRules="@xml/data_extraction_rules" android:fullBackupContent="@xml/backup_rules" - android:icon="@mipmap/ic_launcher" + android:icon="@mipmap/ic_launcher_round" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" @@ -20,25 +20,28 @@ - + + - + + - + + android:theme="@style/Theme.Near.Splash"> diff --git a/Near/app/src/main/java/com/alarmy/near/presentation/feature/login/LoginScreen.kt b/Near/app/src/main/java/com/alarmy/near/presentation/feature/login/LoginScreen.kt index 10990fba..c231a9c3 100644 --- a/Near/app/src/main/java/com/alarmy/near/presentation/feature/login/LoginScreen.kt +++ b/Near/app/src/main/java/com/alarmy/near/presentation/feature/login/LoginScreen.kt @@ -89,7 +89,7 @@ private fun LoginIntroductionSection(modifier: Modifier = Modifier) { Image( modifier = modifier.wrapContentSize(Alignment.Center), alignment = Alignment.Center, - painter = painterResource(R.drawable.ic_near_logo_title), + painter = painterResource(R.drawable.ic_near_logo_title_primary), contentDescription = stringResource(R.string.near_logo_title), ) diff --git a/Near/app/src/main/java/com/alarmy/near/presentation/feature/main/MainActivity.kt b/Near/app/src/main/java/com/alarmy/near/presentation/feature/main/MainActivity.kt index 35f00fdc..92fd46ef 100644 --- a/Near/app/src/main/java/com/alarmy/near/presentation/feature/main/MainActivity.kt +++ b/Near/app/src/main/java/com/alarmy/near/presentation/feature/main/MainActivity.kt @@ -4,17 +4,47 @@ 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.getValue +import androidx.core.splashscreen.SplashScreen +import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.lifecycle.lifecycleScope import com.alarmy.near.presentation.ui.theme.NearTheme import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.launch @AndroidEntryPoint class MainActivity : ComponentActivity() { + private val mainViewModel: MainViewModel by viewModels() + override fun onCreate(savedInstanceState: Bundle?) { + val splashScreen = installSplashScreen() + super.onCreate(savedInstanceState) enableEdgeToEdge() + setupSplashScreen(splashScreen) + setContent { NearTheme { - NearApp() + val uiState by mainViewModel.uiState.collectAsStateWithLifecycle() + if (!uiState.isLoading) { + NearApp( + isLoggedIn = uiState.isLoggedIn + ) + } + } + } + } + + /** + * 스플래시 스크린을 설정하고 MainViewModel의 상태를 관찰합니다. + * API 스플래시가 표시되는 동안 백그라운드에서 검증을 수행합니다. + */ + private fun setupSplashScreen(splashScreen: SplashScreen) { + lifecycleScope.launch { + mainViewModel.uiState.collect { uiState -> + splashScreen.setKeepOnScreenCondition { uiState.isLoading } } } } diff --git a/Near/app/src/main/java/com/alarmy/near/presentation/feature/main/MainViewModel.kt b/Near/app/src/main/java/com/alarmy/near/presentation/feature/main/MainViewModel.kt new file mode 100644 index 00000000..3c97b6fb --- /dev/null +++ b/Near/app/src/main/java/com/alarmy/near/presentation/feature/main/MainViewModel.kt @@ -0,0 +1,53 @@ +package com.alarmy.near.presentation.feature.main + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.alarmy.near.data.repository.AuthRepository +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class MainViewModel @Inject constructor( + private val authRepository: AuthRepository, +) : ViewModel() { + + // UI 상태 관리 + private val _uiState = MutableStateFlow(MainUiState()) + val uiState: StateFlow = _uiState.asStateFlow() + + init { + checkLoginStatus() + } + + /** + * 로그인 상태를 확인하고 스플래시 스크린을 제어합니다. + * API 스플래시가 표시되는 동안 백그라운드에서 검증을 수행합니다. + */ + private fun checkLoginStatus() { + viewModelScope.launch { + + // 로그인 상태 검증 + val isLoggedIn = runCatching { + authRepository.isLoggedIn() + }.getOrElse { false } + + // UI 상태 업데이트 + _uiState.value = _uiState.value.copy( + isLoading = false, + isLoggedIn = isLoggedIn + ) + } + } +} + +/** + * MainActivity의 UI 상태를 관리하는 데이터 클래스 + */ +data class MainUiState( + val isLoading: Boolean = true, + val isLoggedIn: Boolean = false, +) diff --git a/Near/app/src/main/java/com/alarmy/near/presentation/feature/main/NearApp.kt b/Near/app/src/main/java/com/alarmy/near/presentation/feature/main/NearApp.kt index 106f00b5..3e739250 100644 --- a/Near/app/src/main/java/com/alarmy/near/presentation/feature/main/NearApp.kt +++ b/Near/app/src/main/java/com/alarmy/near/presentation/feature/main/NearApp.kt @@ -6,7 +6,6 @@ import androidx.compose.foundation.layout.consumeWindowInsets import androidx.compose.foundation.layout.exclude import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.ime -import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.safeDrawing import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.material3.Scaffold @@ -26,6 +25,7 @@ import kotlinx.coroutines.launch internal fun NearApp( modifier: Modifier = Modifier, navController: NavHostController = rememberNavController(), + isLoggedIn: Boolean = false, ) { val snackBarState = remember { SnackbarHostState() } val scope = rememberCoroutineScope() @@ -48,6 +48,7 @@ internal fun NearApp( NearNavHost( modifier = Modifier.consumeWindowInsets(innerPadding), // 하위 뷰에 Padding을 소비한 것으로 알립니다. navController = navController, + isLoggedIn = isLoggedIn, onShowSnackbar = { scope.launch { snackBarState.showSnackbar( diff --git a/Near/app/src/main/java/com/alarmy/near/presentation/feature/main/NearNavHost.kt b/Near/app/src/main/java/com/alarmy/near/presentation/feature/main/NearNavHost.kt index adf33e39..8c0cdb19 100644 --- a/Near/app/src/main/java/com/alarmy/near/presentation/feature/main/NearNavHost.kt +++ b/Near/app/src/main/java/com/alarmy/near/presentation/feature/main/NearNavHost.kt @@ -7,6 +7,7 @@ import androidx.compose.ui.platform.LocalContext import androidx.core.net.toUri import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost +import androidx.navigation.navOptions import com.alarmy.near.presentation.feature.friendprofile.navigation.friendProfileNavGraph import com.alarmy.near.presentation.feature.friendprofile.navigation.navigateToFriendProfile import com.alarmy.near.presentation.feature.friendprofileedittor.navigation.FRIEND_PROFILE_EDIT_COMPLETE_KEY @@ -14,26 +15,29 @@ import com.alarmy.near.presentation.feature.friendprofileedittor.navigation.frie import com.alarmy.near.presentation.feature.friendprofileedittor.navigation.navigateToFriendProfileEditor import com.alarmy.near.presentation.feature.home.navigation.RouteHome import com.alarmy.near.presentation.feature.home.navigation.homeNavGraph -import java.net.URLEncoder -import java.nio.charset.StandardCharsets import com.alarmy.near.presentation.feature.home.navigation.navigateToHome import com.alarmy.near.presentation.feature.login.navigation.RouteLogin import com.alarmy.near.presentation.feature.login.navigation.loginNavGraph +import java.net.URLEncoder +import java.nio.charset.StandardCharsets @Composable internal fun NearNavHost( modifier: Modifier = Modifier, navController: NavHostController, + isLoggedIn: Boolean = false, onShowSnackbar: (Throwable?) -> Unit = { _ -> }, ) { val context = LocalContext.current + /* * 화면 이동 및 구성을 위한 컴포저블 함수입니다. + * 로그인 상태에 따라 즉시 적절한 화면으로 시작합니다. * */ NavHost( modifier = modifier, navController = navController, - startDestination = RouteHome, + startDestination = if (isLoggedIn) RouteHome else RouteLogin, ) { friendProfileNavGraph(onShowErrorSnackBar = onShowSnackbar, onClickBackButton = { navController.popBackStack() @@ -72,12 +76,14 @@ internal fun NearNavHost( ) navController.popBackStack() }) + + // 로그인 화면 NavGraph loginNavGraph( onShowErrorSnackBar = onShowSnackbar, onNavigateToHome = { navController.navigateToHome( - navOptions = androidx.navigation.navOptions { + navOptions = navOptions { popUpTo(RouteLogin) { inclusive = true } } ) @@ -94,5 +100,21 @@ internal fun NearNavHost( onAlarmClick = {}, onAddContactClick = {}, ) + + // 친구 프로필 화면 NavGraph + friendProfileNavGraph( + onShowErrorSnackBar = onShowSnackbar, + onClickBackButton = { + navController.popBackStack() + } + ) + + // 친구 프로필 편집 화면 NavGraph + friendProfileEditorNavGraph( + onShowErrorSnackBar = onShowSnackbar, + onClickBackButton = { + navController.popBackStack() + } + ) } } diff --git a/Near/app/src/main/res/drawable-hdpi/img_bg.png b/Near/app/src/main/res/drawable-hdpi/img_bg.png new file mode 100644 index 00000000..7ad84255 Binary files /dev/null and b/Near/app/src/main/res/drawable-hdpi/img_bg.png differ diff --git a/Near/app/src/main/res/drawable/img_bg.png b/Near/app/src/main/res/drawable-mdpi/img_bg.png similarity index 100% rename from Near/app/src/main/res/drawable/img_bg.png rename to Near/app/src/main/res/drawable-mdpi/img_bg.png diff --git a/Near/app/src/main/res/drawable-xhdpi/img_bg.png b/Near/app/src/main/res/drawable-xhdpi/img_bg.png new file mode 100644 index 00000000..eca43ad9 Binary files /dev/null and b/Near/app/src/main/res/drawable-xhdpi/img_bg.png differ diff --git a/Near/app/src/main/res/drawable-xxhdpi/img_bg.png b/Near/app/src/main/res/drawable-xxhdpi/img_bg.png new file mode 100644 index 00000000..bb6e965a Binary files /dev/null and b/Near/app/src/main/res/drawable-xxhdpi/img_bg.png differ diff --git a/Near/app/src/main/res/drawable-xxxhdpi/img_bg.png b/Near/app/src/main/res/drawable-xxxhdpi/img_bg.png new file mode 100644 index 00000000..bc5e2345 Binary files /dev/null and b/Near/app/src/main/res/drawable-xxxhdpi/img_bg.png differ diff --git a/Near/app/src/main/res/drawable/ic_launcher_foreground.xml b/Near/app/src/main/res/drawable/ic_launcher_foreground.xml index 2b068d11..db210bef 100644 --- a/Near/app/src/main/res/drawable/ic_launcher_foreground.xml +++ b/Near/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -1,30 +1,33 @@ - - - - - - - - - - \ No newline at end of file + android:width="100dp" + android:height="101dp" + android:viewportWidth="100" + android:viewportHeight="101"> + + + + + + + + + + + + + diff --git a/Near/app/src/main/res/drawable/ic_near_logo_title.xml b/Near/app/src/main/res/drawable/ic_near_logo_title_primary.xml similarity index 100% rename from Near/app/src/main/res/drawable/ic_near_logo_title.xml rename to Near/app/src/main/res/drawable/ic_near_logo_title_primary.xml diff --git a/Near/app/src/main/res/drawable/ic_near_logo_title_white.xml b/Near/app/src/main/res/drawable/ic_near_logo_title_white.xml new file mode 100644 index 00000000..68cda581 --- /dev/null +++ b/Near/app/src/main/res/drawable/ic_near_logo_title_white.xml @@ -0,0 +1,18 @@ + + + + + + diff --git a/Near/app/src/main/res/drawable/img_splash_logo.xml b/Near/app/src/main/res/drawable/img_splash_logo.xml new file mode 100644 index 00000000..3ea2c402 --- /dev/null +++ b/Near/app/src/main/res/drawable/img_splash_logo.xml @@ -0,0 +1,21 @@ + + + + + + + diff --git a/Near/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/Near/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml deleted file mode 100644 index 6f3b755b..00000000 --- a/Near/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/Near/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/Near/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml index 6f3b755b..e61cccae 100644 --- a/Near/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ b/Near/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -1,6 +1,4 @@ - - - - \ No newline at end of file + + diff --git a/Near/app/src/main/res/mipmap-hdpi/ic_launcher.png b/Near/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 00000000..f1fae059 Binary files /dev/null and b/Near/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/Near/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/Near/app/src/main/res/mipmap-hdpi/ic_launcher.webp deleted file mode 100644 index c209e78e..00000000 Binary files a/Near/app/src/main/res/mipmap-hdpi/ic_launcher.webp and /dev/null differ diff --git a/Near/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/Near/app/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 00000000..cff35eaa Binary files /dev/null and b/Near/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/Near/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/Near/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp deleted file mode 100644 index b2dfe3d1..00000000 Binary files a/Near/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp and /dev/null differ diff --git a/Near/app/src/main/res/mipmap-mdpi/ic_launcher.png b/Near/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 00000000..1d716579 Binary files /dev/null and b/Near/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/Near/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/Near/app/src/main/res/mipmap-mdpi/ic_launcher.webp deleted file mode 100644 index 4f0f1d64..00000000 Binary files a/Near/app/src/main/res/mipmap-mdpi/ic_launcher.webp and /dev/null differ diff --git a/Near/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/Near/app/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 00000000..55dad60a Binary files /dev/null and b/Near/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/Near/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/Near/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp deleted file mode 100644 index 62b611da..00000000 Binary files a/Near/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp and /dev/null differ diff --git a/Near/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/Near/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 00000000..4bdeb0d0 Binary files /dev/null and b/Near/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/Near/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/Near/app/src/main/res/mipmap-xhdpi/ic_launcher.webp deleted file mode 100644 index 948a3070..00000000 Binary files a/Near/app/src/main/res/mipmap-xhdpi/ic_launcher.webp and /dev/null differ diff --git a/Near/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/Near/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 00000000..c57d22ed Binary files /dev/null and b/Near/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/Near/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/Near/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp deleted file mode 100644 index 1b9a6956..00000000 Binary files a/Near/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp and /dev/null differ diff --git a/Near/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/Near/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 00000000..10430089 Binary files /dev/null and b/Near/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/Near/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/Near/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp deleted file mode 100644 index 28d4b77f..00000000 Binary files a/Near/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp and /dev/null differ diff --git a/Near/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/Near/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 00000000..afc978cf Binary files /dev/null and b/Near/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/Near/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/Near/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp deleted file mode 100644 index 9287f508..00000000 Binary files a/Near/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp and /dev/null differ diff --git a/Near/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/Near/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 00000000..b24a89ed Binary files /dev/null and b/Near/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/Near/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/Near/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp deleted file mode 100644 index aa7d6427..00000000 Binary files a/Near/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp and /dev/null differ diff --git a/Near/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/Near/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 00000000..2010a26f Binary files /dev/null and b/Near/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/Near/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/Near/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp deleted file mode 100644 index 9126ae37..00000000 Binary files a/Near/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp and /dev/null differ diff --git a/Near/app/src/main/res/values/strings.xml b/Near/app/src/main/res/values/strings.xml index 173ad5c1..8ec92e67 100644 --- a/Near/app/src/main/res/values/strings.xml +++ b/Near/app/src/main/res/values/strings.xml @@ -8,6 +8,9 @@ 메뉴 네트워크 에러가 발생했습니다. + + 스플래쉬 배경 + Near 로고 Near 타이틀 diff --git a/Near/app/src/main/res/values/themes.xml b/Near/app/src/main/res/values/themes.xml index dbbeb9df..abc32d05 100644 --- a/Near/app/src/main/res/values/themes.xml +++ b/Near/app/src/main/res/values/themes.xml @@ -1,5 +1,14 @@ + + diff --git a/Near/gradle/libs.versions.toml b/Near/gradle/libs.versions.toml index d3aa3673..0c32d4ea 100644 --- a/Near/gradle/libs.versions.toml +++ b/Near/gradle/libs.versions.toml @@ -30,6 +30,8 @@ datastorePreferences = "1.1.7" datastoreCore = "1.1.7" #Kakao v2All = "2.21.7" +# Splash Screen +splashScreen = "1.0.1" [libraries] androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } @@ -63,6 +65,7 @@ logging-interceptor = { group = "com.squareup.okhttp3", name = "logging-intercep retrofit-kotlin-serialization-converter = { group = "com.squareup.retrofit2", name = "converter-kotlinx-serialization", version.ref = "retrofitVersion" } androidx-datastore-core = { group = "androidx.datastore", name = "datastore-core", version.ref = "datastoreCore" } v2-all = { module = "com.kakao.sdk:v2-all", version.ref = "v2All" } +androidx-core-splashscreen = { group = "androidx.core", name = "core-splashscreen", version.ref = "splashScreen" } [plugins] android-application = { id = "com.android.application", version.ref = "agp" }