-
Notifications
You must be signed in to change notification settings - Fork 1
SplashActivity Handler 제거, SplashScreen API + ViewModel + 테스트로 교체 #400
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
96fc9cf
138f71d
9d01dd8
f147715
820aa9f
cd48d3d
4c8be96
47689c8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| #!/usr/bin/env bash | ||
| set -euo pipefail | ||
|
|
||
| PKGS="${TEST_PACKAGES:-}" | ||
| if [ -z "$PKGS" ]; then | ||
| ./gradlew connectedDebugAndroidTest --stacktrace | ||
| else | ||
| # connectedDebugAndroidTest uses runner args for filtering, not --tests | ||
| PKG_FILTER=$(echo "$PKGS" | tr ' ' '\n' | grep -v '^$' | tr '\n' ',' | sed 's/,$//') | ||
| ./gradlew connectedDebugAndroidTest \ | ||
| -Pandroid.testInstrumentationRunnerArguments.package="$PKG_FILTER" \ | ||
| --stacktrace | ||
| fi |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| package com.runnect.runnect.presentation.splash | ||
|
|
||
| import androidx.compose.ui.test.assertIsDisplayed | ||
| import androidx.compose.ui.test.junit4.createComposeRule | ||
| import androidx.compose.ui.test.onRoot | ||
| import org.junit.Rule | ||
| import org.junit.Test | ||
|
|
||
| class SplashScreenTest { | ||
|
|
||
| @get:Rule | ||
| val composeTestRule = createComposeRule() | ||
|
|
||
| @Test | ||
| fun `SplashScreen이_렌더링된다`() { | ||
| composeTestRule.setContent { | ||
| SplashScreen() | ||
| } | ||
| composeTestRule.onRoot().assertIsDisplayed() | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | |||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,19 @@ | |||||||||||||||||
| package com.runnect.runnect.presentation.splash | |||||||||||||||||
|
|
|||||||||||||||||
| import androidx.compose.foundation.Image | |||||||||||||||||
| import androidx.compose.foundation.layout.fillMaxSize | |||||||||||||||||
| import androidx.compose.runtime.Composable | |||||||||||||||||
| import androidx.compose.ui.Modifier | |||||||||||||||||
| import androidx.compose.ui.layout.ContentScale | |||||||||||||||||
| import androidx.compose.ui.res.painterResource | |||||||||||||||||
| import com.runnect.runnect.R | |||||||||||||||||
|
|
|||||||||||||||||
| @Composable | |||||||||||||||||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
|||||||||||||||||
| fun SplashScreen() { | |||||||||||||||||
| Image( | |||||||||||||||||
| painter = painterResource(id = R.drawable.splash), | |||||||||||||||||
| contentDescription = null, | |||||||||||||||||
| modifier = Modifier.fillMaxSize(), | |||||||||||||||||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
|||||||||||||||||
| contentScale = ContentScale.FillBounds, | |||||||||||||||||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
|||||||||||||||||
| ContentScale | ImageView scaleType | 동작 |
|---|---|---|
FillBounds |
fitXY |
비율 무시하고 컨테이너 꽉 채움 |
Crop |
centerCrop |
비율 유지, 짧은 축 기준으로 크롭 |
Fit |
fitCenter |
비율 유지, 긴 축 기준으로 맞춤 |
FillWidth |
fitStart(유사) |
너비 기준으로 채움 |
여기서 FillBounds를 선택한 이유: splash.webp가 정확히 기기 화면 비율(1440×3200)로 제작된 전체화면 이미지라 비율 왜곡이 발생해도 원본 windowBackground 렌더링 방식과 동일한 결과를 냄.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| package com.runnect.runnect.presentation.splash | ||
|
|
||
| import androidx.lifecycle.ViewModel | ||
| import androidx.lifecycle.viewModelScope | ||
| import dagger.hilt.android.lifecycle.HiltViewModel | ||
| import kotlinx.coroutines.delay | ||
| import kotlinx.coroutines.flow.MutableSharedFlow | ||
| import kotlinx.coroutines.flow.MutableStateFlow | ||
| import kotlinx.coroutines.flow.SharedFlow | ||
| import kotlinx.coroutines.flow.StateFlow | ||
| import kotlinx.coroutines.flow.asSharedFlow | ||
| import kotlinx.coroutines.flow.asStateFlow | ||
| import kotlinx.coroutines.launch | ||
| import javax.inject.Inject | ||
|
|
||
| @HiltViewModel | ||
| class SplashViewModel @Inject constructor() : ViewModel() { | ||
|
|
||
| private val _isReady = MutableStateFlow(false) | ||
| val isReady: StateFlow<Boolean> = _isReady.asStateFlow() | ||
|
|
||
| private val _navigateEvent = MutableSharedFlow<Unit>(replay = 1) | ||
| val navigateEvent: SharedFlow<Unit> = _navigateEvent.asSharedFlow() | ||
|
|
||
| init { | ||
| viewModelScope.launch { | ||
| delay(SPLASH_DELAY) | ||
| _isReady.value = true | ||
| _navigateEvent.emit(Unit) | ||
| } | ||
| } | ||
|
|
||
| companion object { | ||
| const val SPLASH_DELAY = 1000L | ||
| } | ||
| } |
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| package com.runnect.runnect.presentation.splash | ||
|
|
||
| import app.cash.turbine.test | ||
| import kotlinx.coroutines.Dispatchers | ||
| import kotlinx.coroutines.ExperimentalCoroutinesApi | ||
| import kotlinx.coroutines.test.StandardTestDispatcher | ||
| import kotlinx.coroutines.test.advanceTimeBy | ||
| import kotlinx.coroutines.test.advanceUntilIdle | ||
| import kotlinx.coroutines.test.resetMain | ||
| import kotlinx.coroutines.test.runTest | ||
| import kotlinx.coroutines.test.setMain | ||
| import org.junit.After | ||
| import org.junit.Assert.assertFalse | ||
| import org.junit.Assert.assertTrue | ||
| import org.junit.Before | ||
| import org.junit.Test | ||
|
|
||
| @OptIn(ExperimentalCoroutinesApi::class) | ||
| class SplashViewModelTest { | ||
| private val testDispatcher = StandardTestDispatcher() | ||
|
|
||
| @Before | ||
| fun setUp() { | ||
| Dispatchers.setMain(testDispatcher) | ||
| } | ||
|
|
||
| @After | ||
| fun tearDown() { | ||
| Dispatchers.resetMain() | ||
| } | ||
|
|
||
| @Test | ||
| fun `초기 상태에서 isReady는 false다`() { | ||
| val viewModel = SplashViewModel() | ||
| assertFalse(viewModel.isReady.value) | ||
| } | ||
|
|
||
| @Test | ||
| fun `1초 경과 전에는 isReady가 false다`() = runTest(testDispatcher) { | ||
| val viewModel = SplashViewModel() | ||
| advanceTimeBy(SplashViewModel.SPLASH_DELAY - 1) | ||
| assertFalse(viewModel.isReady.value) | ||
| } | ||
|
|
||
| @Test | ||
| fun `1초 경과 후 isReady가 true가 된다`() = runTest(testDispatcher) { | ||
| val viewModel = SplashViewModel() | ||
| advanceUntilIdle() | ||
| assertTrue(viewModel.isReady.value) | ||
| } | ||
|
|
||
| @Test | ||
| fun `1초 후 navigateEvent가 emit된다`() = runTest(testDispatcher) { | ||
| val viewModel = SplashViewModel() | ||
| viewModel.navigateEvent.test { | ||
| advanceTimeBy(SplashViewModel.SPLASH_DELAY) | ||
| awaitItem() | ||
| cancelAndIgnoreRemainingEvents() | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
setContent { }— Compose 진입점Activity에서 XML
setContentView(R.layout.xxx)를 대체하는 Compose 진입점.이 블록 안에 넘긴
@Composable함수가 화면 전체를 그린다.핵심 개념:
setContent내부에서 Compose 런타임이 초기화되고,@Composable함수 호출 트리가 UI 트리로 변환됨ComponentActivity.setContent는androidx.activity.compose라이브러리가 제공 —AppCompatActivity를 상속해도 사용 가능공식 문서 — setContent