Home > Blockchain >  How to Implement Error handling in Text To Speech while working with Recycler View
How to Implement Error handling in Text To Speech while working with Recycler View

Time:11-16

I am passing TextToSpeech from Fragment to my RecyclerView Adapter.
While passing it, I am also sending a flag textToSpeechSupported to confirm whether the currently set device language is supported for TextToSpeech announcement or not.

But every time the value of this flag is being set as false, even though I am setting the value to true in onCreate.
It seems there is an issue with my implementation approach.

But I tired to debug and also added Logs.
Also I did multiple test and tried various other combinations. But every time the flag textToSpeechSupported value is being passed is false, even if the language is supported.
Am I missing something here.

I need the flag textToSpeechSupported value to be true if the device language is supported by TextToSpeech

Please guide.

class WelcomeFragment : Fragment() {

    private lateinit var welcomeAdapter: WelcomeAdapter
    private lateinit var textToSpeech: TextToSpeech
    private var textToSpeechSupported: Boolean = false

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        textToSpeech = TextToSpeech(requireContext()) { status ->
            if (status == SUCCESS) {
                val result = textToSpeech.setLanguage(Locale.getDefault())
                textToSpeechSupported =
                    !(result == TextToSpeech.LANG_NOT_SUPPORTED || result == TextToSpeech.LANG_MISSING_DATA)
            }
        }
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        welcomeAdapter = WelcomeAdapter(textToSpeech, textToSpeechSupported)
        binding.adapter = welcomeAdapter 
        
    }
}
class Welcomedapter(private val textToSpeech: TextToSpeech, private val textToSpeechSupported: Boolean) : ListAdapter<Welcome, ViewHolder>(WelcomeDiffCallback()) {

    //....

    class ViewHolder private constructor(val binding: ContainerWelcomeBinding) : RecyclerView.ViewHolder(binding.root) {
        fun bind(item: Welcome, textToSpeech: TextToSpeech, textToSpeechSupported: Boolean) {
            binding.apply {
                welcomeMessageText.text = item.welcome
                textToSpeechImage.setOnClickListener {
                    if (textToSpeechSupported) {
                        textToSpeech.speak(item.welcome, TextToSpeech.QUEUE_FLUSH, null)
                    } else {
                        // Send an event for Toast saying that language is not supported for Text to Speech
                    }
                }
            }
        }
    }
}

The objective/goal is to ensure that value of flag textToSpeechSupported is correctly calculated and passed to Recycler View Adapter.

CodePudding user response:

The problem is that onViewCreated is being called BEFORE the tts has had time to initialize, so you are accessing textToSpeechSupported too early and you're always getting your default (false) value.

So, instead of calling:

welcomeAdapter = WelcomeAdapter(textToSpeech, textToSpeechSupported)
binding.adapter = welcomeAdapter 

from inside onViewCreated, make a new function:

fun thisFunctionRunsAFTERtheTTSisInitialized() {

    // put that code here instead.

}

And then call that function from inside your onCreate so it will run AFTER the tts has initialized:

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        textToSpeech = TextToSpeech(requireContext()) { status ->
            if (status == SUCCESS) {
                val result = textToSpeech.setLanguage(Locale.getDefault())
                textToSpeechSupported =
                    !(result == TextToSpeech.LANG_NOT_SUPPORTED || result == TextToSpeech.LANG_MISSING_DATA)

                thisFunctionRunsAFTERtheTTSisInitialized() // <---------------

            }
        }
    }

CodePudding user response:

It would be better to pass an interface or function to adapter e.g

class Welcomedapter(private val block : () -> Unit) : ListAdapter<Welcome, ViewHolder>(WelcomeDiffCallback()) {

    //....

    class ViewHolder private constructor(val binding: ContainerWelcomeBinding) : RecyclerView.ViewHolder(binding.root) {
        fun bind(item: Welcome, textToSpeech: TextToSpeech, textToSpeechSupported: Boolean) {
            binding.apply {
                welcomeMessageText.text = item.welcome
                textToSpeechImage.setOnClickListener {
                    block()
                }
            }
        }
    }
}


override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    welcomeAdapter = WelcomeAdapter {
        if (textToSpeechSupported) {
            textToSpeech.speak(item.welcome, TextToSpeech.QUEUE_FLUSH, null)
        } else {
            // show the toast here now 
            
        }
    }
    binding.adapter = welcomeAdapter

}
  • Related