diff --git a/app/src/main/kotlin/com/wire/android/datastore/GlobalDataStore.kt b/app/src/main/kotlin/com/wire/android/datastore/GlobalDataStore.kt index e750f20f06..b9fa7463f5 100644 --- a/app/src/main/kotlin/com/wire/android/datastore/GlobalDataStore.kt +++ b/app/src/main/kotlin/com/wire/android/datastore/GlobalDataStore.kt @@ -59,6 +59,8 @@ class GlobalDataStore @Inject constructor(@ApplicationContext private val contex private val APP_LOCK_SOURCE = intPreferencesKey("app_lock_source") val APP_THEME_OPTION = stringPreferencesKey("app_theme_option") + val RECORD_AUDIO_EFFECTS_CHECKBOX = booleanPreferencesKey("record_audio_effects_checkbox") + private val Context.dataStore: DataStore by preferencesDataStore(name = PREFERENCES_NAME) private fun userMigrationStatusKey(userId: String): Preferences.Key = @@ -95,6 +97,13 @@ class GlobalDataStore @Inject constructor(@ApplicationContext private val contex context.dataStore.edit { it[IS_LOGGING_ENABLED] = enabled } } + fun isRecordAudioEffectsCheckboxEnabled(): Flow = + getBooleanPreference(RECORD_AUDIO_EFFECTS_CHECKBOX, false) + + suspend fun setRecordAudioEffectsCheckboxEnabled(enabled: Boolean) { + context.dataStore.edit { it[RECORD_AUDIO_EFFECTS_CHECKBOX] = enabled } + } + fun isEncryptedProteusStorageEnabled(): Flow = getBooleanPreference( IS_ENCRYPTED_PROTEUS_STORAGE_ENABLED, diff --git a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/recordaudio/AudioMediaRecorder.kt b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/recordaudio/AudioMediaRecorder.kt index bdfeec8570..60c498daea 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/recordaudio/AudioMediaRecorder.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/recordaudio/AudioMediaRecorder.kt @@ -49,7 +49,8 @@ class AudioMediaRecorder @Inject constructor( private var mediaRecorder: MediaRecorder? = null - var outputFile: File? = null + var originalOutputFile: File? = null + var effectsOutputFile: File? = null private val _maxFileSizeReached = MutableSharedFlow() fun getMaxFileSizeReached(): Flow = @@ -63,10 +64,14 @@ class AudioMediaRecorder @Inject constructor( MediaRecorder() } - outputFile = kaliumFileSystem + originalOutputFile = kaliumFileSystem .tempFilePath(getRecordingAudioFileName()) .toFile() + effectsOutputFile = kaliumFileSystem + .tempFilePath(getRecordingAudioEffectsFileName()) + .toFile() + mediaRecorder?.setAudioSource(MediaRecorder.AudioSource.MIC) mediaRecorder?.setAudioSamplingRate(SAMPLING_RATE) mediaRecorder?.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4) @@ -74,7 +79,7 @@ class AudioMediaRecorder @Inject constructor( mediaRecorder?.setAudioChannels(AUDIO_CHANNELS) mediaRecorder?.setAudioEncodingBitRate(AUDIO_ENCONDING_BIT_RATE) mediaRecorder?.setMaxFileSize(assetLimitInMegabyte) - mediaRecorder?.setOutputFile(outputFile) + mediaRecorder?.setOutputFile(originalOutputFile) observeAudioFileSize(assetLimitInMegabyte) } @@ -119,6 +124,8 @@ class AudioMediaRecorder @Inject constructor( private companion object { fun getRecordingAudioFileName(): String = "wire-audio-${DateTimeUtil.currentInstant().audioFileDateTime()}.m4a" + fun getRecordingAudioEffectsFileName(): String = + "wire-audio-${DateTimeUtil.currentInstant().audioFileDateTime()}-filter.m4a" const val SIZE_OF_1MB = 1024 * 1024 const val AUDIO_CHANNELS = 1 const val SAMPLING_RATE = 44100 diff --git a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/recordaudio/RecordAudioButtons.kt b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/recordaudio/RecordAudioButtons.kt index 430e0635d3..25494ae29c 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/recordaudio/RecordAudioButtons.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/recordaudio/RecordAudioButtons.kt @@ -23,11 +23,13 @@ import android.view.WindowManager import androidx.annotation.DrawableRes import androidx.annotation.StringRes import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.Checkbox import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text @@ -81,6 +83,8 @@ fun RecordAudioButtonClose( @Composable fun RecordAudioButtonEnabled( + applyAudioFilterState: Boolean, + applyAudioFilterClick: (Boolean) -> Unit, onClick: () -> Unit, modifier: Modifier ) { @@ -91,12 +95,16 @@ fun RecordAudioButtonEnabled( iconResId = R.drawable.ic_microphone_on, contentDescription = R.string.content_description_record_audio_button_start, buttonColor = colorsScheme().recordAudioStartColor, - bottomText = R.string.record_audio_start_label + bottomText = R.string.record_audio_start_label, + applyAudioFilterState = applyAudioFilterState, + applyAudioFilterClick = applyAudioFilterClick, + isAudioFilterEnabled = true ) } @Composable fun RecordAudioButtonRecording( + applyAudioFilterState: Boolean, onClick: () -> Unit, modifier: Modifier ) { @@ -135,12 +143,15 @@ fun RecordAudioButtonRecording( contentDescription = R.string.content_description_record_audio_button_stop, buttonColor = colorsScheme().recordAudioStopColor, bottomText = R.string.record_audio_recording_label, - buttonState = if (seconds > 0) WireButtonState.Default else WireButtonState.Disabled + buttonState = if (seconds > 0) WireButtonState.Default else WireButtonState.Disabled, + applyAudioFilterState = applyAudioFilterState, + applyAudioFilterClick = { } ) } @Composable fun RecordAudioButtonSend( + applyAudioFilterState: Boolean, audioState: AudioState, onClick: () -> Unit, modifier: Modifier, @@ -167,7 +178,9 @@ fun RecordAudioButtonSend( iconResId = R.drawable.ic_send, contentDescription = R.string.content_description_record_audio_button_send, buttonColor = colorsScheme().recordAudioStartColor, - bottomText = R.string.record_audio_send_label + bottomText = R.string.record_audio_send_label, + applyAudioFilterState = applyAudioFilterState, + applyAudioFilterClick = { } ) } @@ -180,7 +193,10 @@ private fun RecordAudioButton( @StringRes contentDescription: Int, buttonColor: Color, @StringRes bottomText: Int, - buttonState: WireButtonState = WireButtonState.Default + buttonState: WireButtonState = WireButtonState.Default, + applyAudioFilterState: Boolean, + applyAudioFilterClick: (Boolean) -> Unit, + isAudioFilterEnabled: Boolean = false ) { Column( modifier = modifier, @@ -213,6 +229,22 @@ private fun RecordAudioButton( text = stringResource(id = bottomText), style = MaterialTheme.wireTypography.body02 ) + + Spacer(modifier = Modifier.height(dimensions().spacing40x)) + Row( + verticalAlignment = Alignment.CenterVertically + ) { + Checkbox( + enabled = isAudioFilterEnabled, + checked = applyAudioFilterState, + onCheckedChange = applyAudioFilterClick + ) + Text( + text = stringResource(id = R.string.record_audio_apply_filter_label), + style = MaterialTheme.wireTypography.body01, + color = if (isAudioFilterEnabled) Color.Unspecified else colorsScheme().checkboxTextDisabled + ) + } } } @@ -228,7 +260,12 @@ fun PreviewRecordAudioButtonClose() { @Composable fun PreviewRecordAudioButtonEnabled() { WireTheme { - RecordAudioButtonEnabled(onClick = {}, modifier = Modifier) + RecordAudioButtonEnabled( + onClick = {}, + modifier = Modifier, + applyAudioFilterState = false, + applyAudioFilterClick = {} + ) } } @@ -236,7 +273,11 @@ fun PreviewRecordAudioButtonEnabled() { @Composable fun PreviewRecordAudioButtonRecording() { WireTheme { - RecordAudioButtonRecording(onClick = {}, modifier = Modifier) + RecordAudioButtonRecording( + onClick = {}, + modifier = Modifier, + applyAudioFilterState = false + ) } } @@ -254,7 +295,8 @@ fun PreviewRecordAudioButtonSend() { modifier = Modifier, outputFile = null, onPlayAudio = {}, - onSliderPositionChange = {} + onSliderPositionChange = {}, + applyAudioFilterState = false ) } } @@ -270,7 +312,9 @@ fun PreviewRecordAudioButton() { iconResId = R.drawable.ic_microphone_on, contentDescription = R.string.content_description_record_audio_button_start, buttonColor = colorsScheme().recordAudioStartColor, - bottomText = R.string.record_audio_start_label + bottomText = R.string.record_audio_start_label, + applyAudioFilterState = false, + applyAudioFilterClick = {} ) } } diff --git a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/recordaudio/RecordAudioComponent.kt b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/recordaudio/RecordAudioComponent.kt index 6db417fd62..ea7f166a5f 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/recordaudio/RecordAudioComponent.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/recordaudio/RecordAudioComponent.kt @@ -70,7 +70,7 @@ fun RecordAudioComponent( // for sending analytics events val observer = LifecycleEventObserver { _, event -> if (event == Lifecycle.Event.ON_STOP && - viewModel.getButtonState() != RecordAudioButtonState.ENABLED + viewModel.state.buttonState != RecordAudioButtonState.ENABLED ) { viewModel.stopRecording() } @@ -102,30 +102,32 @@ fun RecordAudioComponent( val buttonModifier = Modifier .align(Alignment.BottomCenter) - .padding(bottom = dimensions().spacing80x) + .padding(bottom = dimensions().spacing20x) - when (viewModel.getButtonState()) { + when (viewModel.state.buttonState) { RecordAudioButtonState.ENABLED -> RecordAudioButtonEnabled( + applyAudioFilterState = viewModel.state.shouldApplyEffects, + applyAudioFilterClick = viewModel::setShouldApplyEffects, onClick = { recordAudioFlow.launch() }, modifier = buttonModifier ) RecordAudioButtonState.RECORDING -> RecordAudioButtonRecording( + applyAudioFilterState = viewModel.state.shouldApplyEffects, onClick = viewModel::stopRecording, modifier = buttonModifier ) RecordAudioButtonState.READY_TO_SEND -> RecordAudioButtonSend( - audioState = viewModel.getAudioState(), + applyAudioFilterState = viewModel.state.shouldApplyEffects, + audioState = viewModel.state.audioState, onClick = { - viewModel.sendRecording( - onAudioRecorded = onAudioRecorded, - ) { + viewModel.sendRecording(onAudioRecorded = onAudioRecorded) { onCloseRecordAudio() } }, modifier = buttonModifier, - outputFile = viewModel.getOutputFile(), + outputFile = viewModel.getPlayableAudioFile(), onPlayAudio = viewModel::onPlayAudio, onSliderPositionChange = viewModel::onSliderPositionChange ) @@ -133,13 +135,13 @@ fun RecordAudioComponent( } DiscardRecordedAudioDialog( - dialogState = viewModel.getDiscardDialogState(), + dialogState = viewModel.state.discardDialogState, onDismiss = viewModel::onDismissDiscardDialog, onDiscard = { viewModel.discardRecording(onCloseRecordAudio) } ) MicrophonePermissionsDeniedDialog( - dialogState = viewModel.getPermissionsDeniedDialogState(), + dialogState = viewModel.state.permissionsDeniedDialogState, onDismiss = viewModel::onDismissPermissionsDeniedDialog, onOpenSettings = { context.openAppInfoScreen() @@ -147,7 +149,7 @@ fun RecordAudioComponent( ) RecordedAudioMaxFileSizeReachedDialog( - dialogState = viewModel.getMaxFileSizeReachedDialogState(), + dialogState = viewModel.state.maxFileSizeReachedDialogState, onDismiss = viewModel::onDismissMaxFileSizeReachedDialog ) } diff --git a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/recordaudio/RecordAudioState.kt b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/recordaudio/RecordAudioState.kt index 667a230906..7d60ee2c4e 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/recordaudio/RecordAudioState.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/recordaudio/RecordAudioState.kt @@ -25,7 +25,9 @@ data class RecordAudioState( val discardDialogState: RecordAudioDialogState = RecordAudioDialogState.Hidden, val permissionsDeniedDialogState: RecordAudioDialogState = RecordAudioDialogState.Hidden, val maxFileSizeReachedDialogState: RecordAudioDialogState = RecordAudioDialogState.Hidden, - val outputFile: File? = null, + val originalOutputFile: File? = null, + val effectsOutputFile: File? = null, + val shouldApplyEffects: Boolean = false, val audioState: AudioState = AudioState.DEFAULT ) diff --git a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/recordaudio/RecordAudioViewModel.kt b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/recordaudio/RecordAudioViewModel.kt index bf31b06a9a..8afa74077c 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/recordaudio/RecordAudioViewModel.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/recordaudio/RecordAudioViewModel.kt @@ -17,12 +17,16 @@ */ package com.wire.android.ui.home.messagecomposer.recordaudio +import android.content.Context import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.core.net.toUri import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.waz.audioeffect.AudioEffect +import com.wire.android.appLogger +import com.wire.android.datastore.GlobalDataStore import com.wire.android.media.audiomessage.AudioState import com.wire.android.media.audiomessage.RecordAudioMessagePlayer import com.wire.android.ui.home.conversations.model.UriAsset @@ -34,49 +38,49 @@ import com.wire.android.util.ui.UIText import com.wire.kalium.logic.feature.asset.GetAssetSizeLimitUseCase import com.wire.kalium.logic.feature.call.usecase.ObserveEstablishedCallsUseCase import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.launch import okio.Path.Companion.toPath import java.io.File +import java.io.IOException import javax.inject.Inject import kotlin.io.path.deleteIfExists @Suppress("TooManyFunctions") @HiltViewModel class RecordAudioViewModel @Inject constructor( + @ApplicationContext private val context: Context, private val recordAudioMessagePlayer: RecordAudioMessagePlayer, private val observeEstablishedCalls: ObserveEstablishedCallsUseCase, private val getAssetSizeLimit: GetAssetSizeLimitUseCase, private val currentScreenManager: CurrentScreenManager, - private val audioMediaRecorder: AudioMediaRecorder + private val audioMediaRecorder: AudioMediaRecorder, + private val globalDataStore: GlobalDataStore ) : ViewModel() { - private var state: RecordAudioState by mutableStateOf(RecordAudioState()) + var state: RecordAudioState by mutableStateOf(RecordAudioState()) + private set private var hasOngoingCall: Boolean = false private val infoMessage = MutableSharedFlow() - fun getButtonState(): RecordAudioButtonState = state.buttonState - - fun getDiscardDialogState(): RecordAudioDialogState = state.discardDialogState - - fun getPermissionsDeniedDialogState(): RecordAudioDialogState = - state.permissionsDeniedDialogState - - fun getMaxFileSizeReachedDialogState(): RecordAudioDialogState = - state.maxFileSizeReachedDialogState - - fun getOutputFile(): File? = state.outputFile - - fun getAudioState(): AudioState = state.audioState + private val tag = "RecordAudioViewModel" fun getInfoMessage(): SharedFlow = infoMessage.asSharedFlow() + fun getPlayableAudioFile(): File? = if (state.shouldApplyEffects) { + state.effectsOutputFile + } else { + state.originalOutputFile + } + init { observeAudioPlayerState() + observeEffectsCheckbox() viewModelScope.launch { launch { @@ -126,6 +130,14 @@ class RecordAudioViewModel @Inject constructor( } } + private fun observeEffectsCheckbox() { + viewModelScope.launch { + globalDataStore.isRecordAudioEffectsCheckboxEnabled().collect { + state = state.copy(shouldApplyEffects = it) + } + } + } + fun startRecording() { if (hasOngoingCall) { viewModelScope.launch { @@ -137,7 +149,8 @@ class RecordAudioViewModel @Inject constructor( audioMediaRecorder.setUp(assetSizeLimit) if (audioMediaRecorder.startRecording()) { state = state.copy( - outputFile = audioMediaRecorder.outputFile, + originalOutputFile = audioMediaRecorder.originalOutputFile, + effectsOutputFile = audioMediaRecorder.effectsOutputFile, buttonState = RecordAudioButtonState.RECORDING ) } else { @@ -150,12 +163,31 @@ class RecordAudioViewModel @Inject constructor( fun stopRecording() { if (state.buttonState == RecordAudioButtonState.RECORDING) { audioMediaRecorder.stop() + } + audioMediaRecorder.release() + + if (state.originalOutputFile != null && state.effectsOutputFile != null) { + if (state.shouldApplyEffects) { + val result = AudioEffect(context) + .applyEffectM4A( + state.originalOutputFile!!.path, + state.effectsOutputFile!!.path, + AudioEffect.AVS_AUDIO_EFFECT_VOCODER_MED, + true + ) + + if (result > -1) { + appLogger.i("[$tag] -> Audio file with effects generated successfully.") + } else { + appLogger.w("[$tag] -> There was an issue with generating audio file with effects.") + } + } state = state.copy( buttonState = RecordAudioButtonState.READY_TO_SEND, audioState = AudioState.DEFAULT.copy( totalTimeInMs = AudioState.TotalTimeInMs.Known( - state.outputFile?.let { + getPlayableAudioFile()?.let { getAudioLengthInMs( dataPath = it.path.toPath(), mimeType = AUDIO_MIME_TYPE @@ -165,7 +197,6 @@ class RecordAudioViewModel @Inject constructor( ) ) } - audioMediaRecorder.release() } fun showDiscardRecordingDialog(onCloseRecordAudio: () -> Unit) { @@ -206,13 +237,15 @@ class RecordAudioViewModel @Inject constructor( fun discardRecording(onCloseRecordAudio: () -> Unit) { viewModelScope.launch { - state.outputFile?.toPath()?.deleteIfExists() + state.originalOutputFile?.toPath()?.deleteIfExists() + state.effectsOutputFile?.toPath()?.deleteIfExists() recordAudioMessagePlayer.stop() recordAudioMessagePlayer.close() state = state.copy( buttonState = RecordAudioButtonState.ENABLED, discardDialogState = RecordAudioDialogState.Hidden, - outputFile = null + originalOutputFile = null, + effectsOutputFile = null ) onCloseRecordAudio() } @@ -225,21 +258,35 @@ class RecordAudioViewModel @Inject constructor( viewModelScope.launch { recordAudioMessagePlayer.stop() recordAudioMessagePlayer.close() - state.outputFile?.let { - onAudioRecorded( - UriAsset( - uri = it.toUri(), - saveToDeviceIfInvalid = false - ) - ) - onComplete() - // TODO(RecordAudio): Question: Should we remove the file here as well? + + val resultFile = if (state.shouldApplyEffects) { + try { + state.originalOutputFile?.toPath()?.deleteIfExists() + } catch (exception: IOException) { + appLogger.e("[$tag] -> Couldn't delete original audio file before sending audio file with effects.") + } + state.effectsOutputFile!!.toUri() + } else { + try { + state.effectsOutputFile?.toPath()?.deleteIfExists() + } catch (exception: IOException) { + appLogger.e("[$tag] -> Couldn't delete audio file with effects before sending original audio file.") + } + state.originalOutputFile!!.toUri() } + + onAudioRecorded( + UriAsset( + uri = resultFile, + saveToDeviceIfInvalid = false + ) + ) + onComplete() } } fun onPlayAudio() { - state.outputFile?.let { audioFile -> + getPlayableAudioFile()?.let { audioFile -> viewModelScope.launch { recordAudioMessagePlayer.playAudio( audioFile = audioFile @@ -256,6 +303,15 @@ class RecordAudioViewModel @Inject constructor( } } + fun setShouldApplyEffects(enabled: Boolean) { + viewModelScope.launch { + globalDataStore.setRecordAudioEffectsCheckboxEnabled(enabled) + } + state = state.copy( + shouldApplyEffects = enabled + ) + } + override fun onCleared() { super.onCleared() recordAudioMessagePlayer.close() diff --git a/app/src/main/kotlin/com/wire/android/ui/theme/WireColorScheme.kt b/app/src/main/kotlin/com/wire/android/ui/theme/WireColorScheme.kt index f6ed2177ec..8049ebbd57 100644 --- a/app/src/main/kotlin/com/wire/android/ui/theme/WireColorScheme.kt +++ b/app/src/main/kotlin/com/wire/android/ui/theme/WireColorScheme.kt @@ -103,6 +103,7 @@ data class WireColorScheme( val validE2eiStatusColor: Color, val mlsVerificationTextColor: Color, val wireAccentColors: WireAccentColors, + val checkboxTextDisabled: Color ) { fun toColorScheme(): ColorScheme = ColorScheme( primary = primary, @@ -247,7 +248,8 @@ private val LightWireColorScheme = WireColorScheme( Accent.Petrol -> WireColorPalette.LightPetrol500 Accent.Unknown -> WireColorPalette.LightBlue500 } - } + }, + checkboxTextDisabled = WireColorPalette.Gray70 ) // Dark WireColorScheme @@ -366,7 +368,8 @@ private val DarkWireColorScheme = WireColorScheme( Accent.Petrol -> WireColorPalette.DarkPetrol500 Accent.Unknown -> WireColorPalette.DarkBlue500 } - } + }, + checkboxTextDisabled = WireColorPalette.Gray70 ) @PackagePrivate diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a13b887560..43538449ae 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1286,6 +1286,7 @@ Start Recording Recording Audio… Send Audio Message + Apply audio filter Discard Audio Message? The audio message will be deleted and can not be sent. Discard diff --git a/app/src/test/kotlin/com/wire/android/ui/home/messagecomposer/recordaudio/RecordAudioViewModelTest.kt b/app/src/test/kotlin/com/wire/android/ui/home/messagecomposer/recordaudio/RecordAudioViewModelTest.kt index 32c41cde47..ef35036ec5 100644 --- a/app/src/test/kotlin/com/wire/android/ui/home/messagecomposer/recordaudio/RecordAudioViewModelTest.kt +++ b/app/src/test/kotlin/com/wire/android/ui/home/messagecomposer/recordaudio/RecordAudioViewModelTest.kt @@ -17,8 +17,10 @@ */ package com.wire.android.ui.home.messagecomposer.recordaudio +import android.content.Context import app.cash.turbine.test import com.wire.android.config.CoroutineTestExtension +import com.wire.android.datastore.GlobalDataStore import com.wire.android.framework.FakeKaliumFileSystem import com.wire.android.media.audiomessage.AudioState import com.wire.android.media.audiomessage.RecordAudioMessagePlayer @@ -82,7 +84,7 @@ class RecordAudioViewModelTest { // then assertEquals( RecordAudioButtonState.RECORDING, - viewModel.getButtonState() + viewModel.state.buttonState ) coVerify(exactly = 1) { arrangement.getAssetSizeLimit(false) } verify(exactly = 1) { arrangement.audioMediaRecorder.setUp(ASSET_SIZE_LIMIT) } @@ -105,7 +107,7 @@ class RecordAudioViewModelTest { // then assertEquals( RecordAudioButtonState.READY_TO_SEND, - viewModel.getButtonState() + viewModel.state.buttonState ) } @@ -121,7 +123,7 @@ class RecordAudioViewModelTest { // then assertEquals( RecordAudioDialogState.Hidden, - viewModel.getDiscardDialogState() + viewModel.state.discardDialogState ) } } @@ -140,7 +142,7 @@ class RecordAudioViewModelTest { // then assertEquals( RecordAudioDialogState.Shown, - viewModel.getDiscardDialogState() + viewModel.state.discardDialogState ) } } @@ -158,7 +160,7 @@ class RecordAudioViewModelTest { // then assertEquals( RecordAudioDialogState.Hidden, - viewModel.getDiscardDialogState() + viewModel.state.discardDialogState ) } @@ -175,7 +177,7 @@ class RecordAudioViewModelTest { // then assertEquals( RecordAudioDialogState.Shown, - viewModel.getPermissionsDeniedDialogState() + viewModel.state.permissionsDeniedDialogState ) } @@ -192,7 +194,7 @@ class RecordAudioViewModelTest { // then assertEquals( RecordAudioDialogState.Hidden, - viewModel.getPermissionsDeniedDialogState() + viewModel.state.permissionsDeniedDialogState ) } @@ -211,15 +213,15 @@ class RecordAudioViewModelTest { // then assertEquals( RecordAudioButtonState.ENABLED, - viewModel.getButtonState() + viewModel.state.buttonState ) assertEquals( RecordAudioDialogState.Hidden, - viewModel.getDiscardDialogState() + viewModel.state.discardDialogState ) assertEquals( null, - viewModel.getOutputFile() + viewModel.state.originalOutputFile ) } } @@ -236,7 +238,7 @@ class RecordAudioViewModelTest { // when viewModel.startRecording() // then - assertEquals(RecordAudioButtonState.RECORDING, viewModel.getButtonState()) + assertEquals(RecordAudioButtonState.RECORDING, viewModel.state.buttonState) expectNoEvents() } } @@ -253,7 +255,7 @@ class RecordAudioViewModelTest { // when viewModel.startRecording() // then - assertEquals(RecordAudioButtonState.ENABLED, viewModel.getButtonState()) + assertEquals(RecordAudioButtonState.ENABLED, viewModel.state.buttonState) assertEquals(RecordAudioInfoMessageType.UnableToRecordAudioError.uiText, awaitItem()) } } @@ -265,14 +267,18 @@ class RecordAudioViewModelTest { val observeEstablishedCalls = mockk() val currentScreenManager = mockk() val getAssetSizeLimit = mockk() + val globalDataStore = mockk() + val context = mockk() val viewModel by lazy { RecordAudioViewModel( + context = context, recordAudioMessagePlayer = recordAudioMessagePlayer, observeEstablishedCalls = observeEstablishedCalls, currentScreenManager = currentScreenManager, audioMediaRecorder = audioMediaRecorder, getAssetSizeLimit = getAssetSizeLimit, + globalDataStore = globalDataStore ) } @@ -286,9 +292,13 @@ class RecordAudioViewModelTest { every { audioMediaRecorder.startRecording() } returns true every { audioMediaRecorder.stop() } returns Unit every { audioMediaRecorder.release() } returns Unit - every { audioMediaRecorder.outputFile } returns fakeKaliumFileSystem + every { globalDataStore.isRecordAudioEffectsCheckboxEnabled() } returns flowOf(false) + every { audioMediaRecorder.originalOutputFile } returns fakeKaliumFileSystem .tempFilePath("temp_recording.mp3") .toFile() + every { audioMediaRecorder.effectsOutputFile } returns fakeKaliumFileSystem + .tempFilePath("temp_recording_effects.mp3") + .toFile() coEvery { audioMediaRecorder.getMaxFileSizeReached() } returns flowOf( RecordAudioDialogState.MaxFileSizeReached( maxSize = GetAssetSizeLimitUseCaseImpl.ASSET_SIZE_DEFAULT_LIMIT_BYTES diff --git a/kalium b/kalium index 99c099c4cb..409e685fde 160000 --- a/kalium +++ b/kalium @@ -1 +1 @@ -Subproject commit 99c099c4cb4b339130f4139aca1f59d8f91ca792 +Subproject commit 409e685fdeb6f3e55c20bc0df1e82522ec53c659