Skip to content

Commit 5d64c9d

Browse files
bngshyonghanJu
andcommitted
✨ RunningViewModel 구현
Co-authored-by: yonghanJu <[email protected]>
1 parent 4aea853 commit 5d64c9d

File tree

5 files changed

+186
-3
lines changed

5 files changed

+186
-3
lines changed

presentation/src/main/java/com/whyranoid/presentation/running/RunningActivity.kt

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,26 @@
11
package com.whyranoid.presentation.running
22

33
import android.os.Bundle
4+
import androidx.activity.viewModels
45
import com.naver.maps.geometry.LatLng
56
import com.naver.maps.map.CameraUpdate
67
import com.naver.maps.map.MapView
78
import com.naver.maps.map.NaverMap
89
import com.naver.maps.map.OnMapReadyCallback
9-
import com.naver.maps.map.overlay.Marker
1010
import com.whyranoid.presentation.R
1111
import com.whyranoid.presentation.base.BaseActivity
1212
import com.whyranoid.presentation.databinding.ActivityRunningBinding
13+
import com.whyranoid.presentation.util.dateToString
14+
import com.whyranoid.presentation.util.repeatWhenUiStarted
15+
import dagger.hilt.android.AndroidEntryPoint
16+
import java.util.Date
1317

18+
@AndroidEntryPoint
1419
internal class RunningActivity :
1520
BaseActivity<ActivityRunningBinding>(R.layout.activity_running), OnMapReadyCallback {
1621

22+
private val viewModel: RunningViewModel by viewModels()
23+
1724
private lateinit var mapView: MapView
1825
private lateinit var naverMap: NaverMap
1926

@@ -23,6 +30,20 @@ internal class RunningActivity :
2330

2431
mapView.onCreate(savedInstanceState)
2532
mapView.getMapAsync(this)
33+
34+
binding.vm = viewModel
35+
36+
repeatWhenUiStarted {
37+
viewModel.runningState.collect { runningState ->
38+
with(runningState.runningData) {
39+
binding.tvStartTime.text = Date(startTime).dateToString("hh:mm")
40+
binding.tvRunningTime.text =
41+
String.format("%d:%02d", runningTime / 60, runningTime % 60)
42+
binding.tvTotalDistance.text = String.format("%.4f m", totalDistance)
43+
binding.tvPace.text = String.format("%.4f km/h", pace * 3.6)
44+
}
45+
}
46+
}
2647
}
2748

2849
override fun onMapReady(naverMap: NaverMap) {
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package com.whyranoid.presentation.running
2+
3+
import android.content.Context
4+
import androidx.lifecycle.LiveData
5+
import androidx.lifecycle.ViewModel
6+
import androidx.lifecycle.viewModelScope
7+
import androidx.work.Constraints
8+
import androidx.work.ExistingWorkPolicy
9+
import androidx.work.NetworkType
10+
import androidx.work.OneTimeWorkRequestBuilder
11+
import androidx.work.WorkInfo
12+
import androidx.work.WorkManager
13+
import com.whyranoid.domain.usecase.StartRunningUseCase
14+
import dagger.hilt.android.lifecycle.HiltViewModel
15+
import dagger.hilt.android.qualifiers.ApplicationContext
16+
import kotlinx.coroutines.launch
17+
import javax.inject.Inject
18+
19+
@HiltViewModel
20+
class RunningViewModel @Inject constructor(
21+
@ApplicationContext context: Context,
22+
startRunningUseCase: StartRunningUseCase,
23+
private val runningRepository: RunningRepository
24+
) : ViewModel() {
25+
26+
val runningState = runningRepository.runningState
27+
28+
init {
29+
startRunningWorker(context)
30+
if (runningRepository.runningState.value is RunningState.NotRunning) {
31+
viewModelScope.launch {
32+
startRunningUseCase()
33+
}
34+
}
35+
}
36+
37+
private fun startRunningWorker(context: Context): LiveData<WorkInfo> {
38+
val constraints = Constraints.Builder()
39+
.setRequiredNetworkType(NetworkType.CONNECTED)
40+
.build()
41+
42+
val runningRequest = OneTimeWorkRequestBuilder<RunningWorker>()
43+
.setConstraints(constraints)
44+
.build()
45+
46+
val workManager = WorkManager.getInstance(context)
47+
48+
workManager
49+
.beginUniqueWork(
50+
RunningWorker.WORKER_NAME,
51+
ExistingWorkPolicy.KEEP,
52+
runningRequest
53+
)
54+
.enqueue()
55+
56+
return workManager.getWorkInfoByIdLiveData(runningRequest.id)
57+
}
58+
59+
fun onCheckingPauseOrResume() {
60+
when (runningRepository.runningState.value) {
61+
is RunningState.Running -> onPauseButtonClicked()
62+
is RunningState.Paused -> onResumeButtonClicked()
63+
else -> return
64+
}
65+
}
66+
67+
private fun onPauseButtonClicked() {
68+
runningRepository.pauseRunning()
69+
}
70+
71+
private fun onResumeButtonClicked() {
72+
runningRepository.resumeRunning()
73+
}
74+
75+
fun onFinishButtonClicked() {
76+
// TODO: 액티비티에 이벤트 알려주기
77+
runningRepository.finishRunning()
78+
}
79+
}

presentation/src/main/java/com/whyranoid/presentation/util/Extensions.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ import androidx.lifecycle.LifecycleOwner
55
import androidx.lifecycle.lifecycleScope
66
import androidx.lifecycle.repeatOnLifecycle
77
import kotlinx.coroutines.launch
8+
import java.text.SimpleDateFormat
9+
import java.util.Date
10+
import java.util.Locale
811

912
fun LifecycleOwner.repeatWhenUiStarted(block: suspend () -> Unit) {
1013
lifecycleScope.launch {
@@ -13,3 +16,8 @@ fun LifecycleOwner.repeatWhenUiStarted(block: suspend () -> Unit) {
1316
}
1417
}
1518
}
19+
20+
fun Date.dateToString(format: String): String {
21+
val formatter = SimpleDateFormat(format, Locale.getDefault())
22+
return formatter.format(this)
23+
}

presentation/src/main/res/layout/activity_running.xml

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
<?xml version="1.0" encoding="utf-8"?>
2-
<layout xmlns:android="http://schemas.android.com/apk/res/android">
2+
<layout xmlns:android="http://schemas.android.com/apk/res/android"
3+
xmlns:app="http://schemas.android.com/apk/res-auto">
34

45
<data>
56

7+
<variable
8+
name="vm"
9+
type="com.whyranoid.presentation.running.RunningViewModel" />
610
</data>
711

812
<androidx.constraintlayout.widget.ConstraintLayout
@@ -14,5 +18,72 @@
1418
android:layout_width="match_parent"
1519
android:layout_height="match_parent" />
1620

21+
<TextView
22+
android:id="@+id/tv_running_time"
23+
android:layout_width="wrap_content"
24+
android:layout_height="wrap_content"
25+
android:layout_margin="8dp"
26+
android:textColor="@color/black"
27+
style="@style/MoGakRunText.Bold.Large"
28+
app:layout_constraintBottom_toTopOf="@id/tv_pace"
29+
app:layout_constraintEnd_toEndOf="parent"
30+
app:layout_constraintStart_toEndOf="@id/tv_startTime" />
31+
32+
<TextView
33+
android:id="@+id/tv_startTime"
34+
android:layout_width="wrap_content"
35+
android:layout_height="wrap_content"
36+
android:layout_margin="8dp"
37+
android:textColor="@color/black"
38+
style="@style/MoGakRunText.Bold.Large"
39+
app:layout_constraintBottom_toTopOf="@id/tv_total_distance"
40+
app:layout_constraintEnd_toStartOf="@id/tv_running_time"
41+
app:layout_constraintStart_toStartOf="parent" />
42+
43+
<TextView
44+
android:id="@+id/tv_pace"
45+
android:layout_width="wrap_content"
46+
android:layout_height="wrap_content"
47+
android:layout_margin="8dp"
48+
android:textColor="@color/black"
49+
style="@style/MoGakRunText.Bold.Large"
50+
app:layout_constraintBottom_toTopOf="@id/btn_pause_or_resume"
51+
app:layout_constraintEnd_toEndOf="parent"
52+
app:layout_constraintStart_toEndOf="@id/tv_total_distance" />
53+
54+
<TextView
55+
android:id="@+id/tv_total_distance"
56+
android:layout_width="wrap_content"
57+
android:layout_height="wrap_content"
58+
android:layout_margin="8dp"
59+
android:textColor="@color/black"
60+
style="@style/MoGakRunText.Bold.Large"
61+
app:layout_constraintBottom_toTopOf="@id/btn_pause_or_resume"
62+
app:layout_constraintEnd_toStartOf="@id/tv_pace"
63+
app:layout_constraintStart_toStartOf="parent" />
64+
65+
66+
<com.google.android.material.button.MaterialButton
67+
android:id="@+id/btn_pause_or_resume"
68+
android:layout_width="0dp"
69+
android:layout_height="wrap_content"
70+
android:layout_margin="8dp"
71+
android:text="@string/running_pause_or_resume"
72+
android:onClick="@{() -> vm.onCheckingPauseOrResume()}"
73+
app:layout_constraintBottom_toBottomOf="parent"
74+
app:layout_constraintEnd_toStartOf="@id/btn_finish"
75+
app:layout_constraintStart_toStartOf="parent" />
76+
77+
<com.google.android.material.button.MaterialButton
78+
android:id="@+id/btn_finish"
79+
android:layout_width="0dp"
80+
android:layout_height="wrap_content"
81+
android:layout_margin="8dp"
82+
android:text="@string/running_finish"
83+
android:onClick="@{() -> vm.onFinishButtonClicked()}"
84+
app:layout_constraintBottom_toBottomOf="parent"
85+
app:layout_constraintEnd_toEndOf="parent"
86+
app:layout_constraintStart_toEndOf="@id/btn_pause_or_resume" />
87+
1788
</androidx.constraintlayout.widget.ConstraintLayout>
1889
</layout>

presentation/src/main/res/values/strings.xml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@
5757
<string name="running_history_label_finish_running_time">종료시간</string>
5858
<string name="running_history_label_running_distance">이동거리</string>
5959
<string name="running_history_label_running_pace">페이스</string>
60-
60+
6161
<!-- 그룹 상세보기 화면-->
6262
<string name="text_headcount">%d명</string>
6363
<string name="text_recruit">홍보하기</string>
@@ -67,4 +67,8 @@
6767
<string name="text_delete_group">그룹 삭제하기</string>
6868
<string name="text_cancel">취소</string>
6969

70+
<!-- 러닝 화면 -->
71+
<string name="running_pause_or_resume">일시정지/재개</string>
72+
<string name="running_finish">정지</string>
73+
7074
</resources>

0 commit comments

Comments
 (0)