Text to speech and Orientation change - android

My application makes heavy use of the Text to Speech API.
It is a game which leads the player to change the orientation frequently. I do have different layouts for portrait and landscape and this is ok.
I use onRetainNonConfigurationInstance() and onCreate() to keep the state.
However, there is one fact that causes problems:
I am calling tts.shutdown() in onDestroy() which I think is necessary to free resources properly. However, this causes the current text and all queued messages to be lost when the user changes the orientation. As the Text-to-Speech object has a reference on the "old" activity, I cannot transfer it to the new activity.
How can I solve this problem?

This could be a good use for the Application class, where you initiate the TTS in onCreate() (and forget about it, as onTerminate() is not called on real devices).

Related

Getting around onDestroy being called on orientation change?

I have a stock Nexus 5 running 4.4.2 (using ART if it matters) and I've found an interesting scenario. I have this as my onDestroy():
#Override
protected void onDestroy() {
super.onDestroy();
t.setText("onDestroy");
t.show();
}
It's a GPS oriented app so I'm up and walking around. I am using the technique mentioned in this question to show a lot of debug toast messages.
Anyway, when I rotate my app, the toast appears. I understand that the activity is destroyed and recreated for the new orientation, but how can I know what's really going on? How can I tell when my app is REALLY getting destroyed and not just being rotated? Similar to this question, I want to log out when a particular activity is destroyed.
Since Honeycomb, the isChangingConfigurations() method can be queried to check whether the Activity is being recreated due to configuration changes. Alternatively, the isFinishing() method can be queried on any API level to check whether the Activity is actually being finished, or is only being destroyed temporarily by the system.
As far as I can determine, the two methods should always return mutually consistent results in practice. The only point where they might have diverged is when the system kills the process to clear memory, but there are no callbacks or interaction with the app at that point.
The documentation of the onDestroy() method mentions the use of the isFinishing() method:
Perform any final cleanup before an activity is destroyed. This can happen either because the activity is finishing (someone called finish() on it, or because the system is temporarily destroying this instance of the activity to save space. You can distinguish between these two scenarios with the isFinishing() method.
You can put it in a fragment with setRetainInstanceState(true) set. Place your code in the onDestroy() method of the fragment. Then, the fragment will not be destroyed on orientation changes.
First of all, you should not use onDestroy() to do anything because its not guaranteed to be called. I would put things on the onPause() method; I wouldn't even put things in onStop().
Also, Im not sure why you want to log out a user when they navigate away from the app. I would rather implement some kind of timer on the app or server to log out after x time.
Now, the answer lies in the documentation: http://developer.android.com/reference/android/app/Activity.html#ConfigurationChanges
You might want to override onConfigurationChanged so that your activity is not restarted.
I found a couple of solutions which are really just patterns to detect when the screen rotates. Alternatively, you can determine that the device was actually destroyed by checking some static data member to see if it was initialized or not.
Configuration changed solutions:
The first one involves handling all of the configuration changes in the onConfigurationChanged callback.
"Note that this will only be called if you have selected
configurations you would like to handle with the configChanges
attribute in your manifest."
The second involves listening for Display.getRotation() which returns a Surface.ROTATION_* object. Which is the new orientation of your screen relative to the natural state of the device orientation.
Again, you can use the configuration changes along with the static member.
Add ConfigChanges.UiMode flag to the ConfigurationChanges attribute for your MainActivity class, and this solves the problem.
More details: Android Launcher "OnDestroy" gets called twice

Do variables retain values on orientation change?

I know that when you change the device from landscape to portrait and vice versa, the onCreate method is called. That has been a cause of some problems for me.
Say I have a database which holds the downloads that are to be performed. I plan on making a splash screen where a DAO object will convert this data into objects and put them into a Vector<DownloadTask>. This will happen on an AsyncTask's doInBackground().
onPostExecute the splash screen will start a new Activity.
All is fine so far except when I have to handle orientation change.
The new Acitivity after the splash screen will start a Timer which is responsible for scheduling the downloads. This will be started in onCreate. The DownloadTask all implement Runnable so they are threads.
When the download is over or abruptly stopped, the thread is supposed to tell this to the scheduler and decrease the number of threads running simultaneously. This is to cap the number of simultaneous downloads.
So, if the timing is right (or wrong) such that the orientation change and the fninishing of the thread line up together, what will happen to the Timer ? Will it be null ?
When orientation changes, are the variables reset to their default value ?
Also, how do I prevent orientation change ?? Like some games do
When orientation changes, are the variables reset to their default value ?
If they are Activity level variables then yes they will return to their default values since the Activity is destroyed and recreated each time. To avoid this you could either save the variables in finish() each time but this could be messy since you don't know when this might happen (but there are ways around that). You could also specify in your manifest.xml that you will handle these changes by adding
android:configChanges="orientation"
to your <activity> tags in the manifest. In the docs, they don't recommend this but I think mostly to cover their butts. I do it and haven't had any problems so far. This will keep the Activity from being destroyed.
Also, how do I prevent orientation change ??
You can do this also in the manifest by adding something like
android:screenOrientation="portrait"
to the <activity> tag for those Activities that you wish to have a certain orientation.

How to prevent TextToSpeech from stopping during rotation

My app is implementing Text-to-Speech that is working well. But when I rotate my phone, the dictation stops itself.
You have to know that I use two different layout for landscape/portrait mode.
How should I handle the screen rotation ?
1) With android:configChanges="orientation|screenSize" // I don't think it's the best way
2) With something else?
Thank you
There are several steps to enabling the TTS to continue working on rotation, without needing to handle the configuration changes in your app. The steps include:
Make the TTS variable static
private static TextToSpeech tts;
Only initialize it once, if it's null
if (tts == null) { ... }
Initialize the TTS object using the Application Context
tts = new TextToSpeech(getApplicationContext(), this);
Here's a snapshot of my experimental app that incorporates these elements http://code.google.com/p/android-tts-experiments/source/detail?r=b879fd1042151cbebc736b89a9fb660c895081ea I've managed to test it on various android devices and android versions. Sometimes the TTS seems to hesitate slightly on rotation on my older, less powerful devices, however it continues speaking the text it's being given to say and queued messages are retained in the queue and spoken once the current text has been spoken completely by the TTS engine.
You basically have 2 options:
1) Handle the orientation change yourself. Usually this is pretty easy. You just need to implement onConfigurationChange() and in there you can just rebuild the layout by calling setContentView(). Be aware that this will recreate all your UI views so you will have to call findViewById() and reset onClickListeners again for all views that you need to keep track of.
2) Ensure that the TTS instance doesn't get shutdown on orientation change. You can do this either by passing the TTS instance from one instance of the activity to the next OR keep track of the TTS instance in a static variable (if you do this you need to make sure that you call shutdown() on it at the appropriate time, not in onDestroy() of your activity.

Android - Losing scope on UI elements after onDestroy()

I am trying to handle problems that occur in my application when the phone is plugged into certain types of chargers and put into "Car Mode" or "Driving Mode".
In the running application, onDestroy() is called and immediately followed by onCreate(), and the application starts again normally. However, subsequent calls to update UI elements (in the newly created main Activity) now have no effect, and it looks like I've lost scope on my layout.
RelativeLayout splash = (RelativeLayout) findViewById(R.id.splash);
splash.setVisibility(View.VISIBLE);
What could be ocurring onDestroy() that I'm not accounting for? I don't do much cleanup onDestroy because I didn't think I needed to.
The Activity has been detached from the UI by the time onDestroy() is called so having UI calls to it doesn't make any sense. If you need the splash to be shown, set it to View.VISIBLE in onCreate(), onResume(), or maybe onPause(). I'm not entirely sure if onPause() would act any different.
When the phone rotates the activity is destroyed and recreated. Plugging into a car charger usually forces the phone to landscape mode, thus rotating it (from portrait, most likely) and calling onDestroy. There is a way to prevent this behavior with some activity flags -- but Google advises against it.
We need to see some more code for this Activity to figure out what's going on.
Also, as DeeV points out, the activity is long gone by the time onDestroy gets called, so it might not be the right place to be doing whatever it is that you're doing -- but we need more code to be sure.
As a sidenote, sliding the keyboard up (on phone's that have slideout keyboards) will produce the same effect.

Android: Keep MediaPlayer running during Activity screen orientation update

I use a MediaPlayer to play an MP3. Currently I disabled screen orientation changes by using
android:screenOrientation="portrait"
android:configChanges="keyboardHidden|orientation"
in the manifest. I do want to support landscape mode now - i.e. removed those tags - but have the problem that during the destroy/create cycle the player gets stopped and then restarted. This is okay and I actually do this even manually in onPause() to stop the player when the activity goes in the background.
To keep it running during orientation changes now, I tried making it static (and using the Application Context to create it once). Of course, when I remove the player.stop() in onPause() now, it does what I want - well, until the Activity goes in the background.
So, two questions:
How can I determine if the Activity will be recreated directly after the call to onStop()
Or: How can I keep the MediaPlayer running during that cycle, yet stop it when the App goes in the background?
Have you looked at using the onConfigurationChanged() callback to handle some of this logic?
Regarding your question how you can reliably determine whether your Activity is destroyed due to a configuration change, see my answer here: How to save/restore(update) ref to the dialog during screen rotation?(I need ref in onCreate method of activity.)
With this, the answer to your second question should be easy.
Try running MediaPlayer in different Thread.
You can add to this thread a more complex API to which you can call from onCreate/onStop/on*
If you are using fragments (and you should :P), then calling setRetainInstance(true) inside your fragments onCreate() call will make this problem go completely go away.
Please see the following answer: https://stackoverflow.com/a/31466602/994021
It is a POC I've created for this purpose. Tired of solving this issue over and over again for different projects. I hope it helps.
I really recommend to switch to a third party player like ExoPlayer from google guys: https://github.com/google/ExoPlayer

Categories

Resources