diff --git a/data/src/main/java/com/kkkk/data/dataSource/RhythmDataSource.kt b/data/src/main/java/com/kkkk/data/dataSource/RhythmDataSource.kt index 3d0d6e5..605271b 100644 --- a/data/src/main/java/com/kkkk/data/dataSource/RhythmDataSource.kt +++ b/data/src/main/java/com/kkkk/data/dataSource/RhythmDataSource.kt @@ -2,11 +2,12 @@ package com.kkkk.data.dataSource import com.kkkk.data.dto.BaseResponse import com.kkkk.data.dto.request.RecordRequestDto +import com.kkkk.data.dto.request.RhythmRequestDto import okhttp3.ResponseBody interface RhythmDataSource { suspend fun postToGetRhythmUrl( - bpm: Int + request: RhythmRequestDto ): BaseResponse suspend fun getRhythmWav( diff --git a/data/src/main/java/com/kkkk/data/dataSourceImpl/RhythmDataSourceImpl.kt b/data/src/main/java/com/kkkk/data/dataSourceImpl/RhythmDataSourceImpl.kt index 14a62cd..63bf101 100644 --- a/data/src/main/java/com/kkkk/data/dataSourceImpl/RhythmDataSourceImpl.kt +++ b/data/src/main/java/com/kkkk/data/dataSourceImpl/RhythmDataSourceImpl.kt @@ -3,6 +3,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.dto.request.RhythmRequestDto import com.kkkk.data.service.RhythmService import okhttp3.ResponseBody import javax.inject.Inject @@ -13,8 +14,8 @@ constructor( private val rhythmService: RhythmService ) : RhythmDataSource { - override suspend fun postToGetRhythmUrl(bpm: Int): BaseResponse = - rhythmService.postToGetRhythmUrl(bpm) + override suspend fun postToGetRhythmUrl(request: RhythmRequestDto): BaseResponse = + rhythmService.postToGetRhythmUrl(request) override suspend fun getRhythmWav(url: String): ResponseBody = rhythmService.getRhythmWav(url) diff --git a/data/src/main/java/com/kkkk/data/dto/request/RhythmRequestDto.kt b/data/src/main/java/com/kkkk/data/dto/request/RhythmRequestDto.kt new file mode 100644 index 0000000..7a6e442 --- /dev/null +++ b/data/src/main/java/com/kkkk/data/dto/request/RhythmRequestDto.kt @@ -0,0 +1,17 @@ +package com.kkkk.data.dto.request + +import com.kkkk.domain.entity.request.RhythmRequestModel +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class RhythmRequestDto( + @SerialName("bpm") + val bpm: Int, + @SerialName("bit") + val bit: Int, +) { + companion object { + fun RhythmRequestModel.toDto() = RhythmRequestDto(bpm, bit) + } +} \ No newline at end of file diff --git a/data/src/main/java/com/kkkk/data/repositoryImpl/RhythmRepositoryImpl.kt b/data/src/main/java/com/kkkk/data/repositoryImpl/RhythmRepositoryImpl.kt index e5c3a8a..e6bfa74 100644 --- a/data/src/main/java/com/kkkk/data/repositoryImpl/RhythmRepositoryImpl.kt +++ b/data/src/main/java/com/kkkk/data/repositoryImpl/RhythmRepositoryImpl.kt @@ -2,7 +2,9 @@ package com.kkkk.data.repositoryImpl import com.kkkk.data.dataSource.RhythmDataSource import com.kkkk.data.dto.request.RecordRequestDto.Companion.toDto +import com.kkkk.data.dto.request.RhythmRequestDto.Companion.toDto import com.kkkk.domain.entity.request.RecordRequestModel +import com.kkkk.domain.entity.request.RhythmRequestModel import com.kkkk.domain.repository.RhythmRepository import javax.inject.Inject @@ -12,9 +14,9 @@ constructor( private val rhythmDataSource: RhythmDataSource, ) : RhythmRepository { - override suspend fun postToGetRhythmUrl(bpm: Int): Result = + override suspend fun postToGetRhythmUrl(request: RhythmRequestModel): Result = runCatching { - rhythmDataSource.postToGetRhythmUrl(bpm).data + rhythmDataSource.postToGetRhythmUrl(request.toDto()).data } override suspend fun getRhythmWav(url: String): Result = diff --git a/data/src/main/java/com/kkkk/data/service/RhythmService.kt b/data/src/main/java/com/kkkk/data/service/RhythmService.kt index d0631db..9292fa9 100644 --- a/data/src/main/java/com/kkkk/data/service/RhythmService.kt +++ b/data/src/main/java/com/kkkk/data/service/RhythmService.kt @@ -2,17 +2,17 @@ package com.kkkk.data.service import com.kkkk.data.dto.BaseResponse import com.kkkk.data.dto.request.RecordRequestDto +import com.kkkk.data.dto.request.RhythmRequestDto import okhttp3.ResponseBody import retrofit2.http.Body import retrofit2.http.GET import retrofit2.http.POST -import retrofit2.http.Path import retrofit2.http.Url interface RhythmService { - @POST("/api/v1/rhythm/{bpm}") + @POST("/api/v1/rhythm") suspend fun postToGetRhythmUrl( - @Path("bpm") bpm: Int, + @Body request: RhythmRequestDto, ): BaseResponse @GET diff --git a/domain/src/main/kotlin/com/kkkk/domain/entity/request/RhythmRequestModel.kt b/domain/src/main/kotlin/com/kkkk/domain/entity/request/RhythmRequestModel.kt new file mode 100644 index 0000000..fb93fef --- /dev/null +++ b/domain/src/main/kotlin/com/kkkk/domain/entity/request/RhythmRequestModel.kt @@ -0,0 +1,6 @@ +package com.kkkk.domain.entity.request + +data class RhythmRequestModel( + val bpm: Int, + val bit: Int, +) \ No newline at end of file diff --git a/domain/src/main/kotlin/com/kkkk/domain/repository/RhythmRepository.kt b/domain/src/main/kotlin/com/kkkk/domain/repository/RhythmRepository.kt index fde666a..247d794 100644 --- a/domain/src/main/kotlin/com/kkkk/domain/repository/RhythmRepository.kt +++ b/domain/src/main/kotlin/com/kkkk/domain/repository/RhythmRepository.kt @@ -1,10 +1,11 @@ package com.kkkk.domain.repository import com.kkkk.domain.entity.request.RecordRequestModel +import com.kkkk.domain.entity.request.RhythmRequestModel interface RhythmRepository { suspend fun postToGetRhythmUrl( - bpm: Int + request: RhythmRequestModel ): Result suspend fun getRhythmWav( diff --git a/presentation/src/main/java/com/kkkk/presentation/main/MainActivity.kt b/presentation/src/main/java/com/kkkk/presentation/main/MainActivity.kt index 43c890d..821fb15 100644 --- a/presentation/src/main/java/com/kkkk/presentation/main/MainActivity.kt +++ b/presentation/src/main/java/com/kkkk/presentation/main/MainActivity.kt @@ -4,22 +4,34 @@ import android.os.Bundle import androidx.fragment.app.Fragment import androidx.fragment.app.commit import androidx.fragment.app.replace +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.flowWithLifecycle +import androidx.lifecycle.lifecycleScope import com.kkkk.core.base.BaseActivity import com.kkkk.presentation.main.profile.ProfileFragment import com.kkkk.presentation.main.record.RecordFragment import com.kkkk.presentation.main.rhythm.RhythmFragment +import com.kkkk.presentation.main.rhythm.RhythmViewModel +import com.kkkk.presentation.main.rhythm.StretchFragment import com.kkkk.presentation.main.study.StudyFragment import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import kr.genti.presentation.R import kr.genti.presentation.databinding.ActivityMainBinding @AndroidEntryPoint class MainActivity : BaseActivity(R.layout.activity_main) { + + private lateinit var rhythmViewModel: RhythmViewModel + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) initBnvItemIconTintList() initBnvItemSelectedListener() + initViewModelProvider() + observeStretchViewNavigate() } private fun initBnvItemIconTintList() { @@ -56,4 +68,18 @@ class MainActivity : BaseActivity(R.layout.activity_main) { replace(R.id.fcv_main, T::class.java.canonicalName) } } + + private fun initViewModelProvider() { + rhythmViewModel = ViewModelProvider(this)[RhythmViewModel::class.java] + } + + private fun observeStretchViewNavigate() { + rhythmViewModel.isStretchView.flowWithLifecycle(lifecycle).onEach { isStretch -> + if (isStretch) { + navigateTo() + } else { + navigateTo() + } + }.launchIn(lifecycleScope) + } } diff --git a/presentation/src/main/java/com/kkkk/presentation/main/record/RecordFragment.kt b/presentation/src/main/java/com/kkkk/presentation/main/record/RecordFragment.kt index a3c6b31..cc54777 100644 --- a/presentation/src/main/java/com/kkkk/presentation/main/record/RecordFragment.kt +++ b/presentation/src/main/java/com/kkkk/presentation/main/record/RecordFragment.kt @@ -18,7 +18,6 @@ import com.kkkk.core.extension.stringOf import com.kkkk.core.extension.toast import com.kkkk.core.state.UiState import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kr.genti.presentation.R @@ -39,6 +38,7 @@ class RecordFragment : BaseFragment(R.layout.fragment_rec observeReportMonth() observeChartEntry() setStatusBarColor(R.color.white) + viewModel.setGraphWithDate() } private fun observeReportMonth() { @@ -53,11 +53,11 @@ class RecordFragment : BaseFragment(R.layout.fragment_rec } private fun observeChartEntry() { - viewModel.chartEntry.flowWithLifecycle(lifecycle).distinctUntilChanged().onEach { state -> + viewModel.chartEntry.flowWithLifecycle(lifecycle).onEach { state -> when (state) { is UiState.Success -> { - binding.ivChartLoading.isVisible = false - binding.layoutChart.isVisible = true + setLoadingView(false) + binding.ivChartEmpty.isVisible = false binding.chartReport.apply { data = LineData(LineDataSet(state.data, CHART_RECORD).setDataSettings()) invalidate() @@ -66,17 +66,33 @@ class RecordFragment : BaseFragment(R.layout.fragment_rec } is UiState.Failure -> { - binding.ivChartLoading.isVisible = true + setLoadingView(false) + binding.ivChartEmpty.isVisible = true toast(stringOf(R.string.error_msg)) } - is UiState.Loading -> binding.ivChartLoading.isVisible = false + is UiState.Loading -> setLoadingView(true) - is UiState.Empty -> binding.ivChartLoading.isVisible = true + is UiState.Empty -> { + setLoadingView(false) + binding.ivChartEmpty.isVisible = true + } } }.launchIn(lifecycleScope) } + private fun setLoadingView(isLoading: Boolean) { + binding.layoutLoading.isVisible = isLoading + if (isLoading) { + binding.layoutChart.visibility = View.INVISIBLE + setStatusBarColor(R.color.transparent_50) + } else { + binding.layoutChart.visibility = View.VISIBLE + setStatusBarColor(R.color.white) + } + } + + private fun LineDataSet.setDataSettings(): LineDataSet { this.apply { color = colorOf(R.color.purple_50) diff --git a/presentation/src/main/java/com/kkkk/presentation/main/record/RecordViewModel.kt b/presentation/src/main/java/com/kkkk/presentation/main/record/RecordViewModel.kt index 7cf2917..0f11e3e 100644 --- a/presentation/src/main/java/com/kkkk/presentation/main/record/RecordViewModel.kt +++ b/presentation/src/main/java/com/kkkk/presentation/main/record/RecordViewModel.kt @@ -34,10 +34,6 @@ constructor( var startDate = "" var endDate = "" - init { - setGraphWithDate() - } - fun setIsChangingMonth() { isChangingMonth.value = isChangingMonth.value?.not() ?: false } @@ -48,7 +44,7 @@ constructor( setGraphWithDate() } - private fun setGraphWithDate() { + fun setGraphWithDate() { endDate = DATE_FORMAT.format(Date()) DATE_FORMAT.parse(endDate)?.let { date -> val postCalendar = Calendar.getInstance().apply { diff --git a/presentation/src/main/java/com/kkkk/presentation/main/rhythm/RhythmFragment.kt b/presentation/src/main/java/com/kkkk/presentation/main/rhythm/RhythmFragment.kt index 9e89fa6..82e857b 100644 --- a/presentation/src/main/java/com/kkkk/presentation/main/rhythm/RhythmFragment.kt +++ b/presentation/src/main/java/com/kkkk/presentation/main/rhythm/RhythmFragment.kt @@ -20,6 +20,8 @@ import com.google.android.gms.wearable.DataEventBuffer import com.google.android.gms.wearable.DataMapItem import com.google.android.gms.wearable.Wearable 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 @@ -59,33 +61,43 @@ class RhythmFragment : BaseFragment(R.layout.fragment_rhy ) { super.onViewCreated(view, savedInstanceState) - initChangeLevelBtnListener() + setLoadingView(true) + initChangeRhythmBtnListener() + initStretchNavigateBtnListener() initPlayBtnListener() initStopBtnListener() initWearableSyncBtnListener() + initExistingRhythm() observeStepCount() observeRhythmChanged() observeRhythmUrlState() observeDownloadState() observeRecordSaveState() - setStatusBarColor(R.color.white) } - private fun initChangeLevelBtnListener() { + private fun initChangeRhythmBtnListener() { binding.btnChangeLevel.setOnSingleClickListener { rhythmBottomSheet = RhythmBottomSheet() rhythmBottomSheet?.show(parentFragmentManager, BOTTOM_SHEET_CHANGE_LEVEL) } } + private fun initStretchNavigateBtnListener() { + binding.btnStretchMode.setOnSingleClickListener { + viewModel.navigateToStretchView(true) + } + } + private fun initPlayBtnListener() { binding.btnRhythmPlay.setOnSingleClickListener { - if (::mediaPlayer.isInitialized) { - mediaPlayer.start() - switchPlayingState(true) - requireActivity().window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) - } else { - toast(stringOf(R.string.error_msg)) + if (!viewModel.isLoading) { + if (::mediaPlayer.isInitialized) { + mediaPlayer.start() + switchPlayingState(true) + requireActivity().window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) + } else { + toast(stringOf(R.string.error_msg)) + } } } } @@ -102,6 +114,15 @@ class RhythmFragment : BaseFragment(R.layout.fragment_rhy } } + override fun onStop() { + super.onStop() + if (::mediaPlayer.isInitialized) { + mediaPlayer.pause() + switchPlayingState(false) + requireActivity().window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) + } + } + private fun switchPlayingState(start: Boolean) { with(binding) { btnRhythmPlay.isVisible = !start @@ -116,29 +137,35 @@ class RhythmFragment : BaseFragment(R.layout.fragment_rhy } } + private fun initExistingRhythm() { + setUiWithCurrentRhythm() + viewModel.postToGetRhythmUrlFromServer() + } + private fun observeStepCount() { - viewModel.stepCount.flowWithLifecycle(lifecycle).distinctUntilChanged().onEach { level -> - binding.tvRhythmStep.text = viewModel.stepCount.value.toString() + viewModel.stepCount.flowWithLifecycle(lifecycle).distinctUntilChanged().onEach { + binding.tvRhythmStep.text = + getString(R.string.rhythm_tv_step, viewModel.stepCount.value) }.launchIn(lifecycleScope) } private fun observeRhythmChanged() { - viewModel.isRhythmChanged.flowWithLifecycle(lifecycle).distinctUntilChanged() - .onEach { isChanged -> - if (isChanged) { - if (::mediaPlayer.isInitialized) { - mediaPlayer.pause() - switchPlayingState(false) - } - setUiWithCurrentLevel() - viewModel.resetRhythmChangedState() - viewModel.postToGetRhythmUrlFromServer() + viewModel.isRhythmChanged.flowWithLifecycle(lifecycle).onEach { isChanged -> + if (isChanged) { + setLoadingView(true) + if (::mediaPlayer.isInitialized) { + mediaPlayer.pause() + switchPlayingState(false) } - }.launchIn(lifecycleScope) + setUiWithCurrentRhythm() + viewModel.resetRhythmChangedState() + viewModel.postToGetRhythmUrlFromServer() + } + }.launchIn(lifecycleScope) } - private fun setUiWithCurrentLevel() { - val color = when (viewModel.bit.rem(3)) { + private fun setUiWithCurrentRhythm() { + val color = when (viewModel.bit) { 2 -> COLOR_PURPLE 3 -> COLOR_SKY 4 -> COLOR_GREEN @@ -146,25 +173,25 @@ class RhythmFragment : BaseFragment(R.layout.fragment_rhy 8 -> COLOR_SKY else -> return } -// with(binding) { -// 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() -// } -// } + with(binding) { + tvRhythmBpm.apply { + text = getString(R.string.rhythm_tv_bpm, viewModel.bpm) + setTextColor(colorOf(getResource("${color}_50", COLOR))) + background = + drawableOf(getResource("shape_white_fill_${color}50_line_17_rect", DRAWABLE)) + } + tvRhythmBit.apply { + text = getString(R.string.rhythm_tv_bit, viewModel.bit) + background = + drawableOf(getResource("shape_${color}50_fill_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) = @@ -176,6 +203,7 @@ class RhythmFragment : BaseFragment(R.layout.fragment_rhy when (state) { is UiState.Success -> { if (File(requireContext().filesDir, viewModel.filename).exists()) { + setLoadingView(false) setMediaPlayer() } else { setLoadingView(true) @@ -212,24 +240,20 @@ class RhythmFragment : BaseFragment(R.layout.fragment_rhy outputStream.write(byteArray) outputStream.flush() } + }.onSuccess { + setMediaPlayer() + }.onFailure { + toast(stringOf(R.string.error_msg)) } - .onSuccess { - setMediaPlayer() - } - .onFailure { - toast(stringOf(R.string.error_msg)) - } } private fun setMediaPlayer() { if (::mediaPlayer.isInitialized) mediaPlayer.release() mediaPlayer = MediaPlayer().apply { setDataSource( - File( - requireContext().filesDir, - viewModel.filename - ).absolutePath + File(requireContext().filesDir, viewModel.filename).absolutePath ) + isLooping = true prepare() } setLoadingView(false) @@ -237,6 +261,7 @@ class RhythmFragment : BaseFragment(R.layout.fragment_rhy private fun setLoadingView(isLoading: Boolean) { binding.layoutLoading.isVisible = isLoading + viewModel.isLoading = isLoading if (isLoading) { setStatusBarColor(R.color.transparent_50) } else { diff --git a/presentation/src/main/java/com/kkkk/presentation/main/rhythm/RhythmViewModel.kt b/presentation/src/main/java/com/kkkk/presentation/main/rhythm/RhythmViewModel.kt index a3bdaef..b180a3b 100644 --- a/presentation/src/main/java/com/kkkk/presentation/main/rhythm/RhythmViewModel.kt +++ b/presentation/src/main/java/com/kkkk/presentation/main/rhythm/RhythmViewModel.kt @@ -5,6 +5,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.kkkk.core.state.UiState import com.kkkk.domain.entity.request.RecordRequestModel +import com.kkkk.domain.entity.request.RhythmRequestModel import com.kkkk.domain.repository.RhythmRepository import com.kkkk.domain.repository.UserRepository import dagger.hilt.android.lifecycle.HiltViewModel @@ -31,6 +32,11 @@ constructor( var isBpmMinusAvailable = MutableLiveData(false) var isBpmPlusAvailable = MutableLiveData(true) + var isLoading = false + + private val _isStretchView = MutableSharedFlow() + val isStretchView: SharedFlow = _isStretchView + private val _isRhythmChanged = MutableSharedFlow() val isRhythmChanged: SharedFlow = _isRhythmChanged @@ -57,6 +63,7 @@ constructor( init { initRhythmLevelFromDataStore() + } private fun initRhythmLevelFromDataStore() { @@ -106,13 +113,22 @@ constructor( } fun resetRhythmChangedState() { - _isRhythmChanged.resetReplayCache() + viewModelScope.launch { + _isRhythmChanged.emit(false) + } + } + + fun navigateToStretchView(isStretch: Boolean) { + viewModelScope.launch { + _isStretchView.emit(isStretch) + _isStretchView.resetReplayCache() + } } fun postToGetRhythmUrlFromServer() { _rhythmUrlState.value = UiState.Loading viewModelScope.launch { - rhythmRepository.postToGetRhythmUrl(bpm) + rhythmRepository.postToGetRhythmUrl(RhythmRequestModel(bpm, bit)) .onSuccess { _rhythmUrlState.value = UiState.Success(it) } diff --git a/presentation/src/main/java/com/kkkk/presentation/main/rhythm/StretchFragment.kt b/presentation/src/main/java/com/kkkk/presentation/main/rhythm/StretchFragment.kt new file mode 100644 index 0000000..3d3850a --- /dev/null +++ b/presentation/src/main/java/com/kkkk/presentation/main/rhythm/StretchFragment.kt @@ -0,0 +1,95 @@ +package com.kkkk.presentation.main.rhythm + +import android.media.MediaPlayer +import android.os.Bundle +import android.view.View +import android.view.WindowManager +import androidx.core.view.isVisible +import androidx.fragment.app.activityViewModels +import com.kkkk.core.base.BaseFragment +import com.kkkk.core.extension.setOnSingleClickListener +import com.kkkk.core.extension.stringOf +import com.kkkk.core.extension.toast +import dagger.hilt.android.AndroidEntryPoint +import kr.genti.presentation.R +import kr.genti.presentation.databinding.FragmentStretchBinding +import java.io.File + +@AndroidEntryPoint +class StretchFragment : BaseFragment(R.layout.fragment_stretch) { + + private val viewModel by activityViewModels() + private lateinit var mediaPlayer: MediaPlayer + + override fun onViewCreated( + view: View, + savedInstanceState: Bundle?, + ) { + super.onViewCreated(view, savedInstanceState) + + initRhythmNavigateBtnListener() + initPlayBtnListener() + initStopBtnListener() + setMediaPlayer() + } + + private fun initRhythmNavigateBtnListener() { + binding.btnRhythmMode.setOnSingleClickListener { + viewModel.navigateToStretchView(false) + } + } + + private fun initPlayBtnListener() { + binding.btnStretchPlay.setOnSingleClickListener { + if (::mediaPlayer.isInitialized) { + mediaPlayer.start() + switchPlayingState(true) + requireActivity().window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) + } else { + toast(stringOf(R.string.error_msg)) + } + } + } + + private fun initStopBtnListener() { + binding.btnStretchStop.setOnSingleClickListener { + if (::mediaPlayer.isInitialized) { + mediaPlayer.pause() + switchPlayingState(false) + requireActivity().window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) + } + } + } + + override fun onStop() { + super.onStop() + if (::mediaPlayer.isInitialized) { + mediaPlayer.pause() + switchPlayingState(false) + requireActivity().window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) + } + } + + private fun switchPlayingState(start: Boolean) { + with(binding) { + btnStretchPlay.isVisible = !start + btnStretchStop.isVisible = start + lottieStretchBg.isVisible = start + } + } + + private fun setMediaPlayer() { + if (File(requireContext().filesDir, viewModel.filename).exists()) { + if (::mediaPlayer.isInitialized) mediaPlayer.release() + mediaPlayer = MediaPlayer().apply { + setDataSource( + File(requireContext().filesDir, viewModel.filename).absolutePath + ) + isLooping = true + prepare() + } + } else { + toast(stringOf(R.string.error_msg)) + } + } +} \ No newline at end of file diff --git a/presentation/src/main/res/drawable/shape_gray200_fill_16_rect.xml b/presentation/src/main/res/drawable/shape_gray200_fill_17_rect.xml similarity index 80% rename from presentation/src/main/res/drawable/shape_gray200_fill_16_rect.xml rename to presentation/src/main/res/drawable/shape_gray200_fill_17_rect.xml index b5ed002..04bed02 100644 --- a/presentation/src/main/res/drawable/shape_gray200_fill_16_rect.xml +++ b/presentation/src/main/res/drawable/shape_gray200_fill_17_rect.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/presentation/src/main/res/drawable/shape_gray200_fill_26_rect.xml b/presentation/src/main/res/drawable/shape_gray200_fill_26_rect.xml new file mode 100644 index 0000000..dcf8162 --- /dev/null +++ b/presentation/src/main/res/drawable/shape_gray200_fill_26_rect.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/presentation/src/main/res/drawable/shape_green50_fill_17_rect.xml b/presentation/src/main/res/drawable/shape_green50_fill_17_rect.xml new file mode 100644 index 0000000..4d0745f --- /dev/null +++ b/presentation/src/main/res/drawable/shape_green50_fill_17_rect.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/presentation/src/main/res/drawable/shape_purple50_fill_16_rect.xml b/presentation/src/main/res/drawable/shape_purple50_fill_17_rect.xml similarity index 80% rename from presentation/src/main/res/drawable/shape_purple50_fill_16_rect.xml rename to presentation/src/main/res/drawable/shape_purple50_fill_17_rect.xml index fe33df5..b6ebfad 100644 --- a/presentation/src/main/res/drawable/shape_purple50_fill_16_rect.xml +++ b/presentation/src/main/res/drawable/shape_purple50_fill_17_rect.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/presentation/src/main/res/drawable/shape_sky50_fill_17_rect.xml b/presentation/src/main/res/drawable/shape_sky50_fill_17_rect.xml new file mode 100644 index 0000000..8dd401b --- /dev/null +++ b/presentation/src/main/res/drawable/shape_sky50_fill_17_rect.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/presentation/src/main/res/drawable/shape_white_fill_26_rect.xml b/presentation/src/main/res/drawable/shape_white_fill_26_rect.xml new file mode 100644 index 0000000..e972e75 --- /dev/null +++ b/presentation/src/main/res/drawable/shape_white_fill_26_rect.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/presentation/src/main/res/drawable/shape_white_fill_gray600_line_17_rect.xml b/presentation/src/main/res/drawable/shape_white_fill_gray600_line_17_rect.xml new file mode 100644 index 0000000..083a944 --- /dev/null +++ b/presentation/src/main/res/drawable/shape_white_fill_gray600_line_17_rect.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/presentation/src/main/res/layout/bottom_sheet_rhythm.xml b/presentation/src/main/res/layout/bottom_sheet_rhythm.xml index 09f6f2f..687fa56 100644 --- a/presentation/src/main/res/layout/bottom_sheet_rhythm.xml +++ b/presentation/src/main/res/layout/bottom_sheet_rhythm.xml @@ -60,13 +60,13 @@ android:layout_column="0" android:layout_columnWeight="1" android:layout_margin="8dp" - android:background="@{vm.tempBit == 2 ? @drawable/shape_purple50_fill_16_rect : @drawable/shape_gray200_fill_16_rect}" + android:background="@{vm.tempBit == 2 ? @drawable/shape_purple50_fill_17_rect : @drawable/shape_gray200_fill_17_rect}" android:gravity="center" android:onClick="@{() -> vm.setTempBit(2)}" android:paddingVertical="12dp" android:text="2 비트" android:textColor="@{vm.tempBit == 2 ? @color/white : @color/gray_600}" - tools:background="@drawable/shape_gray200_fill_16_rect" /> + tools:background="@drawable/shape_gray200_fill_17_rect" /> + tools:background="@drawable/shape_gray200_fill_17_rect" /> + tools:background="@drawable/shape_gray200_fill_17_rect" /> + tools:background="@drawable/shape_gray200_fill_17_rect" /> + tools:background="@drawable/shape_gray200_fill_17_rect" /> diff --git a/presentation/src/main/res/layout/fragment_record.xml b/presentation/src/main/res/layout/fragment_record.xml index 301c549..f50f9cd 100644 --- a/presentation/src/main/res/layout/fragment_record.xml +++ b/presentation/src/main/res/layout/fragment_record.xml @@ -23,7 +23,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="18dp" - android:layout_marginTop="32dp" + android:layout_marginTop="16dp" android:onClick="@{() -> vm.setIsChangingMonth()}" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" @@ -152,9 +152,10 @@ + app:layout_constraintTop_toTopOf="@id/layout_chart" /> + + + + + + \ No newline at end of file diff --git a/presentation/src/main/res/layout/fragment_rhythm.xml b/presentation/src/main/res/layout/fragment_rhythm.xml index 0527d6a..d28a164 100644 --- a/presentation/src/main/res/layout/fragment_rhythm.xml +++ b/presentation/src/main/res/layout/fragment_rhythm.xml @@ -11,32 +11,53 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - + app:layout_constraintTop_toTopOf="parent"> + + + + + + + app:layout_constraintTop_toBottomOf="@id/toggle_rhythm_mode" /> + + + + + app:layout_constraintHorizontal_chainStyle="packed" + app:layout_constraintStart_toEndOf="@id/tv_rhythm_bpm" + tools:text="000 걸음" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/presentation/src/main/res/layout/fragment_study.xml b/presentation/src/main/res/layout/fragment_study.xml index 95f17fc..9ae8d33 100644 --- a/presentation/src/main/res/layout/fragment_study.xml +++ b/presentation/src/main/res/layout/fragment_study.xml @@ -1,7 +1,6 @@ @@ -22,10 +21,10 @@ style="@style/TextAppearance.Stempo.Head2" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:layout_marginTop="16dp" android:text="과제" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" - margin:layout_marginTop="16dp" /> + app:layout_constraintTop_toTopOf="parent" /> @font/font_pretendard_semibold - 26sp - 9sp + 26dp + 9dp diff --git a/presentation/src/main/res/values/strings.xml b/presentation/src/main/res/values/strings.xml index 1683355..91aaf6f 100644 --- a/presentation/src/main/res/values/strings.xml +++ b/presentation/src/main/res/values/strings.xml @@ -21,7 +21,9 @@ 과제 마이 - %d단계 리듬 + %d비트 + %dbpm + %d걸음 리듬에 맞춰 걸어주세요! 단계 변경하기 완료하기