Before Compose I had an Android TTS manager that tracked the progress of large amounts of text. And I used a SeekBar
to mark the reading progress. Now I want to change the SeekBar for a Slider
to mark the reading progress, but I can’t get it to work. I don’t know how to connect the TTS reading progress with a Slider.
This is the code of my TTS Manager;
class TtsManagerCompose(
context: Context,
text: String,
splitRegex: String,
private val mProgressListener: (Int, Int) -> Unit
) : OnInitListener {
private val mTexts: Array<String>
private val mTts: TextToSpeech
private var mTextProgress = 0
private var mIsPlaying = false
init {
mTexts = text.split(splitRegex.toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
mTts = TextToSpeech(context, this)
mTts.setOnUtteranceProgressListener(object : UtteranceProgressListener() {
override fun onDone(utteranceId: String) {
if (!mIsPlaying || mTextProgress == mTexts.size) return
++mTextProgress
updateProgress(mTextProgress, mTexts.size)
speakText()
}
override fun onStart(utteranceId: String) {}
@Deprecated("Deprecated in Java")
override fun onError(utteranceId: String) {
//onError(utteranceId);
}
})
}
private fun speakText() {
if (mTextProgress >= mTexts.size) return
val bundle = Bundle()
bundle.putInt(TextToSpeech.Engine.KEY_PARAM_STREAM, AudioManager.STREAM_MUSIC)
mTts.speak(mTexts[mTextProgress], TextToSpeech.QUEUE_FLUSH, bundle, "TTS_ID")
}
fun start() {
mIsPlaying = true
speakText()
}
fun pause() {
mIsPlaying = false
mTts.stop()
updateProgress(mTextProgress, mTexts.size)
}
fun resume() {
mIsPlaying = false
mTts.stop()
start()
updateProgress(mTextProgress, mTexts.size)
}
fun stop() {
mIsPlaying = false
mTts.stop()
mTextProgress = 0
updateProgress(mTextProgress, mTexts.size)
}
private fun updateProgress(current: Int, max: Int) {
mProgressListener.invoke(current, max)
}
fun changeProgress(progress: Int) {
mTextProgress = progress
if (!mIsPlaying) return
pause()
start()
}
override fun onInit(status: Int) {
if (status == TextToSpeech.SUCCESS) {
val locSpanish = Locale("spa", "ESP")
val result = mTts.setLanguage(locSpanish)
if (result == TextToSpeech.LANG_MISSING_DATA || result == TextToSpeech.LANG_NOT_SUPPORTED) {
return
}
changeProgress(1)
}
}
fun close() {
mTts.stop()
mTts.shutdown()
}
}
This is the code for the screen:
private var mTtsManager: TtsManagerCompose? = null
@ExperimentalMaterial3Api
@Composable
fun TextToSpeechScreen(text:String,) {
var readText by remember { mutableStateOf(false) }
Column(modifier = Modifier.padding(24.dp)) {
isSpeaking = false
SliderMinimalExample()
if(readText){
readText(text = text)
}
Button(onClick = {
readText=true
}) {
Image(
painterResource(id = R.drawable.ic_play),
contentDescription ="Play",
modifier = Modifier.size(20.dp))
}
Button(onClick = {
mTtsManager?.stop()
readText=false
}) {
Image(
painterResource(id = R.drawable.ic_baseline_stop_circle_24),
contentDescription ="Stop",
modifier = Modifier.size(20.dp)) }
}
//TODO: Buttons for Pause / Resume ...
}
And this is the code for read the text:
@Composable
private fun readText(text:String) {
mTtsManager = TtsManagerCompose(
LocalContext.current, text,
Constants.SEPARADOR,
) { current: Int, max: Int ->
// seekBar!!.progress = current
// seekBar!!.max = max
}
mTtsManager!!.start()
}