I have an application using TTS very heavily. It's working fine, but I need to tweak it.
I am using a TTS object in every screen and this is not good. I wish I could creat the TTS object just once (like a Singleton) and them, use it throughout all my activities.
Here is the base code for this to work:
public class SimOuNaoActivity extends Activity implements OnInitListener{
public TextToSpeech tts;
private int MY_DATA_CHECK_CODE = 0;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent checkIntent = new Intent();
checkIntent.setAction(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA);
startActivityForResult(checkIntent, MY_DATA_CHECK_CODE);
tts.speak("Testing 1,2,3", TextToSpeech.QUEUE_ADD, null);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == MY_DATA_CHECK_CODE) {
if (resultCode == TextToSpeech.Engine.CHECK_VOICE_DATA_PASS) {
tts = new TextToSpeech(this, this);
} else {
// missing data, install it
Intent installIntent = new Intent();
installIntent
.setAction(TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA);
startActivity(installIntent);
}
}
}
#Override
public void onInit(int status) {
if (status == TextToSpeech.SUCCESS) {
// Toast.LENGTH_LONG).show();
} else if (status == TextToSpeech.ERROR) {
// Toast.LENGTH_LONG).show();
}
}
#Override
public void onDestroy() {
if (tts != null) {
tts.stop();
tts.shutdown();
}
super.onDestroy();
System.gc();
}
}
What is the correct approach to have the TTS object available in all my activities? Have in mind that it uses some methods like startActivityForResult, etc... so... I would like to know what I can do to make this work fine.
Can anyone help me, please?
Any help is appreciatted!
Thanks!
This is relatively simple - Just implement it as follows and do not forget to pass as context, the application context (this.getApplicationContext()) and not the activity context.
public class SingletonTTS {
private static SingletonTTS instance;
private static Context ctx;
private TextToSpeech mTTS;
private static boolean TTSready = false;
private SingletonTTS(Context context) {
ctx = context;
mTTS = new TextToSpeech(context, new TextToSpeech.OnInitListener() {
#Override
public void onInit(int i) {
TTSready = true;
configTTS();
}
});
}
public static synchronized SingletonTTS getInstance(Context context) {
if (instance == null) {
instance = new SingletonTTS(context);
}
return instance;
}
public static boolean isTTSready(){
return TTSready;
}
private void configTTS() {
Toast.makeText(ctx, "supports " + mTTS.isLanguageAvailable(Locale.getDefault()), Toast.LENGTH_LONG).show();
int available = mTTS.isLanguageAvailable(Locale.getDefault());
if( available != TextToSpeech.LANG_MISSING_DATA
&& available != TextToSpeech.LANG_NOT_SUPPORTED ){
mTTS.setLanguage(new Locale(Locale.getDefault().getLanguage()) );
} else {
/** TODO SAVE */
}
}
public void speakSentence(String sentence){
mTTS.speak(sentence, TextToSpeech.QUEUE_ADD, null);
}
}
Have a look here to share stuff between activities and program with nicer design in android :
Intent.putExtras size limit?
You'll likely want to instantiate and hold your Singleton object in the Application, which can only be instantiated once anyway, so just put your TTS initialisation code in a class of its own and instantiate that class as an object in your Application. You'll need to pass the Application to your TTS class as the Context that TTS will be instantiated with.
public class MyApplication extends Application {
public MyTTS myTTS;
public void onCreate() {
myTTS = new MyTTS(this);
}
}
then you can use getApplication().myTTS.whateverMethodsYouMake(yadayada) from within Activities to access the Application-Singleton-ified TTS class.
Related
I am working on application, where in am getting a tasks from server and then user evaluate through Mobile Application(just like a quiz Application).
During Evaluation of Tasks, if the user presses home button then app goes to background. And when user back to application from recent background applications, then Application started from Splash screen.
I am confused that what price of code should I add, so that when user back to Application, then previous state must be shown to the user??
copy this class further i will tell you what to do
public class Foreground implements Application.ActivityLifecycleCallbacks {
public static final long CHECK_DELAY = 50;
public static final String TAG = Foreground.class.getName();
public interface Listener {
public void onBecameForeground();
public void onBecameBackground();
}
private static Foreground instance;
private boolean foreground = false, paused = true;
private Handler handler = new Handler();
private List<Listener> listeners = new CopyOnWriteArrayList<Listener>();
private Runnable check;
/**
* Its not strictly necessary to use this method - _usually_ invoking
* get with a Context gives us a path to retrieve the Application and
* initialise, but sometimes (e.g. in test harness) the ApplicationContext
* is != the Application, and the docs make no guarantees.
*
* #param application
* #return an initialised Foreground instance
*/
public static Foreground init(Application application){
if (instance == null) {
instance = new Foreground();
application.registerActivityLifecycleCallbacks(instance);
}
return instance;
}
public static Foreground get(Application application){
if (instance == null) {
init(application);
}
return instance;
}
public static Foreground get(Context ctx){
if (instance == null) {
Context appCtx = ctx.getApplicationContext();
if (appCtx instanceof Application) {
init((Application)appCtx);
}
throw new IllegalStateException(
"Foreground is not initialised and " +
"cannot obtain the Application object");
}
return instance;
}
public static Foreground get(){
if (instance == null) {
throw new IllegalStateException(
"Foreground is not initialised - invoke " +
"at least once with parameterised init/get");
}
return instance;
}
public boolean isForeground(){
return foreground;
}
public boolean isBackground(){
return !foreground;
}
public void addListener(Listener listener){
listeners.add(listener);
}
public void removeListener(Listener listener){
listeners.remove(listener);
}
#Override
public void onActivityResumed(Activity activity) {
paused = false;
boolean wasBackground = !foreground;
foreground = true;
if (check != null)
handler.removeCallbacks(check);
if (wasBackground){
Log.i(TAG, "went foreground");
for (Listener l : listeners) {
try {
l.onBecameForeground();
} catch (Exception exc) {
Log.e(TAG, "Listener threw exception!", exc);
}
}
} else {
Log.i(TAG, "still foreground");
}
}
#Override
public void onActivityPaused(Activity activity) {
paused = true;
if (check != null)
handler.removeCallbacks(check);
handler.postDelayed(check = new Runnable(){
#Override
public void run() {
if (foreground && paused) {
foreground = false;
Log.i(TAG, "went background");
for (Listener l : listeners) {
try {
l.onBecameBackground();
} catch (Exception exc) {
Log.e(TAG, "Listener threw exception!", exc);
}
}
} else {
Log.i(TAG, "still foreground");
}
}
}, CHECK_DELAY);
}
#Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {}
#Override
public void onActivityStarted(Activity activity) {}
#Override
public void onActivityStopped(Activity activity) {}
#Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {}
#Override
public void onActivityDestroyed(Activity activity) {}
}
add this in onCreate of year Application Class
Foreground foreground = Foreground.init(this);
final Foreground.Listener myListener = new Foreground.Listener()
{
public void onBecameForeground()
{
Log.d("TAG", "FOREGROUND");
}
public void onBecameBackground()
{
//registerActivityLifecycleCallbacks(new MyLifecycleHandler());
Intent i = new Intent("android.intent.action.MAIN").putExtra("some_msg", "I will be sent!");
sendBroadcast(i);
}
};
foreground.addListener(myListener);
add this code in onCreate of your Base Activity ok ?
IntentFilter intentFilter = new IntentFilter(
"android.intent.action.MAIN");
mReceiver = new BroadcastReceiver()
{
#Override
public void onReceive(Context context, Intent intent)
{
//extract our message from intent
String msg_for_me = intent.getStringExtra("some_msg");
//log our message value
Log.i("InchooTutorial", msg_for_me);
finish();
}
};
//registering our receiver
this.registerReceiver(mReceiver, intentFilter);
not this this is your override onDestroy method copy outside the oncreate
#Override
protected void onDestroy()
{
super.onDestroy();
unregisterReceiver(mReceiver);
}
Overide the methods onStop(), onPause(), onResume() in your main activity.
I'm trying to write an app that will launch, or show a notification or a popup when another specific app goes to the background.
For example:
User launches app A
User uses app A
User puts app A in the background, either by pressing the home button or back button or launching another app
My app detects that and launches itself or shows a popup or whatever
Is there a way to do this and if there is, without killing the battery?
In your app A put the below code in a class:
public class Foreground implements Application.ActivityLifecycleCallbacks {
public static final long CHECK_DELAY = 500;
public static final String TAG = Foreground.class.getName();
public interface Listener {
public void onBecameForeground();
public void onBecameBackground();
}
private static Foreground instance;
private boolean foreground = false, paused = true;
private Handler handler = new Handler();
private List<Listener> listeners = new CopyOnWriteArrayList<Listener>();
private Runnable check;
/**
* Its not strictly necessary to use this method - _usually_ invoking
* get with a Context gives us a path to retrieve the Application and
* initialise, but sometimes (e.g. in test harness) the ApplicationContext
* is != the Application, and the docs make no guarantees.
*
* #param application
* #return an initialised Foreground instance
*/
public static Foreground init(Application application) {
if (instance == null) {
instance = new Foreground();
application.registerActivityLifecycleCallbacks(instance);
}
return instance;
}
public static Foreground get(Application application) {
if (instance == null) {
init(application);
}
return instance;
}
public static Foreground get(Context ctx) {
if (instance == null) {
Context appCtx = ctx.getApplicationContext();
if (appCtx instanceof Application) {
init((Application) appCtx);
}
throw new IllegalStateException(
"Foreground is not initialised and " +
"cannot obtain the Application object");
}
return instance;
}
public static Foreground get() {
if (instance == null) {
throw new IllegalStateException(
"Foreground is not initialised - invoke " +
"at least once with parameterised init/get");
}
return instance;
}
public boolean isForeground() {
return foreground;
}
public boolean isBackground() {
return !foreground;
}
public void addListener(Listener listener) {
listeners.add(listener);
}
public void removeListener(Listener listener) {
listeners.remove(listener);
}
#Override
public void onActivityResumed(Activity activity) {
paused = false;
boolean wasBackground = !foreground;
foreground = true;
if (check != null)
handler.removeCallbacks(check);
if (wasBackground) {
Log.i(TAG, "went foreground");
for (Listener l : listeners) {
try {
l.onBecameForeground();
} catch (Exception exc) {
Log.e(TAG, "Listener threw exception!", exc);
}
}
} else {
Log.i(TAG, "still foreground");
}
}
#Override
public void onActivityPaused(Activity activity) {
paused = true;
if (check != null)
handler.removeCallbacks(check);
handler.postDelayed(check = new Runnable() {
#Override
public void run() {
if (foreground && paused) {
foreground = false;
Log.i(TAG, "went background");
for (Listener l : listeners) {
try {
l.onBecameBackground();
Intent intent = getPackageManager().getLaunchIntentForPackage("com.package.name");
if (intent != null) {
// We found the activity now start the activity
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
} else {
// Bring user to the market or let them choose an app?
intent = new Intent(Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setData(Uri.parse("market://details?id=" + "com.package.name"));
startActivity(intent);
}
} catch (Exception exc) {
Log.e(TAG, "Listener threw exception!", exc);
}
}
} else {
Log.i(TAG, "still foreground");
}
}
}, CHECK_DELAY);
}
#Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}
#Override
public void onActivityStarted(Activity activity) {
}
#Override
public void onActivityStopped(Activity activity) {
}
#Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
#Override
public void onActivityDestroyed(Activity activity) {
}
}
Now in onCreate method of Application.class put the code:
#Override
public void onCreate() {
super.onCreate();
Foreground.init(this);
}
You have to set your app's package name in onActivityPaused() method.
Thanks
Do the code in onPause
#Override
protected void onPause() {
Toast.makeText(getApplicationContext(),"Background",Toast.LENGTH_LONG).show();
super.onPause();
}
So if the app goes in background, you will get toast.
Hope it helps..All the best
Try using Services. Read more on http://developer.android.com/guide/components/services.html
I'm trying to get the activity to finish after it's finished speaking but for some reason I cannot fathom it tells me that the setOnUtteranceCompleted not applicable for text to speech. I'm new to android programming so please be gentle :-)
Here's the code...
public class SpeakActivity extends Activity implements OnUtteranceCompletedListener{
Random randnum = new Random();
TextToSpeech tts = null;
private boolean ttsIsInit = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_speak);
// Show the Up button in the action bar.
setupActionBar();
startTextToSpeech();
}
void startTextToSpeech(){
final int randint = randnum.nextInt(4);
final String text = ((GlobVars) this.getApplication()).getResponse(randint);
tts = new TextToSpeech(this, new OnInitListener() {
public void onInit(int status) {
tts.setOnUtteranceCompletedListener(this);
if (status == TextToSpeech.SUCCESS) {
ttsIsInit = true;
if (tts.isLanguageAvailable(Locale.ENGLISH) >= 0){
tts.setLanguage(Locale.ENGLISH);
}
tts.setPitch(0.5f);
tts.setSpeechRate(0.5f);
if (tts != null && ttsIsInit) {
Log.d("got ere", "spoken");
tts.speak(text, TextToSpeech.QUEUE_ADD, null);
}
}
}
});
}
// shut down tts to free the TTS resources
#Override
public void onDestroy() {
if (tts != null) {
tts.stop();
tts.shutdown();
}
super.onDestroy();
}
#Override
public void onUtteranceCompleted(String arg0) {
((GlobVars) this.getApplication()).setListen(true);
this.finish();
}
}
I am ot sure but as per the docs of setOnUtteranceCompletedListener(), you might need to use TextToSpeech.OnUtteranceCompletedListener listener as an argument. I think the way to use the function is as below. Note that use runOnUIThread method in case you want to make any changes to the UI on the call of the onUtteranceCompleted function.
TextToSpeech tts= new TextToSpeech(context, new OnInitListener() {
#Override
public void onInit(int status) {
tts.setOnUtteranceCompletedListener(new OnUtteranceCompletedListener() {
#Override
public void onUtteranceCompleted(String utteranceId) {
//Do things here
}
});
}
});
Source of above : Check onUtteranceCompleted does not get called? question.
Hope this helps.
I created some TTS Manager, because I want use someTTsObject.speak("some string") in other classes.
This is my Manager class:
public class TtsManager
{
private TextToSpeech myTTS;
private Context context;
public TtsManager(Context baseContext)
{
this.context = baseContext;
initOrInstallTts();
}
public void initOrInstallTts()
{
myTTS = new TextToSpeech(context, new OnInitListener()
{
public void onInit(int status)
{
if (status == TextToSpeech.SUCCESS)
{
myTTS.setLanguage(Locale.US);
}
else
installTts();
}
});
}
private void installTts()
{
Intent installIntent = new Intent();
installIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
installIntent.setAction(TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA);
context.startActivity(installIntent);
}
public void speak(String text)
{
myTTS.speak(text, TextToSpeech.QUEUE_FLUSH, null);
}
}
and this is my main class, where I want to use that:
public class main extends Activity {
TtsManager tts;
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tts = new TtsManager(this);
tts.speak("Welcome in my app");
}
}
When I run compilation I see on the LogCat:
08-30 17:25:52.531: I/TTS received:(2782): Welcome in my app
but i don't hear any text. I tested it on the virtual machine and phone.
Why that doesn't work?
Cheers!
Ok, the problem is that you are not waiting until the callback comes from the system telling you the TTS is initialized. You can't call speak until onInit is called with a SUCCESS value.
problem is that you are calling speak function without initializing tts engine....
add
tts.initOrInstallTts();
after
tts = new TtsManager(this);
Like:
public class main extends Activity {
TtsManager tts;
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tts = new TtsManager(this);
tts.initOrInstallTts();
tts.speak("Welcome in my app");
}
}
I'm getting reports that, on some (not all) HTC Desire HD (FRF91, 2.2) and HTC EVO 4G ( PC36100|3.29.651.5, 2.2), the TextToSpeech.OnInitListener.onInit(int) is being called repeatedly (over 1500 times in the space of a few seconds) on the same object. This behaviour does not occur for any of my other users (or with other Desire HD users) AFAICT.
The code is:
TextToSpeech tts = new TextToSpeech(context, new TextToSpeech.OnInitListener() {
private int mCallCount = 0; // trying to investigate potential infinite loops
#Override
public void onInit(int status) {
if ((mCallCount % 100) == 1) {
// report this
}
mCallCount++;
}
});
Anyone any ideas?
EDIT: I have also tried calling the shutdown() method (the first time multiple listener calls are detected) but this doesn't seem to help.
Maybe you should get around it with your own intermediary method, for example:
private long lastCall = 0;
private long deepBreath = 5*1000; //5 seconds
private boolean hasRested;
TextToSpeech tts = new TextToSpeech(context, new TextToSpeech.OnInitListener() {
#Override
public void onInit(int status) {
long thisCall = Calendar.getInstance().getTimeInMillis();
intermediaryMethod(status, thisCall);
}
});
//new method
public void intermediaryMethod(int status, long thisCall) {
hasRested = (thisCall-lastCall)>=deepBreath;
if (hasRested) {
lastCall = thisCall;
//do something about 'status'
}
}
This may or may not help, but I had a similar problem when call tts from a service, luckily for me I was better off doing my tts from an activity which solved the problem.
If you do this, and it is appropriate, make sure your manifest for the activity has:
android:finishOnTaskLaunch="true"
Try to create object of the Textospeech before on create ie. globally .try this code and check is it still calling many times????
public class TtsActivity extends Activity implements OnInitListener {
private int MY_DATA_CHECK_CODE = 0;
private TextToSpeech tts;
private EditText inputText;
private Button speakButton;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
inputText = (EditText) findViewById(R.id.input_text);
speakButton = (Button) findViewById(R.id.speak_button);
speakButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
String text = inputText.getText().toString();
if (text!=null && text.length()>0) {
Toast.makeText(TtsActivity.this, "Saying: " + text, Toast.LENGTH_LONG).show();
tts.speak(text, TextToSpeech.QUEUE_ADD, null);
}
}
});
Intent checkIntent = new Intent();
checkIntent.setAction(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA);
startActivityForResult(checkIntent, MY_DATA_CHECK_CODE);
}
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == MY_DATA_CHECK_CODE) {
if (resultCode == TextToSpeech.Engine.CHECK_VOICE_DATA_PASS) {
// success, create the TTS instance
tts = new TextToSpeech(this, this);
}
else {
// missing data, install it
Intent installIntent = new Intent();
installIntent.setAction(TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA);
startActivity(installIntent);
}
}
}
#Override
public void onInit(int status) {
if (status == TextToSpeech.SUCCESS) {
Toast.makeText(TtsActivity.this,
"Text-To-Speech engine is initialized", Toast.LENGTH_LONG).show();
}
else if (status == TextToSpeech.ERROR) {
Toast.makeText(TtsActivity.this,
"Error occurred while initializing Text-To-Speech engine", Toast.LENGTH_LONG).show();
}
}
}