Skip to content

Commit

Permalink
Merge pull request #20981 from wordpress-mobile/issue/v2c-update-wave…
Browse files Browse the repository at this point in the history
…form

[Voice to Content] Update waveform
  • Loading branch information
zwarm authored Jun 14, 2024
2 parents dc5473e + 77f2344 commit 29f9541
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 173 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.wordpress.android.ui.voicetocontent

import android.annotation.SuppressLint
import android.app.Dialog
import android.content.Intent
import android.net.Uri
import android.os.Bundle
Expand All @@ -16,7 +18,10 @@ import org.wordpress.android.ui.compose.theme.AppTheme
import org.wordpress.android.R
import org.wordpress.android.util.audio.IAudioRecorder.Companion.REQUIRED_RECORDING_PERMISSIONS
import android.provider.Settings
import android.widget.FrameLayout
import androidx.compose.material.ExperimentalMaterialApi
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import org.wordpress.android.ui.ActivityNavigator
import javax.inject.Inject

Expand Down Expand Up @@ -46,6 +51,25 @@ class VoiceToContentDialogFragment : BottomSheetDialogFragment() {
viewModel.start()
}

@SuppressLint("ClickableViewAccessibility")
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val dialog = super.onCreateDialog(savedInstanceState) as BottomSheetDialog
dialog.setOnShowListener {
val bottomSheet: FrameLayout = dialog.findViewById(
com.google.android.material.R.id.design_bottom_sheet
) ?: return@setOnShowListener

val behavior = BottomSheetBehavior.from(bottomSheet)
behavior.isDraggable = true
behavior.skipCollapsed = true
behavior.state = BottomSheetBehavior.STATE_EXPANDED

// Disable touch interception by the bottom sheet to allow nested scrolling
bottomSheet.setOnTouchListener { _, _ -> false }
}
return dialog
}

private fun observeViewModel() {
viewModel.requestPermission.observe(viewLifecycleOwner) {
requestAllPermissionsForRecording()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.CircularProgressIndicator
import androidx.compose.material.ContentAlpha
import androidx.compose.material.Icon
Expand All @@ -34,7 +36,9 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.rememberNestedScrollInteropConnection
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
Expand All @@ -48,29 +52,42 @@ import androidx.constraintlayout.compose.Dimension
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

@Composable
fun VoiceToContentScreen(
viewModel: VoiceToContentViewModel
) {
val state by viewModel.state.collectAsState()
val amplitudes by viewModel.amplitudes.observeAsState(initial = listOf())
val recordingUpdate by viewModel.recordingUpdate.observeAsState(initial = RecordingUpdate())
val configuration = LocalConfiguration.current
val screenHeight = configuration.screenHeightDp.dp
val bottomSheetHeight = screenHeight * 0.6f // Set to 60% of screen height - but how can it be dynamic?
// Adjust the bottom sheet height based on orientation
val bottomSheetHeight = if (configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) {
screenHeight // Full height in landscape
} else {
screenHeight * 0.6f // 60% height in portrait
}

Surface(
modifier = Modifier
.fillMaxWidth()
.height(bottomSheetHeight),
color = MaterialTheme.colors.surface
) {
VoiceToContentView(state, amplitudes)
Box(
modifier = Modifier
.fillMaxSize()
.nestedScroll(rememberNestedScrollInteropConnection()) // Enable nested scrolling for the bottom sheet
.verticalScroll(rememberScrollState()) // Enable vertical scrolling for the bottom sheet
) {
VoiceToContentView(state, recordingUpdate)
}
}
}

@Composable
fun VoiceToContentView(state: VoiceToContentUiState, amplitudes: List<Float>) {
fun VoiceToContentView(state: VoiceToContentUiState, recordingUpdate: RecordingUpdate) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
Expand All @@ -84,7 +101,7 @@ fun VoiceToContentView(state: VoiceToContentUiState, amplitudes: List<Float>) {
else -> {
Header(state.header)
SecondaryHeader(state.secondaryHeader)
RecordingPanel(state, amplitudes)
RecordingPanel(state, recordingUpdate)
}
}
}
Expand Down Expand Up @@ -167,7 +184,7 @@ fun SecondaryHeader(model: SecondaryHeaderUIModel?) {
}

@Composable
fun RecordingPanel(model: VoiceToContentUiState, amplitudes: List<Float>) {
fun RecordingPanel(model: VoiceToContentUiState, recordingUpdate: RecordingUpdate) {
model.recordingPanel?.let {
Row(
verticalAlignment = Alignment.CenterVertically,
Expand All @@ -189,14 +206,7 @@ fun RecordingPanel(model: VoiceToContentUiState, amplitudes: List<Float>) {
.height(IntrinsicSize.Max)
.padding(48.dp)
) {
WaveformVisualizer(
amplitudes = amplitudes,
modifier = Modifier
.fillMaxWidth()
.height(40.dp)
.padding(16.dp),
color = MaterialTheme.colors.primary
)
ScrollingWaveformVisualizer(recordingUpdate = recordingUpdate)
}
} else if (model.uiStateType == VoiceToContentUIStateType.INELIGIBLE_FOR_FEATURE) {
InEligible(model = it)
Expand Down Expand Up @@ -338,7 +348,7 @@ fun PreviewInitializingView() {
hasPermission = false
)
)
VoiceToContentView(state = state, amplitudes = listOf())
VoiceToContentView(state = state, recordingUpdate = RecordingUpdate())
}
}

Expand All @@ -360,7 +370,7 @@ fun PreviewReadyToRecordView() {
isEligibleForFeature = true
)
)
VoiceToContentView(state = state, amplitudes = listOf())
VoiceToContentView(state = state, recordingUpdate = RecordingUpdate())
}
}

Expand All @@ -381,7 +391,7 @@ fun PreviewNotEligibleToRecordView() {
upgradeUrl = "https://www.wordpress.com"
)
)
VoiceToContentView(state = state, amplitudes = listOf())
VoiceToContentView(state = state, recordingUpdate = RecordingUpdate())
}
}

Expand All @@ -404,18 +414,7 @@ fun PreviewRecordingView() {
isEligibleForFeature = true
)
)
VoiceToContentView(
state = state,
amplitudes = listOf(
1.1f,
2.2f,
3.3f,
4.4f,
2.2f,
3.3f,
1.1f
)
)
VoiceToContentView(state = state, recordingUpdate = RecordingUpdate())
}
}

Expand All @@ -428,6 +427,6 @@ fun PreviewProcessingView() {
uiStateType = VoiceToContentUIStateType.PROCESSING,
header = HeaderUIModel(label = R.string.voice_to_content_processing_label, onClose = { })
)
VoiceToContentView(state = state, amplitudes = listOf())
VoiceToContentView(state = state, recordingUpdate = RecordingUpdate())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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.RecordingUpdate
import org.wordpress.android.viewmodel.ContextProvider
import org.wordpress.android.viewmodel.ScopedViewModel
import java.io.File
Expand All @@ -49,8 +50,8 @@ class VoiceToContentViewModel @Inject constructor(
private val _dismiss = MutableLiveData<Unit>()
val dismiss = _dismiss as LiveData<Unit>

private val _amplitudes = MutableLiveData<List<Float>>()
val amplitudes: LiveData<List<Float>> get() = _amplitudes
private val _recordingUpdate = MutableLiveData<RecordingUpdate>()
val recordingUpdate: LiveData<RecordingUpdate> get() = _recordingUpdate

private val _onIneligibleForVoiceToContent = MutableLiveData<String>()
val onIneligibleForVoiceToContent = _onIneligibleForVoiceToContent as LiveData<String>
Expand Down Expand Up @@ -104,11 +105,8 @@ class VoiceToContentViewModel @Inject constructor(
}

// Recording
// todo: This doesn't work as expected
@Suppress("MagicNumber")
private fun updateAmplitudes(newAmplitudes: List<Float>) {
_amplitudes.value = listOf(1.1f, 2.2f, 4.4f, 3.2f, 1.1f, 2.2f, 1.0f, 3.5f)
Log.d(javaClass.simpleName, "Update amplitudes: $newAmplitudes")
private fun updateRecordingData(recordingUpdate: RecordingUpdate) {
_recordingUpdate.value = recordingUpdate
}

private fun observeRecordingUpdates() {
Expand All @@ -117,7 +115,7 @@ class VoiceToContentViewModel @Inject constructor(
if (update.fileSizeLimitExceeded) {
stopRecording()
} else {
updateAmplitudes(update.amplitudes)
updateRecordingData(update)
// todo: Handle other updates if needed when UI is ready, e.g., elapsed time and file size
Log.d("AudioRecorder", "Recording update: $update")
}
Expand Down
Loading

0 comments on commit 29f9541

Please sign in to comment.