Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package com.runnect.runnect.presentation.countdown

import androidx.compose.ui.test.assertContentDescriptionEquals
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
import com.runnect.runnect.presentation.ui.theme.RunnectTheme
import org.junit.Assert.assertEquals
import org.junit.Rule
import org.junit.Test

class CountDownScreenTest {

@get:Rule
val composeTestRule = createComposeRule()

@Test
fun 카운트다운_배경_숫자_안내문구가_노출된다() {
composeTestRule.setContent {
RunnectTheme {
CountDownContent(count = 3)
}
}

composeTestRule.onNodeWithTag(CountDownScreenTestTags.BACKGROUND).assertIsDisplayed()
composeTestRule.onNodeWithTag(CountDownScreenTestTags.NUMBER)
.assertIsDisplayed()
.assertContentDescriptionEquals("3")
composeTestRule.onNodeWithText("잠시 후 러닝을 시작합니다").assertIsDisplayed()
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
}

@Test
fun 카운트다운이_끝나면_완료_콜백이_호출된다() {
var finishedCount = 0
composeTestRule.mainClock.autoAdvance = false

composeTestRule.setContent {
RunnectTheme {
CountDownRoute(
onFinished = { finishedCount += 1 }
)
}
}

composeTestRule.waitForIdle()
repeat(3) {
composeTestRule.mainClock.advanceTimeBy(CountDownStateMachine.TICK_MILLIS)
composeTestRule.waitForIdle()
}
composeTestRule.waitUntil(timeoutMillis = 5_000L) {
finishedCount == 1
}

assertEquals(1, finishedCount)
}
}
Original file line number Diff line number Diff line change
@@ -1,24 +1,18 @@
package com.runnect.runnect.presentation.countdown

import android.content.Intent
import android.graphics.drawable.Drawable
import android.os.Bundle
import android.view.animation.Animation
import android.view.animation.Animation.AnimationListener
import android.view.animation.AnimationUtils
import androidx.appcompat.content.res.AppCompatResources
import com.runnect.runnect.R
import com.runnect.runnect.binding.BindingActivity
import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatActivity
import com.runnect.runnect.data.dto.CourseData
import com.runnect.runnect.databinding.ActivityCountDownBinding
import com.runnect.runnect.presentation.run.RunActivity
import com.runnect.runnect.presentation.ui.theme.RunnectTheme
import com.runnect.runnect.util.analytics.Analytics
import com.runnect.runnect.util.analytics.EventName
import com.runnect.runnect.util.analytics.EventName.Param
import com.runnect.runnect.util.extension.getCompatibleParcelableExtra
import timber.log.Timber

class CountDownActivity: BindingActivity<ActivityCountDownBinding>(R.layout.activity_count_down) {
class CountDownActivity : AppCompatActivity() {
private val courseData: CourseData? by lazy { intent.getCompatibleParcelableExtra(EXTRA_COURSE_DATA) }

override fun onCreate(savedInstanceState: Bundle?) {
Expand All @@ -29,64 +23,39 @@ class CountDownActivity: BindingActivity<ActivityCountDownBinding>(R.layout.acti
Param.COURSE_ID to courseData?.courseId
)

val intentToRun = Intent(this, RunActivity::class.java)
val numList = arrayListOf(
AppCompatResources.getDrawable(this, R.drawable.anim_num1),
AppCompatResources.getDrawable(this, R.drawable.anim_num2)
)
val anim = AnimationUtils.loadAnimation(this, R.anim.anim_count)
setAnimationListener(anim, numList, intentToRun)
binding.ivCountDown.startAnimation(anim)
setContent {
RunnectTheme {
CountDownRoute(
onFinished = ::moveToRun
)
}
}
}

@Deprecated("Deprecated in Java")
override fun onBackPressed() {
Analytics.logEvent(
EventName.CLICK_CANCEL_COUNTDOWN,
Param.COURSE_ID to courseData?.courseId
)
finish()
overridePendingTransition(R.anim.slide_in_left, R.anim.slide_out_right)
overridePendingTransition(
com.runnect.runnect.R.anim.slide_in_left,
com.runnect.runnect.R.anim.slide_out_right
)
}

private fun setAnimationListener(
anim: Animation,
numList: ArrayList<Drawable?>,
intentToRun: Intent,
) {
var counter = COUNT_START

anim.setAnimationListener(object : AnimationListener {
override fun onAnimationStart(animation: Animation) {
}

override fun onAnimationEnd(animation: Animation) {
counter -= COUNT_DECREASE_UNIT
if (counter == COUNT_END) {
courseData?.let { courseData ->
intentToRun.apply {
putExtra(
EXTRA_COUNTDOWN_TO_RUN, courseData
)
}
}
startActivity(intentToRun)
finish()
} else {
binding.ivCountDown.post {
binding.ivCountDown.setImageDrawable(numList[counter])
binding.ivCountDown.startAnimation(animation)
}
}
}
override fun onAnimationRepeat(animation: Animation) {}
})
private fun moveToRun() {
val intentToRun = Intent(this, RunActivity::class.java)
courseData?.let { courseData ->
intentToRun.putExtra(EXTRA_COUNTDOWN_TO_RUN, courseData)
}
startActivity(intentToRun)
finish()
}

companion object {
const val COUNT_START = 2
const val COUNT_END = -1
const val COUNT_DECREASE_UNIT = 1
const val EXTRA_COUNTDOWN_TO_RUN = "CountToRunData"
const val EXTRA_COURSE_DATA = "CourseData"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package com.runnect.runnect.presentation.countdown

import androidx.annotation.DrawableRes
import androidx.compose.animation.core.FastOutSlowInEasing
import androidx.compose.animation.core.RepeatMode
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.infiniteRepeatable
import androidx.compose.animation.core.rememberInfiniteTransition
import androidx.compose.animation.core.tween
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.scale
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.runnect.runnect.R
import com.runnect.runnect.presentation.ui.theme.RunnectTheme
import com.runnect.runnect.presentation.ui.theme.White
import kotlinx.coroutines.delay

object CountDownScreenTestTags {
const val BACKGROUND = "count_down_background"
const val NUMBER = "count_down_number"
const val DESCRIPTION = "count_down_description"
}

object CountDownStateMachine {
const val INITIAL_COUNT = 3
private const val LAST_VISIBLE_COUNT = 1
const val TICK_MILLIS = 1_000L

fun nextCount(currentCount: Int): Int? =
if (currentCount > LAST_VISIBLE_COUNT) currentCount - 1 else null

@DrawableRes
fun numberDrawableRes(count: Int): Int = when (count) {
3 -> R.drawable.anim_num3
2 -> R.drawable.anim_num2
1 -> R.drawable.anim_num1
else -> error("Unsupported countdown number: $count")
}
}

@Composable
fun CountDownRoute(
onFinished: () -> Unit,
modifier: Modifier = Modifier,
) {
var currentCount by remember { mutableIntStateOf(CountDownStateMachine.INITIAL_COUNT) }

LaunchedEffect(currentCount) {
delay(CountDownStateMachine.TICK_MILLIS)
val nextCount = CountDownStateMachine.nextCount(currentCount)
if (nextCount == null) {
onFinished()
} else {
currentCount = nextCount
}
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

CountDownContent(
count = currentCount,
modifier = modifier
)
}

@Composable
fun CountDownContent(
count: Int,
modifier: Modifier = Modifier,
) {
val scale by rememberInfiniteTransition(label = "countDownScale")
.animateFloat(
initialValue = 0.4f,
targetValue = 1f,
animationSpec = infiniteRepeatable(
animation = tween(
durationMillis = CountDownStateMachine.TICK_MILLIS.toInt(),
easing = FastOutSlowInEasing
),
repeatMode = RepeatMode.Restart
),
label = "countDownNumberScale"
)

Box(
modifier = modifier.fillMaxSize()
) {
Image(
painter = painterResource(R.drawable.star_background),
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = Modifier
.fillMaxSize()
.testTag(CountDownScreenTestTags.BACKGROUND)
)
Image(
painter = painterResource(CountDownStateMachine.numberDrawableRes(count)),
contentDescription = count.toString(),
modifier = Modifier
.align(Alignment.BottomCenter)
.offset(y = (-350).dp)
.size(width = 88.dp, height = 117.dp)
.scale(scale)
.testTag(CountDownScreenTestTags.NUMBER)
)
Text(
text = stringResource(R.string.count_down_desc),
style = RunnectTheme.textStyle.medium15,
color = White,
modifier = Modifier
.align(Alignment.BottomCenter)
.offset(y = (-280).dp)
.testTag(CountDownScreenTestTags.DESCRIPTION)
)
}
}
13 changes: 0 additions & 13 deletions app/src/main/res/anim/anim_count.xml

This file was deleted.

43 changes: 0 additions & 43 deletions app/src/main/res/layout/activity_count_down.xml

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.runnect.runnect.presentation.countdown

import com.runnect.runnect.R
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
import org.junit.Test

class CountDownStateMachineTest {

@Test
fun `카운트다운은 3에서 시작한다`() {
assertEquals(3, CountDownStateMachine.INITIAL_COUNT)
}

@Test
fun `카운트다운은 3_2_1 순서로 진행되고 이후 종료된다`() {
assertEquals(2, CountDownStateMachine.nextCount(3))
assertEquals(1, CountDownStateMachine.nextCount(2))
assertNull(CountDownStateMachine.nextCount(1))
}

@Test
fun `카운트다운 숫자에 맞는 drawable을 반환한다`() {
assertEquals(R.drawable.anim_num3, CountDownStateMachine.numberDrawableRes(3))
assertEquals(R.drawable.anim_num2, CountDownStateMachine.numberDrawableRes(2))
assertEquals(R.drawable.anim_num1, CountDownStateMachine.numberDrawableRes(1))
}
}
Loading