diff --git a/WordPress/src/main/java/org/wordpress/android/ui/voicetocontent/VoiceToContentScreen.kt b/WordPress/src/main/java/org/wordpress/android/ui/voicetocontent/VoiceToContentScreen.kt index 4cba83806d76..1d139dd42969 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/voicetocontent/VoiceToContentScreen.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/voicetocontent/VoiceToContentScreen.kt @@ -53,6 +53,7 @@ import org.wordpress.android.R import org.wordpress.android.ui.compose.components.buttons.Drawable import org.wordpress.android.ui.compose.theme.AppTheme import org.wordpress.android.util.audio.RecordingUpdate +import java.util.Locale @Composable fun VoiceToContentScreen( @@ -100,7 +101,7 @@ fun VoiceToContentView(state: VoiceToContentUiState, recordingUpdate: RecordingU VoiceToContentUIStateType.ERROR -> ErrorView(state) else -> { Header(state.header) - SecondaryHeader(state.secondaryHeader) + SecondaryHeader(state.secondaryHeader, recordingUpdate) RecordingPanel(state, recordingUpdate) } } @@ -159,14 +160,16 @@ fun Header(model: HeaderUIModel) { } @Composable -fun SecondaryHeader(model: SecondaryHeaderUIModel?) { +fun SecondaryHeader(model: SecondaryHeaderUIModel?, recordingUpdate: RecordingUpdate) { model?.let { Row( verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth() ) { - Text(text = stringResource(id = model.label), style = secondaryHeaderStyle) - Spacer(modifier = Modifier.width(8.dp)) // Add space between text and progress + if (model.isLabelVisible) { + Text(text = stringResource(id = model.label), style = secondaryHeaderStyle) + Spacer(modifier = Modifier.width(8.dp)) // Add space between text and progress + } if (model.isProgressIndicatorVisible) { Box( modifier = Modifier.size(20.dp) // size the progress indicator @@ -176,13 +179,47 @@ fun SecondaryHeader(model: SecondaryHeaderUIModel?) { ) } } else { - Text(text = model.requestsAvailable, style = secondaryHeaderStyle) + Text( + text = if (model.isTimeElapsedVisible) + formatTime(recordingUpdate.remainingTimeInSeconds, model.timeMaxDurationInSeconds) + else model.requestsAvailable, + style = secondaryHeaderStyle + ) } Spacer(modifier = Modifier.height(16.dp)) } } } +@Composable +fun formatTime(remainingTimeInSeconds: Int, maxDurationInSeconds: Int): String { + val default = getDefaultTimeString(maxDurationInSeconds) + if (remainingTimeInSeconds == -1) return default + + val minutes = remainingTimeInSeconds / 60 + val seconds = remainingTimeInSeconds % 60 + + val value = if (minutes == 1) default + else String.format(Locale.getDefault(), "%02d:%02d", minutes, seconds) + + return value +} + +@Composable +fun getDefaultTimeString(maxDurationInSeconds: Int): String { + if (maxDurationInSeconds <= 0) { + return "00:00" + } + + // Calculate minutes and seconds + val minutes = (maxDurationInSeconds - 1) / 60 + val seconds = (maxDurationInSeconds - 1) % 60 + + // Format and return the default time string + return String.format(Locale.getDefault(), "%02d:%02d", minutes, seconds) +} + + @Composable fun RecordingPanel(model: VoiceToContentUiState, recordingUpdate: RecordingUpdate) { model.recordingPanel?.let { diff --git a/WordPress/src/main/java/org/wordpress/android/ui/voicetocontent/VoiceToContentUiState.kt b/WordPress/src/main/java/org/wordpress/android/ui/voicetocontent/VoiceToContentUiState.kt index 9fb2ca350065..01d189f89456 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/voicetocontent/VoiceToContentUiState.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/voicetocontent/VoiceToContentUiState.kt @@ -13,7 +13,7 @@ data class SecondaryHeaderUIModel( val isLabelVisible: Boolean = true, val isProgressIndicatorVisible: Boolean = false, val requestsAvailable: String = "0", - val timeElapsed: String = "00:00:00", + val timeMaxDurationInSeconds: Int = 0, val isTimeElapsedVisible: Boolean = false ) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/voicetocontent/VoiceToContentViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/voicetocontent/VoiceToContentViewModel.kt index 9e99790777ae..68fb9d361a32 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/voicetocontent/VoiceToContentViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/voicetocontent/VoiceToContentViewModel.kt @@ -26,6 +26,7 @@ import org.wordpress.android.ui.voicetocontent.VoiceToContentUIStateType.RECORDI import org.wordpress.android.util.audio.IAudioRecorder import org.wordpress.android.util.audio.IAudioRecorder.AudioRecorderResult.Error import org.wordpress.android.util.audio.IAudioRecorder.AudioRecorderResult.Success +import org.wordpress.android.util.audio.RecordingStrategy import org.wordpress.android.util.audio.RecordingUpdate import org.wordpress.android.viewmodel.ContextProvider import org.wordpress.android.viewmodel.ScopedViewModel @@ -136,6 +137,7 @@ class VoiceToContentViewModel @Inject constructor( recordingUseCase.startRecording { audioRecorderResult -> when (audioRecorderResult) { is Success -> { + transitionToProcessing() val file = getRecordingFile(audioRecorderResult.recordingPath) file?.let { executeVoiceToContent(it) @@ -284,8 +286,9 @@ class VoiceToContentViewModel @Inject constructor( uiStateType = RECORDING, header = currentState.header.copy(label = R.string.voice_to_content_recording_label), secondaryHeader = currentState.secondaryHeader?.copy( - timeElapsed = "00:00:00", - isTimeElapsedVisible = true + isTimeElapsedVisible = true, + timeMaxDurationInSeconds = MAX_DURATION, + isLabelVisible = false ), recordingPanel = currentState.recordingPanel?.copy( onStopTap = ::onStopTap, @@ -338,6 +341,7 @@ class VoiceToContentViewModel @Inject constructor( private val NetworkUnavailableMsg = R.string.error_network_connection private val GenericFailureMsg = R.string.voice_to_content_generic_error private const val VOICE_TO_CONTENT = "Voice to content" + private val MAX_DURATION = RecordingStrategy.VoiceToContentRecordingStrategy().maxDuration } } diff --git a/WordPress/src/main/java/org/wordpress/android/util/audio/AudioRecorder.kt b/WordPress/src/main/java/org/wordpress/android/util/audio/AudioRecorder.kt index 12c0a01b2f65..a7a1ecedff16 100644 --- a/WordPress/src/main/java/org/wordpress/android/util/audio/AudioRecorder.kt +++ b/WordPress/src/main/java/org/wordpress/android/util/audio/AudioRecorder.kt @@ -149,11 +149,16 @@ class AudioRecorder( @Suppress("MagicNumber") private fun startRecordingUpdates() { recordingJob = coroutineScope.launch { - var elapsedTimeInSeconds = 0 + val startTime = System.currentTimeMillis() val amplitudeList = mutableListOf() while (recorder != null) { delay(RECORDING_UPDATE_INTERVAL) - elapsedTimeInSeconds += (RECORDING_UPDATE_INTERVAL / 1000).toInt() + // Calculate elapsed time in seconds + val elapsedTimeInSeconds = ((System.currentTimeMillis() - startTime) / 1000).toInt() + + // Calculate remaining time + val remainingTimeInSeconds = recordingStrategy.maxDuration - elapsedTimeInSeconds + val fileSize = File(filePath).length() val amplitude = recorder?.maxAmplitude?.toFloat() ?: 0f amplitudeList.add(amplitude) @@ -162,7 +167,7 @@ class AudioRecorder( amplitudeList.removeAt(0) } _recordingUpdates.value = RecordingUpdate( - elapsedTime = elapsedTimeInSeconds, + remainingTimeInSeconds = remainingTimeInSeconds, fileSize = fileSize, fileSizeLimitExceeded = fileSize >= recordingStrategy.maxFileSize, amplitudes = amplitudeList.toList() @@ -208,7 +213,7 @@ class AudioRecorder( companion object { private const val TAG = "AudioRecorder" - private const val RECORDING_UPDATE_INTERVAL = 100L // in milliseconds + private const val RECORDING_UPDATE_INTERVAL = 75L // in milliseconds private const val RESUME_DELAY = 500L // in milliseconds private const val FILE_SIZE_THRESHOLD = 100000L private const val DURATION_THRESHOLD = 1 diff --git a/WordPress/src/main/java/org/wordpress/android/util/audio/RecordingUpdate.kt b/WordPress/src/main/java/org/wordpress/android/util/audio/RecordingUpdate.kt index f067d6cff8a4..80a91b2b6ffe 100644 --- a/WordPress/src/main/java/org/wordpress/android/util/audio/RecordingUpdate.kt +++ b/WordPress/src/main/java/org/wordpress/android/util/audio/RecordingUpdate.kt @@ -1,7 +1,7 @@ package org.wordpress.android.util.audio data class RecordingUpdate( - val elapsedTime: Int = 0, // in seconds + val remainingTimeInSeconds: Int = -1, val fileSize: Long = 0L, // in bytes val fileSizeLimitExceeded: Boolean = false, val amplitudes: List = emptyList()