Skip to content

Commit

Permalink
[MERGE] #28 -> develop
Browse files Browse the repository at this point in the history
[FEAT/#28] 리듬뷰 / 단계별 이미지 에셋 설정 및 재생속도 조정
  • Loading branch information
Marchbreeze authored Jul 31, 2024
2 parents ae1ceba + fbb3d99 commit 73207e2
Show file tree
Hide file tree
Showing 30 changed files with 1,804 additions and 70 deletions.
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package com.kkkk.data.dataSource

import com.kkkk.data.dto.BaseResponse
import com.kkkk.data.dto.request.RecordRequestDto
import okhttp3.ResponseBody
import java.io.File

interface RhythmDataSource {
suspend fun postToGetRhythmUrl(
Expand All @@ -12,4 +12,8 @@ interface RhythmDataSource {
suspend fun getRhythmWav(
url: String
): ResponseBody

suspend fun postRhythmRecord(
request: RecordRequestDto
): BaseResponse<String>
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.kkkk.data.dataSourceImpl

import com.kkkk.data.dataSource.RhythmDataSource
import com.kkkk.data.dto.BaseResponse
import com.kkkk.data.dto.request.RecordRequestDto
import com.kkkk.data.service.RhythmService
import okhttp3.ResponseBody
import javax.inject.Inject
Expand All @@ -17,4 +18,7 @@ constructor(

override suspend fun getRhythmWav(url: String): ResponseBody =
rhythmService.getRhythmWav(url)

override suspend fun postRhythmRecord(request: RecordRequestDto): BaseResponse<String> =
rhythmService.postRhythmRecord(request)
}
19 changes: 19 additions & 0 deletions data/src/main/java/com/kkkk/data/dto/request/RecordRequestDto.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.kkkk.data.dto.request

import com.kkkk.domain.entity.request.RecordRequestModel
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class RecordRequestDto(
@SerialName("accuracy")
val accuracy: Double,
@SerialName("duration")
val duration: Int,
@SerialName("steps")
val steps: Int,
) {
companion object {
fun RecordRequestModel.toDto() = RecordRequestDto(accuracy, duration, steps)
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.kkkk.data.repositoryImpl

import com.kkkk.data.dataSource.RhythmDataSource
import com.kkkk.data.dto.request.RecordRequestDto.Companion.toDto
import com.kkkk.domain.entity.request.RecordRequestModel
import com.kkkk.domain.repository.RhythmRepository
import javax.inject.Inject

Expand All @@ -19,4 +21,9 @@ constructor(
runCatching {
rhythmDataSource.getRhythmWav(url).bytes()
}

override suspend fun postRhythmRecord(request: RecordRequestModel): Result<String> =
runCatching {
rhythmDataSource.postRhythmRecord(request.toDto()).data
}
}
7 changes: 7 additions & 0 deletions data/src/main/java/com/kkkk/data/service/RhythmService.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.kkkk.data.service

import com.kkkk.data.dto.BaseResponse
import com.kkkk.data.dto.request.RecordRequestDto
import okhttp3.ResponseBody
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.POST
import retrofit2.http.Path
Expand All @@ -17,4 +19,9 @@ interface RhythmService {
suspend fun getRhythmWav(
@Url url: String
): ResponseBody

@POST("api/v1/records")
suspend fun postRhythmRecord(
@Body request: RecordRequestDto
): BaseResponse<String>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.kkkk.domain.entity.request

data class RecordRequestModel(
val accuracy: Double,
val duration: Int,
val steps: Int,
)
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.kkkk.domain.repository

import java.io.File
import com.kkkk.domain.entity.request.RecordRequestModel

interface RhythmRepository {
suspend fun postToGetRhythmUrl(
Expand All @@ -10,4 +10,8 @@ interface RhythmRepository {
suspend fun getRhythmWav(
url: String
): Result<ByteArray>

suspend fun postRhythmRecord(
request: RecordRequestModel
): Result<String>
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.kkkk.presentation.main.rhythm

import android.content.DialogInterface
import android.os.Bundle
import android.view.View
import androidx.fragment.app.activityViewModels
Expand Down Expand Up @@ -29,6 +30,14 @@ class RhythmBottomSheet :
}

private fun initSubmitBtnListener() {
binding.btnSubmitLevel.setOnSingleClickListener { dismiss() }
binding.btnSubmitLevel.setOnSingleClickListener {
viewModel.setRhythmLevel()
dismiss()
}
}

override fun onDismiss(dialog: DialogInterface) {
super.onDismiss(dialog)
viewModel.resetTempRhythmLevel()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,19 @@ package com.kkkk.presentation.main.rhythm
import android.media.MediaPlayer
import android.os.Bundle
import android.view.View
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
import com.kkkk.core.base.BaseFragment
import com.kkkk.core.extension.colorOf
import com.kkkk.core.extension.drawableOf
import com.kkkk.core.extension.setOnSingleClickListener
import com.kkkk.core.extension.setStatusBarColor
import com.kkkk.core.extension.stringOf
import com.kkkk.core.extension.toast
import com.kkkk.core.state.UiState
import com.kkkk.presentation.main.rhythm.RhythmViewModel.Companion.LEVEL_UNDEFINED
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.launchIn
Expand All @@ -29,6 +30,7 @@ class RhythmFragment : BaseFragment<FragmentRhythmBinding>(R.layout.fragment_rhy

private val viewModel by activityViewModels<RhythmViewModel>()
private var rhythmBottomSheet: RhythmBottomSheet? = null
private var rhythmSaveDialog: RhythmSaveDialog? = null
private lateinit var mediaPlayer: MediaPlayer

override fun onViewCreated(
Expand All @@ -43,6 +45,7 @@ class RhythmFragment : BaseFragment<FragmentRhythmBinding>(R.layout.fragment_rhy
observeRhythmLevel()
observeRhythmUrlState()
observeDownloadState()
observeRecordSaveState()
}

private fun initChangeLevelBtnListener() {
Expand All @@ -69,47 +72,62 @@ class RhythmFragment : BaseFragment<FragmentRhythmBinding>(R.layout.fragment_rhy
mediaPlayer.pause()
switchPlayingState(false)
}
rhythmSaveDialog = RhythmSaveDialog()
rhythmSaveDialog?.show(parentFragmentManager, DIALOG_RHYTHM_SAVE)
}
}

private fun switchPlayingState(start: Boolean) {
with(binding) {
btnRhythmPlay.isVisible = !start
btnRhythmStop.isVisible = start
lottieRhythmBg.isVisible = start
}
}

private fun observeRhythmLevel() {
viewModel.rhythmLevel.observe(viewLifecycleOwner) { level ->
setLoadingView(true)
viewModel.rhythmLevel.flowWithLifecycle(lifecycle).distinctUntilChanged().onEach { level ->
if (level == LEVEL_UNDEFINED) return@onEach
if (::mediaPlayer.isInitialized) {
mediaPlayer.pause()
switchPlayingState(false)
}
setUiWithCurrentLevel()
viewModel.postToGetRhythmUrlFromServer(level)
}
}.launchIn(lifecycleScope)
}

private fun setUiWithCurrentLevel() {
binding.tvRhythmLevel.text =
getString(R.string.rhythm_tv_level, viewModel.rhythmLevel.value)
val (textColor, background) = when (viewModel.rhythmLevel.value?.rem(3)) {
1 -> Pair(R.color.purple_50, R.drawable.shape_purple50_line_17_rect)
2 -> Pair(R.color.sky_50, R.drawable.shape_sky50_line_17_rect)
0 -> Pair(R.color.green_50, R.drawable.shape_green50_line_17_rect)
val color = when (viewModel.rhythmLevel.value.rem(3)) {
1 -> COLOR_PURPLE
2 -> COLOR_SKY
0 -> COLOR_GREEN
else -> return
}
with(binding) {
tvRhythmLevel.setTextColor(colorOf(textColor))
tvRhythmLevel.background =
ContextCompat.getDrawable(requireContext(), background)
tvRhythmStep.setTextColor(colorOf(textColor))
tvRhythmStep.background =
ContextCompat.getDrawable(requireContext(), background)
tvRhythmLevel.apply {
text = getString(R.string.rhythm_tv_level, viewModel.rhythmLevel.value)
setTextColor(colorOf(getResource("${color}_50", COLOR)))
background =
drawableOf(getResource("shape_white_fill_${color}50_line_17_rect", DRAWABLE))
}
tvRhythmStep.apply {
setTextColor(colorOf(getResource("${color}_50", COLOR)))
background =
drawableOf(getResource("shape_white_fill_${color}50_line_17_rect", DRAWABLE))
}
ivRhythmBg.setImageResource(getResource("img_rhythm_bg_$color", DRAWABLE))
lottieRhythmBg.apply {
setAnimation(getResource("stempo_rhythm_$color", RAW))
speed = viewModel.bpm / FLOAT_120
playAnimation()
}
}
}

private fun getResource(name: String, defType: String) =
resources.getIdentifier(name, defType, requireContext().packageName)

private fun observeRhythmUrlState() {
viewModel.rhythmUrlState.flowWithLifecycle(lifecycle).distinctUntilChanged()
.onEach { state ->
Expand All @@ -118,6 +136,7 @@ class RhythmFragment : BaseFragment<FragmentRhythmBinding>(R.layout.fragment_rhy
if (File(requireContext().filesDir, viewModel.filename).exists()) {
setMediaPlayer()
} else {
setLoadingView(true)
viewModel.getRhythmWavFile(state.data)
}
}
Expand Down Expand Up @@ -175,23 +194,50 @@ class RhythmFragment : BaseFragment<FragmentRhythmBinding>(R.layout.fragment_rhy
}

private fun setLoadingView(isLoading: Boolean) {
binding.viewLoading.isVisible = isLoading
binding.layoutLoading.isVisible = isLoading
if (isLoading) {
setStatusBarColor(R.color.transparent_50)
} else {
setStatusBarColor(R.color.white)
}
}

private fun observeRecordSaveState() {
viewModel.recordSaveState.flowWithLifecycle(lifecycle).distinctUntilChanged()
.onEach { state ->
when (state) {
is UiState.Success -> {
// TODO : 여기에서 기존 걸음 0으로 만드는 로직 필요
toast(stringOf(R.string.rhythm_toast_save_success))
}

is UiState.Failure -> toast(stringOf(R.string.error_msg))
else -> return@onEach
}
}.launchIn(lifecycleScope)
}

override fun onDestroyView() {
super.onDestroyView()
rhythmBottomSheet = null
rhythmSaveDialog = null
if (::mediaPlayer.isInitialized) {
mediaPlayer.release()
}
}

companion object {
private const val BOTTOM_SHEET_CHANGE_LEVEL = "BOTTOM_SHEET_CHANGE_LEVEL"
private const val DIALOG_RHYTHM_SAVE = "DIALOG_RHYTHM_SAVE"

private const val COLOR_PURPLE = "purple"
private const val COLOR_SKY = "sky"
private const val COLOR_GREEN = "green"

private const val COLOR = "color"
private const val DRAWABLE = "drawable"
private const val RAW = "raw"

private const val FLOAT_120 = 120.00000000000000000000F
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.kkkk.presentation.main.rhythm

import android.os.Bundle
import android.view.View
import android.view.WindowManager
import androidx.fragment.app.activityViewModels
import com.kkkk.core.base.BaseDialog
import com.kkkk.core.extension.setOnSingleClickListener
import kr.genti.presentation.R
import kr.genti.presentation.databinding.DialogRhythmSaveBinding

class RhythmSaveDialog :
BaseDialog<DialogRhythmSaveBinding>(R.layout.dialog_rhythm_save) {
private val viewModel by activityViewModels<RhythmViewModel>()

override fun onStart() {
super.onStart()
dialog?.window?.apply {
setLayout(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
)
setBackgroundDrawableResource(R.color.transparent)
}
}

override fun onViewCreated(
view: View,
savedInstanceState: Bundle?,
) {
super.onViewCreated(view, savedInstanceState)

initPauseBtnListener()
initSaveBtnListener()
}

private fun initPauseBtnListener() {
binding.btnPause.setOnSingleClickListener { dismiss() }
}

private fun initSaveBtnListener() {
binding.btnSave.setOnSingleClickListener {
viewModel.posRhythmRecordToSave()
dismiss()
}
}
}
Loading

0 comments on commit 73207e2

Please sign in to comment.