1

I have a text that is refreshed, and every time this happens.

I call a static method method speak() located in another class with this structure:

public class Voc {

    static TextToSpeech mytts;

    public static void speak(String myText){
       mytts=new TextToSpeech(c, new TextToSpeech.OnInitListener() {
        //.........parameters and config not related to issue........
       }
       mytts.speak();
    }

    public static off(){
       mytts.stop();
       mytts.shutdown();
    }

}

The problem is that if I call off() after multiple speak() invocations, the tts continues to speak. This doesn't happens if I invoke speak() only a single time and not multiple time as happens in the refresh method. This raise me the suspect that off() method doesn't work for all instances despite I have assigned all the new TextToSpeech(...) invocations to same static field in the class Voc.

How could I fix this issue?

AndreaF
  • 11,975
  • 27
  • 102
  • 168

1 Answers1

3

Your issue is due to the fact that you instantiate a new TextToSpeech object on each call of speak(), this is not needed. With the updates I provided below, you have a single TTS object that you turn off and back on, not multiple so when you turn it off the single TTS object is stopped and your issue is no longer present.

I have tested this on my phone and it should work exactly as you need.

Please check my updated code below:

Updated Voc Class

import android.content.Context;
import android.os.Build;
import android.speech.tts.TextToSpeech;
import android.util.Log;
import java.util.Locale;

public class Voc {

    static TextToSpeech mytts = null;

    public static void init(Context c){
        mytts=new TextToSpeech(c, new TextToSpeech.OnInitListener() {
            @Override
            public void onInit(int status) {
                if(status == TextToSpeech.SUCCESS){
                    int result=mytts.setLanguage(Locale.US);
                    if(result==TextToSpeech.LANG_NOT_SUPPORTED ||
                            result==TextToSpeech.LANG_MISSING_DATA){
                        Log.d("error", "Language not supported");
                    }
                } else
                    Log.d("error", "TTS failed :(");
            }
        });
    }

    public static void speak(final String myText){  
        if(mytts != null) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                mytts.speak(myText, TextToSpeech.QUEUE_FLUSH, null, null);
            } else {
                //cover all versions...
                mytts.speak(myText, TextToSpeech.QUEUE_FLUSH, null);
            }
        }
    }

   public static void off(){
        if(mytts !=null) {
            mytts.stop();
            //mytts.shutdown();  //calling this here is not what we want
        }
   }

   public static void shutdown(){
        if(mytts !=null) {
            mytts.shutdown();  //if you need call shutdown with this method
        }
   }
}

Main Activity For Test

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Voc.init(getApplicationContext()); //this is the method that sets everything up

        Button onButton = (Button) findViewById(R.id.button_on);
        onButton.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                Voc.speak("blah blah blah blah blah blah blah blah");

            }
        });

        Button offButton = (Button) findViewById(R.id.button_off);
        offButton.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                Voc.off();
            }
        });
    }
}

Example Layout For Test

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.viatechsystems.tts.MainActivity">

<Button
    android:id="@+id/button_on"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Click me for TTS"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintHorizontal_bias="0.292"
    app:layout_constraintVertical_bias="0.498" />

<Button
    android:id="@+id/button_off"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Off"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintLeft_toLeftOf="@+id/button_on"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintHorizontal_bias="0.75"
    app:layout_constraintVertical_bias="0.498" />

</android.support.constraint.ConstraintLayout>
ViaTech
  • 2,143
  • 1
  • 16
  • 51
  • I'm confused since I don't use`new Voc()` in any part of my code but I invoke speak and off as static methods with `Voc.speak()` and `Voc.off()`. – AndreaF Jun 14 '18 at 15:59
  • What I am saying is that your issue appears to be from calling Voc.speak() multiple times where in the original method you provided, you are creating new objects of TextToSpeach, thus calling static Voc.off() can only turn off a single running TextToSpeach object. For example, in your code a new TTS object is being run each time you call Voc.speak(), when you call Voc.off() you are likely only closing the most recently run object. - Perhaps using an instance of Voc is a good idea in this situation, the way I have it should not create overlapping TTS objects – ViaTech Jun 14 '18 at 16:26
  • the singleton in your code is on Voc class, I call speak multiple times but I don't create multiple `Voc` class instances, since I haven't any `new Voc()`. I will try your approach but I expect the same result. – AndreaF Jun 14 '18 at 16:36
  • as expected I get the same issue – AndreaF Jun 14 '18 at 17:10
  • to maximize the the code reuse since I have to call It in a lot of different activities and the configuration block (omitted here) use a lot of adjustments before the initialization. – AndreaF Jun 14 '18 at 19:35
  • Ok I'll wait. Thank you. – AndreaF Jun 14 '18 at 22:22
  • Thanks for the help but unfortunately speak method in your code sample has basically the same structure of my problematic code. – AndreaF Jun 15 '18 at 17:07
  • Then your question is not clear. Have you even setup the activity like I did in my example, it allows for multiple calls to speak(), and none are overlapping, thus when you call off() the TTS is stopped. The way I setup my code is different than your provided code, so I'm not sure how it is possible we have the same structure of speak(), the error was due to overlapping instances of TTS being called to speak(), which I tested and can guarantee that is why your issue was there... Otherwise post real code to work off from because it seems you have a logic error elsewhere if that didn't fix it – ViaTech Jun 15 '18 at 19:15
  • The speak isn't triggered by a button, but the logic is the same. Don't be confident that your sample solve the issue because you have tried It and the error isn't reproduced, the issue doesn't happens always and in all devices, but when happens you will not be able to stop all invocations to speak method calling stop despite these come from the same Activity. So there is something in speak stop (or off and shutdown) implementation that make this code prone to such error. – AndreaF Jun 15 '18 at 19:49
  • I pull back to your question is unclear then. I'm not attempting to be rude....Good luck figuring it out, if you post real code that presents an error I will fix it and give it back as I can test it on my own system, but based on our discussion you needed was a way to call speak multiple times and turn it off, which my answer provides. My code uses a button to turn on TTS, but you can call speak() without the button click and it works the same... I am unclear how you set up your app so without an updated post to your question providing real code, this is not a productive use of my time. Thanks – ViaTech Jun 15 '18 at 20:08
  • The other code isn't relevant for the issue, since, as I have said, It is an activity that does the multiple invocations and is the same activity (not changing its lifecycle between speak and stop) after a bunch of operations calls the stop method. I appreciate your effort to help me, but makes no sense post tons of code that is no way related to TTS problem. The issue is certainly in the way speak\stop method and Android TTS service works. – AndreaF Jun 15 '18 at 21:15