jetpack compose Voice to Text issue

66 Views Asked by At

I I have trying to implement voice to text system in my jetpack compose by watching a tutorial, But i always getting an issue ERROR_NO_MATCH

`
package com.edureminder.voice

import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch

class AppViewModel(private val stt: SpeechToText) : ViewModel() {
    var state by mutableStateOf(AppState())
        private set

    init {
        viewModelScope.launch {
            with(stt) {
                text.collect { result ->
                    send(AppAction.Update(result))
                }
            }
        }
    }


    fun send(action: AppAction) {
        when (action) {
            AppAction.StartRecord -> {
                stt.start()
            }

            AppAction.EndRecord -> {
                stt.stop()
                viewModelScope.launch {
                    delay(3000)
                    state = state.copy(
                        display = ""
                    )
                }
            }
            is AppAction.Update -> {
                state = state.copy(
                    display = state.display + action.text
                )
            }
        }
    }
}

data class AppState(
    val display: String = ""
)

sealed class AppAction {
    object StartRecord : AppAction()
    object EndRecord : AppAction()
    data class Update(val text: String): AppAction()
}
`
package com.edureminder.voice

import android.Manifest
import android.content.pm.PackageManager
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.compose.setContent
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.spring
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Mic
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.scale
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.core.content.ContextCompat
import androidx.core.view.WindowCompat
import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY
import androidx.lifecycle.viewmodel.compose.viewModel
import com.edureminder.voice.ui.theme.VoiceTheme

@Composable
fun VoiceToText() {
    val context = LocalContext.current
    var permission by remember {
        mutableStateOf(
            ContextCompat.checkSelfPermission(
                context,
                Manifest.permission.RECORD_AUDIO
            ) == PackageManager.PERMISSION_GRANTED
        )
    }
    val launcher = rememberLauncherForActivityResult(
        contract = ActivityResultContracts.RequestPermission()
    ) { granted ->
        permission = granted
    }

    val viewModel = viewModel {
        val app = get(APPLICATION_KEY)!!
        val stt = RealSpeechToText(app.applicationContext)
        AppViewModel(stt)
    }

    var pressed by remember { mutableStateOf(false) }
    val buttonScale by animateFloatAsState(
        targetValue = if (pressed) 0.8f else 1f,
        animationSpec = spring(
            stiffness = Spring.StiffnessLow,
            dampingRatio = Spring.DampingRatioMediumBouncy,
            visibilityThreshold = 0.001f
        ),
        label = "Button Scale"
    )

    VoiceTheme {
        if (permission) {
            Column(
                modifier = Modifier
                    .fillMaxSize()
                    .background(color = MaterialTheme.colorScheme.background)
                    .windowInsetsPadding(insets = WindowInsets.navigationBars)
                    .padding(16.dp),
                verticalArrangement = Arrangement.spacedBy(16.dp)
            ) {
                Box(
                    modifier = Modifier
                        .fillMaxWidth()
                        .weight(1f), contentAlignment = Alignment.Center
                ) {
                    Text(
                        text = viewModel.state.display,
                        modifier = Modifier
                            .fillMaxWidth()
                            .wrapContentHeight(),
                        fontSize = 24.sp,
                        color = MaterialTheme.colorScheme.onBackground
                    )
                }
                Row(
                    modifier = Modifier.fillMaxWidth(),
                    horizontalArrangement = Arrangement.Center,
                    verticalAlignment = Alignment.CenterVertically
                ) {
                    Box(
                        modifier = Modifier
                            .scale(buttonScale)
                            .size(80.dp)
                            .background(
                                color = Color.Red,
                                shape = CircleShape
                            )
                            .clip(CircleShape)
                            .pointerInput(Unit) {
                                detectTapGestures(
                                    onPress = {
                                        pressed = true
                                        viewModel.send(AppAction.StartRecord)
                                        awaitRelease()
                                        pressed = false
                                        viewModel.send(AppAction.EndRecord)
                                    }
                                )
                            },
                        contentAlignment = Alignment.Center
                    ) {
                        Icon(
                            modifier = Modifier.size(30.dp),
                            imageVector = Icons.Default.Mic,
                            tint = MaterialTheme.colorScheme.onBackground,
                            contentDescription = "Record"
                        )
                    }
                }
            }
        } else {
            LaunchedEffect(Unit) {
                launcher.launch(Manifest.permission.RECORD_AUDIO)
            }
        }
    }
}

package com.edureminder.voice

import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.speech.RecognitionListener
import android.speech.RecognizerIntent
import android.speech.SpeechRecognizer
import android.util.Log
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import java.util.Locale

interface SpeechToText {
    val text: StateFlow<String>
    fun start()
    fun stop()
}

class RealSpeechToText(context: Context) : SpeechToText {
    override val text = MutableStateFlow("")

    private val speechRecognizer = SpeechRecognizer.createSpeechRecognizer(context).apply {
        setRecognitionListener(object : RecognitionListener {
            override fun onReadyForSpeech(p0: Bundle?) = Unit
            override fun onBeginningOfSpeech() = Unit
            override fun onRmsChanged(p0: Float) = Unit
            override fun onBufferReceived(p0: ByteArray?) = Unit
            override fun onEndOfSpeech() = Unit
            override fun onResults(results: Bundle?) = Unit
            override fun onEvent(p0: Int, p1: Bundle?) = Unit

            override fun onPartialResults(results: Bundle?) {
                val partial = results
                    ?.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION)
                    ?.getOrNull(0) ?: ""

                Log.d("Speech Recognizer", "onPartialResult: $partial")
                text.value = partial
            }

            override fun onError(error: Int) {
                val message = when (error) {
                    SpeechRecognizer.ERROR_AUDIO -> "Audio"
                    SpeechRecognizer.ERROR_CANNOT_CHECK_SUPPORT -> "Cannot Check Support"
                    SpeechRecognizer.ERROR_CLIENT -> "Client"
                    SpeechRecognizer.ERROR_INSUFFICIENT_PERMISSIONS -> "Insufficient Permissions"
                    SpeechRecognizer.ERROR_LANGUAGE_NOT_SUPPORTED -> "Language Not Supported"
                    SpeechRecognizer.ERROR_LANGUAGE_UNAVAILABLE -> "Language Unavailable"
                    SpeechRecognizer.ERROR_NETWORK -> "Network"
                    SpeechRecognizer.ERROR_NETWORK_TIMEOUT -> "Network Timeout"
                    SpeechRecognizer.ERROR_NO_MATCH -> "No Match"
                    SpeechRecognizer.ERROR_RECOGNIZER_BUSY -> "Busy"
                    SpeechRecognizer.ERROR_SERVER -> "Server Error"
                    SpeechRecognizer.ERROR_SERVER_DISCONNECTED -> "Server Disconnected"
                    SpeechRecognizer.ERROR_SPEECH_TIMEOUT -> "Speech Timeout"
                    SpeechRecognizer.ERROR_TOO_MANY_REQUESTS -> "Too Many Requests"
                    else -> "Unknown"
                }
                Log.d("ApplicationLog", "logged - ${SpeechRecognizer.ERROR_NO_MATCH}")
                Log.e("Speech Recognizer", "STT Error: $message")
            }
        })
    }
    private val intent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH).apply {
        putExtra(
            RecognizerIntent.EXTRA_LANGUAGE_MODEL,
            RecognizerIntent.LANGUAGE_MODEL_FREE_FORM
        )
        putExtra(
            RecognizerIntent.EXTRA_LANGUAGE,
            "en"
        )
        putExtra(
            RecognizerIntent.EXTRA_SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS,
            5000
        )
    }


    override fun start() {
        speechRecognizer.startListening(intent)
    }

    override fun stop() {
        speechRecognizer.stopListening()
    }
}

I want to implement the voice to text in jetpack compose modern way without using google API like this and It's shouln't be open a pup while recording my voice image

0

There are 0 best solutions below