Skip to content

Commit 48b7cfa

Browse files
yonghanJubngsh
authored andcommitted
✨ 러닝 종료 화면 구현
1 parent 64b9ae8 commit 48b7cfa

File tree

6 files changed

+192
-3
lines changed

6 files changed

+192
-3
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.whyranoid.presentation.runningfinish
2+
3+
import com.whyranoid.presentation.model.RunningHistoryUiModel
4+
5+
sealed class Event {
6+
data class PositiveButtonClick(val runningHistory: RunningHistoryUiModel) : Event()
7+
object NegativeButtonButtonClick : Event()
8+
}

presentation/src/main/java/com/whyranoid/presentation/runningfinish/RunningFinishFragment.kt

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,32 @@ package com.whyranoid.presentation.runningfinish
33
import android.os.Bundle
44
import android.view.View
55
import androidx.fragment.app.viewModels
6+
import androidx.navigation.fragment.findNavController
7+
import com.naver.maps.geometry.LatLngBounds
8+
import com.naver.maps.map.CameraUpdate
9+
import com.naver.maps.map.NaverMap
10+
import com.naver.maps.map.OnMapReadyCallback
611
import com.whyranoid.presentation.R
712
import com.whyranoid.presentation.base.BaseFragment
813
import com.whyranoid.presentation.databinding.FragmentRunningFinishBinding
14+
import com.whyranoid.presentation.model.RunningHistoryUiModel
915
import com.whyranoid.presentation.model.UiState
1016
import com.whyranoid.presentation.running.RunningFinishData
17+
import com.whyranoid.presentation.running.toLatLng
18+
import com.whyranoid.presentation.util.pxdp.PxDpUtil
1119
import com.whyranoid.presentation.util.repeatWhenUiStarted
1220
import dagger.hilt.android.AndroidEntryPoint
1321
import timber.log.Timber
1422

1523
@AndroidEntryPoint
1624
internal class RunningFinishFragment :
17-
BaseFragment<FragmentRunningFinishBinding>(R.layout.fragment_running_finish) {
25+
BaseFragment<FragmentRunningFinishBinding>(R.layout.fragment_running_finish),
26+
OnMapReadyCallback {
1827

1928
private val viewModel: RunningFinishViewModel by viewModels()
2029

30+
private var naverMap: NaverMap? = null
31+
2132
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
2233
super.onViewCreated(view, savedInstanceState)
2334

@@ -40,6 +51,12 @@ internal class RunningFinishFragment :
4051
}
4152
}
4253
}
54+
55+
viewLifecycleOwner.repeatWhenUiStarted {
56+
viewModel.eventFlow.collect { event ->
57+
handleEvent(event)
58+
}
59+
}
4360
}
4461

4562
private fun handleDataStateUninitialized() {
@@ -51,10 +68,42 @@ internal class RunningFinishFragment :
5168
}
5269

5370
private fun handleDataStateSuccess(runningFinishData: RunningFinishData) {
54-
Timber.d(runningFinishData.toString())
71+
binding.runningHistoryItem.runningHistory = runningFinishData.runningHistory
72+
73+
naverMap?.let {
74+
val cameraUpdate = CameraUpdate.fitBounds(
75+
LatLngBounds.Builder().include(
76+
runningFinishData.runningPositionList.flatten().map { position ->
77+
position.toLatLng()
78+
}
79+
).build(),
80+
PxDpUtil.pxToDp(requireContext(), 20)
81+
)
82+
it.moveCamera(cameraUpdate)
83+
}
5584
}
5685

5786
private fun handleDataStateFailure(throwable: Throwable) {
5887
Timber.d(throwable.message)
5988
}
89+
90+
private fun handleEvent(event: Event) {
91+
when (event) {
92+
is Event.NegativeButtonButtonClick -> findNavController().popBackStack()
93+
is Event.PositiveButtonClick -> handlePositiveButtonClicked(event.runningHistory)
94+
}
95+
}
96+
97+
private fun handlePositiveButtonClicked(runningHistory: RunningHistoryUiModel) {
98+
// TODO Safe-Args 활용 CreateRunningPostFragment로 이동
99+
}
100+
101+
override fun onMapReady(naverMap: NaverMap) {
102+
this.naverMap = naverMap
103+
if (viewModel.runningFinishDataState.value is UiState.Success) {
104+
handleDataStateSuccess(
105+
(viewModel.runningFinishDataState.value as UiState.Success<RunningFinishData>).value
106+
)
107+
}
108+
}
60109
}

presentation/src/main/java/com/whyranoid/presentation/runningfinish/RunningFinishViewModel.kt

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,27 @@ import androidx.lifecycle.SavedStateHandle
44
import androidx.lifecycle.ViewModel
55
import androidx.lifecycle.viewModelScope
66
import com.whyranoid.domain.model.MoGakRunException
7+
import com.whyranoid.domain.usecase.SaveRunningHistoryUseCase
78
import com.whyranoid.presentation.model.UiState
89
import com.whyranoid.presentation.running.RunningFinishData
910
import com.whyranoid.presentation.running.RunningViewModel.Companion.RUNNING_FINISH_DATA_KEY
1011
import dagger.hilt.android.lifecycle.HiltViewModel
12+
import kotlinx.coroutines.flow.MutableSharedFlow
1113
import kotlinx.coroutines.flow.MutableStateFlow
14+
import kotlinx.coroutines.flow.asSharedFlow
1215
import kotlinx.coroutines.flow.asStateFlow
1316
import kotlinx.coroutines.launch
1417
import javax.inject.Inject
1518

1619
@HiltViewModel
1720
class RunningFinishViewModel @Inject constructor(
21+
private val saveRunningHistoryUseCase: SaveRunningHistoryUseCase,
1822
private val stateHandle: SavedStateHandle
1923
) : ViewModel() {
2024

25+
private val _eventFlow = MutableSharedFlow<Event>()
26+
val eventFlow get() = _eventFlow.asSharedFlow()
27+
2128
private val _runningFinishDataState =
2229
MutableStateFlow<UiState<RunningFinishData>>(UiState.UnInitialized)
2330
val runningFinishDataState get() = _runningFinishDataState.asStateFlow()
@@ -31,11 +38,41 @@ class RunningFinishViewModel @Inject constructor(
3138
viewModelScope.launch {
3239
stateHandle.get<RunningFinishData>(RUNNING_FINISH_DATA_KEY)?.let { runningFinishData ->
3340
_runningFinishDataState.value = UiState.Success(runningFinishData)
41+
with(runningFinishData.runningHistory) {
42+
saveRunningHistoryUseCase(
43+
historyId,
44+
startedAt,
45+
finishedAt,
46+
totalRunningTime,
47+
pace,
48+
totalDistance
49+
)
50+
}
3451
} ?: kotlin.run {
3552
_runningFinishDataState.value = UiState.Failure(
3653
MoGakRunException.FileNotFoundedException
3754
)
3855
}
3956
}
4057
}
58+
59+
fun onPositiveButtonClicked() {
60+
(runningFinishDataState.value as? UiState.Success<RunningFinishData>)?.value?.let { runningFinishData ->
61+
emitEvent(Event.PositiveButtonClick(runningFinishData.runningHistory))
62+
} ?: kotlin.run {
63+
_runningFinishDataState.value = UiState.Failure(
64+
MoGakRunException.FileNotFoundedException
65+
)
66+
}
67+
}
68+
69+
fun onNegativeButtonClicked() {
70+
emitEvent(Event.NegativeButtonButtonClick)
71+
}
72+
73+
private fun emitEvent(event: Event) {
74+
viewModelScope.launch {
75+
_eventFlow.emit(event)
76+
}
77+
}
4178
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.whyranoid.presentation.util.pxdp
2+
3+
import android.content.Context
4+
5+
object PxDpUtil {
6+
fun dpToPx(context: Context, dp: Int): Int {
7+
val scale: Float = context.resources.displayMetrics.density
8+
return (dp * scale + 0.5f).toInt()
9+
}
10+
11+
fun pxToDp(context: Context, px: Int): Int {
12+
val scale: Float = context.resources.displayMetrics.density
13+
val mul = when (scale) {
14+
1.0f -> 4.0f
15+
1.5f -> 8 / 3.0f
16+
2.0f -> 2.0f
17+
else -> 1.0f
18+
}
19+
return (px / (scale * mul)).toInt()
20+
}
21+
}

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

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
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>
6+
57
<variable
68
name="vm"
79
type="com.whyranoid.presentation.runningfinish.RunningFinishViewModel" />
@@ -11,5 +13,72 @@
1113
android:layout_width="match_parent"
1214
android:layout_height="match_parent">
1315

16+
<com.google.android.material.appbar.AppBarLayout
17+
android:id="@+id/tool_bar"
18+
android:layout_width="match_parent"
19+
android:layout_height="wrap_content"
20+
app:layout_constraintTop_toTopOf="parent">
21+
22+
<com.google.android.material.appbar.MaterialToolbar
23+
android:id="@+id/top_app_bar"
24+
style="@style/Widget.MaterialComponents.Toolbar.Primary"
25+
android:layout_width="match_parent"
26+
android:layout_height="?attr/actionBarSize"
27+
app:title="@string/running_finish_tool_bar"
28+
app:titleCentered="true" />
29+
30+
</com.google.android.material.appbar.AppBarLayout>
31+
32+
<androidx.constraintlayout.widget.ConstraintLayout
33+
android:layout_width="match_parent"
34+
android:layout_height="0dp"
35+
app:layout_constraintBottom_toTopOf="@id/running_history_item"
36+
app:layout_constraintTop_toBottomOf="@id/tool_bar">
37+
38+
<fragment xmlns:app="http://schemas.android.com/apk/res-auto"
39+
android:id="@+id/map_fragment"
40+
android:name="com.naver.maps.map.MapFragment"
41+
android:layout_width="match_parent"
42+
android:layout_height="match_parent"
43+
app:navermap_compassEnabled="false"
44+
app:navermap_locationButtonEnabled="false"
45+
app:navermap_rotateGesturesEnabled="false"
46+
app:navermap_scrollGesturesEnabled="false"
47+
app:navermap_stopGesturesEnabled="false"
48+
app:navermap_tiltGesturesEnabled="false"
49+
app:navermap_zoomControlEnabled="false"
50+
app:navermap_zoomGesturesEnabled="false" />
51+
52+
</androidx.constraintlayout.widget.ConstraintLayout>
53+
54+
<include
55+
android:id="@+id/running_history_item"
56+
layout="@layout/item_running_history"
57+
android:layout_width="match_parent"
58+
android:layout_height="wrap_content"
59+
app:layout_constraintBottom_toTopOf="@id/btn_positive" />
60+
61+
<com.google.android.material.button.MaterialButton
62+
android:id="@+id/btn_positive"
63+
android:layout_width="0dp"
64+
android:layout_height="wrap_content"
65+
android:layout_margin="12dp"
66+
android:onClick="@{() -> vm.onPositiveButtonClicked()}"
67+
android:text="@string/running_finish_positive_btn"
68+
app:layout_constraintBottom_toBottomOf="parent"
69+
app:layout_constraintEnd_toStartOf="@id/btn_negative"
70+
app:layout_constraintStart_toStartOf="parent" />
71+
72+
<com.google.android.material.button.MaterialButton
73+
android:id="@+id/btn_negative"
74+
android:layout_width="0dp"
75+
android:layout_height="wrap_content"
76+
android:layout_margin="12dp"
77+
android:onClick="@{() -> vm.onNegativeButtonClicked()}"
78+
android:text="@string/running_finish_negative_btn"
79+
app:layout_constraintBottom_toBottomOf="parent"
80+
app:layout_constraintEnd_toEndOf="parent"
81+
app:layout_constraintStart_toEndOf="@id/btn_positive" />
82+
1483
</androidx.constraintlayout.widget.ConstraintLayout>
1584
</layout>

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,4 +98,9 @@
9898
<string name="text_un_duplicated_group_name">사용 가능한 그룹 이름입니다!</string>
9999
<string name="text_like_count">좋아요 %d개</string>
100100

101+
<!--라닝 종료 화면-->
102+
<string name="running_finish_tool_bar">러닝 종료</string>
103+
<string name="running_finish_positive_btn">자랑할래요</string>
104+
<string name="running_finish_negative_btn">괜찮아요</string>
105+
101106
</resources>

0 commit comments

Comments
 (0)