Skip to content

Commit 9e369ad

Browse files
committed
Feat: 스플래시 화면 Lottie 애니메이션 및 토큰 확인 로직 추가
- 스플래시 화면에 Lottie 애니메이션을 추가하고, 애니메이션 완료 후 화면 이동 - 토큰 확인 로직을 추가하여 토큰 유무에 따라 다음 화면으로 분기
1 parent 8c6df19 commit 9e369ad

6 files changed

Lines changed: 98 additions & 30 deletions

File tree

app/src/main/java/com/threegap/bitnagil/MainNavHost.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,11 @@ fun MainNavHost(
2828
) {
2929
composable<Route.Splash> {
3030
SplashScreenContainer(
31-
navigateToIntro = { navigator.navController.navigate(Route.Intro) },
31+
navigateToIntro = {
32+
navigator.navController.navigate(Route.Intro) {
33+
popUpTo<Route.Splash> { inclusive = true }
34+
}
35+
},
3236
navigateToHome = {
3337
navigator.navController.navigate(Route.Home) {
3438
popUpTo(navigator.navController.graph.startDestinationId) {

app/src/main/res/values/themes.xml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<resources>
33

4-
<style name="Theme.Bitnagil" parent="android:Theme.Material.Light.NoActionBar" />
4+
<style name="Base.Theme.Bitnagil" parent="android:Theme.Material.Light.NoActionBar" />
5+
6+
<style name="Theme.Bitnagil" parent="Base.Theme.Bitnagil">
7+
<item name="android:windowIsTranslucent">true</item>
8+
</style>
59
</resources>
Lines changed: 55 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,28 @@
11
package com.threegap.bitnagil.presentation.splash
22

3+
import androidx.compose.animation.core.tween
4+
import androidx.compose.animation.fadeIn
5+
import androidx.compose.foundation.background
6+
import androidx.compose.foundation.layout.Arrangement
37
import androidx.compose.foundation.layout.Box
8+
import androidx.compose.foundation.layout.Column
49
import androidx.compose.foundation.layout.fillMaxSize
5-
import androidx.compose.material3.Text
10+
import androidx.compose.foundation.layout.padding
11+
import androidx.compose.foundation.layout.size
612
import androidx.compose.runtime.Composable
13+
import androidx.compose.runtime.getValue
14+
import androidx.compose.runtime.mutableStateOf
15+
import androidx.compose.runtime.remember
16+
import androidx.compose.runtime.setValue
717
import androidx.compose.ui.Alignment
818
import androidx.compose.ui.Modifier
919
import androidx.compose.ui.tooling.preview.Preview
20+
import androidx.compose.ui.unit.dp
1021
import androidx.hilt.navigation.compose.hiltViewModel
22+
import com.threegap.bitnagil.designsystem.BitnagilTheme
23+
import com.threegap.bitnagil.designsystem.R
24+
import com.threegap.bitnagil.designsystem.component.atom.BitnagilIcon
25+
import com.threegap.bitnagil.presentation.splash.component.template.BitnagilLottieAnimation
1126
import com.threegap.bitnagil.presentation.splash.model.SplashSideEffect
1227
import org.orbitmvi.orbit.compose.collectSideEffect
1328

@@ -24,26 +39,56 @@ fun SplashScreenContainer(
2439
}
2540
}
2641

27-
SplashScreen()
42+
SplashScreen(
43+
onCompleted = viewModel::onAnimationCompleted,
44+
)
2845
}
2946

3047
@Composable
3148
private fun SplashScreen(
49+
onCompleted: () -> Unit,
3250
modifier: Modifier = Modifier,
3351
) {
34-
Box(
35-
modifier = modifier.fillMaxSize(),
52+
var showIcon by remember { mutableStateOf(false) }
53+
54+
Column(
55+
modifier = modifier
56+
.fillMaxSize()
57+
.background(BitnagilTheme.colors.white),
58+
verticalArrangement = Arrangement.Center,
59+
horizontalAlignment = Alignment.CenterHorizontally
3660
) {
37-
Text(
38-
text = "야무진 로고 추가 예정",
39-
modifier = Modifier
40-
.align(Alignment.Center),
41-
)
61+
Box(
62+
contentAlignment = Alignment.Center
63+
) {
64+
BitnagilLottieAnimation(
65+
lottieJson = R.raw.splash_lottie,
66+
onComplete = onCompleted,
67+
onStart = {
68+
showIcon = true
69+
},
70+
maxFrame = 120,
71+
modifier = Modifier
72+
.padding(bottom = 36.dp)
73+
.size(204.dp)
74+
.align(Alignment.BottomCenter),
75+
)
76+
77+
androidx.compose.animation.AnimatedVisibility(
78+
visible = showIcon,
79+
enter = fadeIn(animationSpec = tween(500)),
80+
modifier = Modifier.align(Alignment.BottomCenter)
81+
) {
82+
BitnagilIcon(id = R.drawable.img_app_name)
83+
}
84+
}
4285
}
4386
}
4487

4588
@Preview(showBackground = true)
4689
@Composable
4790
private fun SplashScreenPreview() {
48-
SplashScreen()
91+
SplashScreen(
92+
onCompleted = {},
93+
)
4994
}

presentation/src/main/java/com/threegap/bitnagil/presentation/splash/SplashViewModel.kt

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -23,44 +23,59 @@ class SplashViewModel @Inject constructor(
2323
savedStateHandle = savedStateHandle,
2424
) {
2525

26+
private var hasToken: Boolean? = null
27+
2628
init {
27-
checkAutoLogin()
29+
checkTokenStatus()
2830
}
2931

3032
override suspend fun SimpleSyntax<SplashState, SplashSideEffect>.reduceState(
3133
intent: SplashIntent,
3234
state: SplashState,
3335
): SplashState? =
3436
when (intent) {
35-
is SplashIntent.SetLoading -> {
36-
state.copy(isLoading = intent.isLoading)
37+
38+
is SplashIntent.SetTokenChecked -> {
39+
state.copy(isTokenChecked = intent.hasToken != null)
3740
}
3841

3942
is SplashIntent.NavigateToIntro -> {
4043
sendSideEffect(SplashSideEffect.NavigateToIntro)
41-
state.copy(isLoading = false)
44+
null
4245
}
4346

4447
is SplashIntent.NavigateToHome -> {
4548
sendSideEffect(SplashSideEffect.NavigateToHome)
46-
state.copy(isLoading = false)
49+
null
4750
}
4851
}
4952

50-
private fun checkAutoLogin() {
53+
private fun checkTokenStatus() {
5154
viewModelScope.launch {
52-
sendIntent(SplashIntent.SetLoading(true))
53-
val delayDeferred = async { delay(2000L) }
54-
val tokenDeferred = async { hasTokenUseCase() }
55-
56-
delayDeferred.await()
57-
val hasToken = tokenDeferred.await()
55+
try {
56+
hasToken = hasTokenUseCase()
57+
sendIntent(SplashIntent.SetTokenChecked(hasToken))
58+
} catch (e: Exception) {
59+
hasToken = false
60+
sendIntent(SplashIntent.SetTokenChecked(false))
61+
}
62+
}
63+
}
5864

59-
if (hasToken) {
60-
sendIntent(SplashIntent.NavigateToHome)
61-
} else {
62-
sendIntent(SplashIntent.NavigateToIntro)
65+
fun onAnimationCompleted() {
66+
val tokenResult = hasToken
67+
if (tokenResult == null) {
68+
viewModelScope.launch {
69+
delay(100)
70+
onAnimationCompleted()
6371
}
72+
return
73+
}
74+
75+
if (tokenResult) {
76+
sendIntent(SplashIntent.NavigateToHome)
77+
} else {
78+
sendIntent(SplashIntent.NavigateToIntro)
6479
}
6580
}
6681
}

presentation/src/main/java/com/threegap/bitnagil/presentation/splash/model/SplashIntent.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package com.threegap.bitnagil.presentation.splash.model
33
import com.threegap.bitnagil.presentation.common.mviviewmodel.MviIntent
44

55
sealed class SplashIntent : MviIntent {
6-
data class SetLoading(val isLoading: Boolean) : SplashIntent()
6+
data class SetTokenChecked(val hasToken: Boolean?) : SplashIntent()
77
data object NavigateToIntro : SplashIntent()
88
data object NavigateToHome : SplashIntent()
99
}

presentation/src/main/java/com/threegap/bitnagil/presentation/splash/model/SplashState.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@ import kotlinx.parcelize.Parcelize
55

66
@Parcelize
77
data class SplashState(
8-
val isLoading: Boolean = false,
8+
val isTokenChecked: Boolean = false,
99
) : MviState

0 commit comments

Comments
 (0)