diff --git a/Prezel/core/data/src/main/java/com/team/prezel/core/data/error/AppErrorExt.kt b/Prezel/core/data/src/main/java/com/team/prezel/core/data/error/AppErrorExt.kt index eac35399..3a27dee3 100644 --- a/Prezel/core/data/src/main/java/com/team/prezel/core/data/error/AppErrorExt.kt +++ b/Prezel/core/data/src/main/java/com/team/prezel/core/data/error/AppErrorExt.kt @@ -17,6 +17,8 @@ private fun Throwable.toDomainThrowable(): Throwable = when (this) { is CancellationException -> this + is AppException -> this + is ApiException -> AppException( error = errorCode.toDomainError(), @@ -74,7 +76,9 @@ private fun ServerErrorCode.toDomainError(): AppError = ServerErrorCode.INVALID_ID_TOKEN, -> AppError.UNAUTHORIZED - ServerErrorCode.VOICE_RECOGNITION_FAILED -> AppError.VOICE_RECOGNITION_FAILED + ServerErrorCode.VOICE_RECOGNITION_FAILED, + ServerErrorCode.VOICE_RECORDING_EMPTY_OR_SILENT, + -> AppError.VOICE_RECOGNITION_FAILED ServerErrorCode.UNKNOWN -> AppError.UNKNOWN } diff --git a/Prezel/core/network/src/main/java/com/team/prezel/core/network/model/BaseResponse.kt b/Prezel/core/network/src/main/java/com/team/prezel/core/network/model/BaseResponse.kt index 6037d95b..0b518369 100644 --- a/Prezel/core/network/src/main/java/com/team/prezel/core/network/model/BaseResponse.kt +++ b/Prezel/core/network/src/main/java/com/team/prezel/core/network/model/BaseResponse.kt @@ -66,6 +66,7 @@ enum class ServerErrorCode( SENTENCE_NOT_FOUND("S001"), VOICE_RECOGNITION_FAILED("V001"), VOICE_ANALYSIS_FAILED("V002"), + VOICE_RECORDING_EMPTY_OR_SILENT("V003"), UNKNOWN("UNKNOWN"), ; diff --git a/Prezel/feature/practice/impl/src/main/java/com/team/prezel/feature/practice/impl/analysis/component/PracticeAnalysisFailurePage.kt b/Prezel/feature/practice/impl/src/main/java/com/team/prezel/feature/practice/impl/analysis/component/PracticeAnalysisFailurePage.kt index 425a5918..24b3884f 100644 --- a/Prezel/feature/practice/impl/src/main/java/com/team/prezel/feature/practice/impl/analysis/component/PracticeAnalysisFailurePage.kt +++ b/Prezel/feature/practice/impl/src/main/java/com/team/prezel/feature/practice/impl/analysis/component/PracticeAnalysisFailurePage.kt @@ -1,6 +1,7 @@ package com.team.prezel.feature.practice.impl.analysis.component import androidx.annotation.DrawableRes +import androidx.annotation.StringRes import androidx.compose.foundation.Image import androidx.compose.foundation.layout.size import androidx.compose.runtime.Composable @@ -27,8 +28,8 @@ internal fun PracticeAnalysisFailurePage( modifier: Modifier = Modifier, ) { StatusView( - title = stringResource(R.string.feature_practice_impl_practice_recording_analysis_error_title), - description = stringResource(R.string.feature_practice_impl_practice_recording_analysis_error_description), + title = stringResource(errorType.titleResId), + description = stringResource(errorType.descriptionResId), modifier = modifier, visual = { Image( @@ -58,6 +59,21 @@ private val PracticeAnalysisErrorType.drawableResId: Int PracticeAnalysisErrorType.VOICE_RECOGNITION_FAILED -> CoreUiR.drawable.core_ui_error_voice } +private val PracticeAnalysisErrorType.titleResId: Int + @StringRes + get() = when (this) { + PracticeAnalysisErrorType.ANALYSIS_FAILED -> R.string.feature_practice_impl_practice_recording_analysis_failed_title + PracticeAnalysisErrorType.VOICE_RECOGNITION_FAILED -> + R.string.feature_practice_impl_practice_recording_voice_recognition_failed_title + } + +private val PracticeAnalysisErrorType.descriptionResId: Int + @StringRes + get() = when (this) { + PracticeAnalysisErrorType.ANALYSIS_FAILED -> R.string.feature_practice_impl_practice_recording_analysis_failed_description + PracticeAnalysisErrorType.VOICE_RECOGNITION_FAILED -> R.string.feature_practice_impl_practice_recording_voice_recognition_failed_description + } + @BasicPreview @Composable private fun PracticeAnalysisAnalyzeFailurePagePreview() { diff --git a/Prezel/feature/practice/impl/src/main/java/com/team/prezel/feature/practice/impl/analysis/component/PracticeAnalysisResultPage.kt b/Prezel/feature/practice/impl/src/main/java/com/team/prezel/feature/practice/impl/analysis/component/PracticeAnalysisResultPage.kt index 43096664..87ebda97 100644 --- a/Prezel/feature/practice/impl/src/main/java/com/team/prezel/feature/practice/impl/analysis/component/PracticeAnalysisResultPage.kt +++ b/Prezel/feature/practice/impl/src/main/java/com/team/prezel/feature/practice/impl/analysis/component/PracticeAnalysisResultPage.kt @@ -24,6 +24,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import com.team.prezel.core.designsystem.component.actions.area.PrezelButtonArea import com.team.prezel.core.designsystem.component.actions.button.PrezelButton +import com.team.prezel.core.designsystem.component.chip.chip.ChipHierarchy import com.team.prezel.core.designsystem.component.chip.chip.PrezelChip import com.team.prezel.core.designsystem.preview.BasicPreview import com.team.prezel.core.designsystem.theme.PrezelTheme @@ -151,7 +152,10 @@ private fun PracticeAnalysisMetricRow( horizontalArrangement = Arrangement.SpaceBetween, ) { PracticeAnalysisMetricLabel(text = stringResource(R.string.feature_practice_impl_practice_recording_analysis_speed)) - PrezelChip(text = stringResource(speed.labelResId)) + PrezelChip( + text = stringResource(speed.labelResId), + hierarchy = ChipHierarchy.SECONDARY, + ) } } } diff --git a/Prezel/feature/practice/impl/src/main/java/com/team/prezel/feature/practice/impl/analysis/model/PracticeAnalysisErrorType.kt b/Prezel/feature/practice/impl/src/main/java/com/team/prezel/feature/practice/impl/analysis/model/PracticeAnalysisErrorType.kt index 5af82af9..25c7a395 100644 --- a/Prezel/feature/practice/impl/src/main/java/com/team/prezel/feature/practice/impl/analysis/model/PracticeAnalysisErrorType.kt +++ b/Prezel/feature/practice/impl/src/main/java/com/team/prezel/feature/practice/impl/analysis/model/PracticeAnalysisErrorType.kt @@ -1,6 +1,6 @@ package com.team.prezel.feature.practice.impl.analysis.model internal enum class PracticeAnalysisErrorType { - VOICE_RECOGNITION_FAILED, ANALYSIS_FAILED, + VOICE_RECOGNITION_FAILED, } diff --git a/Prezel/feature/practice/impl/src/main/java/com/team/prezel/feature/practice/impl/navigation/PracticeEntryBuilder.kt b/Prezel/feature/practice/impl/src/main/java/com/team/prezel/feature/practice/impl/navigation/PracticeEntryBuilder.kt index 37742951..86bae477 100644 --- a/Prezel/feature/practice/impl/src/main/java/com/team/prezel/feature/practice/impl/navigation/PracticeEntryBuilder.kt +++ b/Prezel/feature/practice/impl/src/main/java/com/team/prezel/feature/practice/impl/navigation/PracticeEntryBuilder.kt @@ -1,5 +1,6 @@ package com.team.prezel.feature.practice.impl.navigation +import androidx.compose.runtime.Composable import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.navigation3.runtime.EntryProviderScope import androidx.navigation3.runtime.NavKey @@ -14,30 +15,30 @@ import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.android.components.ActivityRetainedComponent import dagger.multibindings.IntoSet +import kotlinx.serialization.Serializable internal fun EntryProviderScope.featurePracticeEntryBuilder() { entry { key -> - val navigator = LocalNavigator.current + PracticeRecordingRoute(presentationId = key.presentationId) + } - PracticeRecordingScreen( - onBack = navigator::goBack, - navigateToAnalysis = { recordingFilePath, referenceText -> - navigator.navigate( - PracticeAnalysisNavKey( - presentationId = key.presentationId, - recordingFilePath = recordingFilePath, - referenceText = referenceText, - ), - ) - }, - ) + entry { key -> + PracticeRecordingRoute(presentationId = key.presentationId) } entry { key -> val navigator = LocalNavigator.current PracticeAnalysisScreen( - onRetry = navigator::goBack, + onRetry = { + navigator.navigate( + key = PracticeRecordingRetryNavKey( + presentationId = key.presentationId, + recordingFilePath = key.recordingFilePath, + ), + clearStack = true, + ) + }, onComplete = { navigator.replaceRoot(HomeNavKey) }, viewModel = hiltViewModel { factory -> factory.create( @@ -50,6 +51,30 @@ internal fun EntryProviderScope.featurePracticeEntryBuilder() { } } +@Composable +private fun PracticeRecordingRoute(presentationId: Long) { + val navigator = LocalNavigator.current + + PracticeRecordingScreen( + onBack = navigator::goBack, + navigateToAnalysis = { recordingFilePath, referenceText -> + navigator.navigate( + PracticeAnalysisNavKey( + presentationId = presentationId, + recordingFilePath = recordingFilePath, + referenceText = referenceText, + ), + ) + }, + ) +} + +@Serializable +private data class PracticeRecordingRetryNavKey( + val presentationId: Long, + val recordingFilePath: String, +) : NavKey + @Module @InstallIn(ActivityRetainedComponent::class) object FeaturePracticeModule { diff --git a/Prezel/feature/practice/impl/src/main/res/values/strings.xml b/Prezel/feature/practice/impl/src/main/res/values/strings.xml index 272f590f..8302cf75 100644 --- a/Prezel/feature/practice/impl/src/main/res/values/strings.xml +++ b/Prezel/feature/practice/impl/src/main/res/values/strings.xml @@ -12,8 +12,8 @@ 녹음을 재생하지 못했습니다. 분석중 잠시만 기다려주세요 - 분석에 실패했어요 - 음성이 작거나 주변 소음이 많았을 수 있어요.\n조용한 환경에서 다시 시도해 주세요. + 분석 중 문제가 발생했어요. + 일시적인 오류로 분석을 완료하지 못했어요.\n잠시 후 다시 시도해 주세요. 다시 시도하기 완료 발화 @@ -24,4 +24,6 @@ perfect good try + 분석할 음성을 인식하지 못했어요. + 음성이 작거나 주변 소음이 많았을 수 있어요.\n조용한 환경에서 다시 녹음해 주세요.