Android App Development: Handling Screen Orientation Changes - android

I have run into a problem with handling screen orientation changes (For example: portrait to landscape). I'm developing a flashlight app which has a toggle button that turns the LED light on or off on the person's phone. I set up my onPause() method to kill the app like so:
#Override
protected void onPause() {
super.onPause();
finish();
}
This is because if I was to open the app and then go something else (like open youtube) and then come back and press the button again it crashes. But because of this "finish();" thing I can't handle screen orientation changes because apparently when it switches orientation it calls the onPause() method which finishes!
SO my question is...how do I do both? I want to be able to fix the crash problem but also not have it crash when the screen orientation changes. I'm not asking for code (although it would be helpful) just some insight from a new set of eyes.
I thought about using an "if" statement...would this work?
Thanks in advance,
Andrew

When the screen orientation changes, the activity will do onPause(), onStop() and onCreate() again. If you don't want your activity do onPause when the orientation changes, try this:
In your manifest.xml, add android:configChanges="orientation|keyboardHidden" for your activity like:
<activity android:name=".yourname"
android:configChanges="orientation|keyboardHidden">
</activity>
In your activity, override this method:
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
}
But my advice is not to use finish() in onPause(), the system can handle the lifecycle just fine. If you want to finish your activity when the home or back button pressed, try to override onBackPress() and onUserLeaveHint(), these two method can catch back and home button pressed, and add finish() in these two method.
Hope it helps.

Related

IllegalStateException when adding fragment in response to a screen lock

Firstly, I believe this is not a duplicate question, although the solution for the error has been asked a lot of times. I have tried atleast 5 different solutions but they either don't change anything or make things worse. I want to pause the activity when the screen is locked/focus changed and allow the user to unpause the activity when the app is opened again.
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (!hasFocus && pauseFragment == null) {
exercisePlayPause(isPaused, workoutExerciseNum);
isPaused = !isPaused;
}
}
exercisePlayPause calls the following method
public void PassExerciseNum(int exerciseNum, Boolean isPaused) {
if (!isPaused) {
pauseFragment = new PauseFragment();
pauseFragment.getExNum(exerciseNum);
getFragmentManager().beginTransaction().add(R.id.aworkout_layout, pauseFragment, "pause").commit();
} else {
getFragmentManager().beginTransaction().remove(pauseFragment).commit();
pauseFragment = null;
exercisePlayPause(true, exerciseNum);
}
}
This works fine when the home button is pressed or some other app is activated or the notification bar is clicked. But when the screen is locked then I get the following error
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1411)
at android.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1429)
at android.app.BackStackRecord.commitInternal(BackStackRecord.java:687)
at android.app.BackStackRecord.commit(BackStackRecord.java:663)
The offending line is getFragmentManager().beginTransaction().add(R.id.aworkout_layout. I am pausing few processes when any event that makes the user move away from the app. And when the user comes back to the app, he/she can resume from there. Hence, I am not destroying the activity. I have tried using commitAllowingStateLoss() but that makes me lose data for the paused processes and becomes a bit messy.
So in short the question is how to make a fragmenttransaction happen before onSaveInstanceState is triggered when the screen lock button is pressed?
The activity's orientation was sensorLandscape. When a screen locks it converts the screen to portrait and when the screen is unlocked it starts from a portrait mode and then becomes landscape. Hence, onDestroy was called when screen is locked. And when the screen is unlocked, onCreate gets called. This orientation change overrides the activity's orientation set in manifest. What happens when this forceful orientation happens can be controlled by adding android:configChanges="orientation|screenSize|keyboardHidden"/> to the manifest. This prevents onDestroy being called and the "illegalStateException" can be avaoided.

Activity return to Portrait when Screen is Locked

Related questions:
https://stackoverflow.com/questions/15648713/strange-behaviour-while-screen-lock-in-landscape
This is a strange behaviour i got in my Activities.
Portrait mode (It's normal)
Press screen lock, Activity: onPause();
Unlock the screen, Activity: onResume().
Landscape mode (It's strange)
Press screen lock, Activity: onPause() -> onStop() -> onDestroy() -> onCreate() -> onStart() -> onResume() which loads the Portrait layout;
Unlock the screen, Activity: onPause() -> onStop() -> onDestroy() -> onCreate() -> onStart() -> onResume() and loads the Landscape layout.
What I expect is:
Portrait mode: (same)
Landscape mode: (should act like Portrait mode)
Press screen lock, Activity: onPause();
Unlock the screen, Activity: onResume().
So my questions:
Why does my Activities behave like this?
How does your Activities behave?
Original text description of my question:
While i press the Lock Screen button of my phone, when my Activity is at its Landscape mode, i noticed (in the debug messages i output to Eclipse) the Activity is re-created to its Portrait mode (while the screen is all black, of course). Then when i press the Lock Screen button again to unlock the screen, the Activity was destroyed and re-created to its Portrait again.
As i remember (not 100% sure though), and what i expect is, my Activity should only undergo onSaveInstanceState() and onPause(), while Lock Screen in Landscape mode, like what it does in Portrait mode. Rather than re-creating the Activity to Portrait and going back to Landscape again.
Is it that i have messed up something with my phone? How can i fix it back to normal?
Thanks!
Thanks everyone for contributing into this issue. Especially thanks #HoanNguyen for his effort of testing for me in his devices. And Especially thanks #Raghunandan for having an in-depth discussion with me concerning this issue.
Summarising everyone's contributions so far, i have the following conclusions:
1. This is a normal phenomenon.
It seems that, on mobile phones, the running Activities, that are in Landscape mode, are switched into Portrait mode upon screen lock is a normal behaviour. At least it is true on the tested phones so far. So we have to make sure our lifecycle functions can take care this change elegantly always.
2. Guess this is because of the "default orientation" in the locked screen.
We do not have documentation or many resource talking about this issue. But the assumption that the running Activities switching back to the device's "default orientation" upon screen lock, as in most devices the locked screen is in Portrait, is quite logical.
Further study:
I just wonder how the Activities behave if we are having a landscape locked screen?
You can stop the activity from restarting on orientation change but this is generally a pretty bad idea.
The Android documentation has a section on handling runtime changes with this note:
Note: Handling the configuration change yourself can make it much more difficult to use alternative resources, because the system does not automatically apply them for you. This technique should be considered a last resort when you must avoid restarts due to a configuration change and is not recommended for most applications.
Android generally only recommends you supress recreating on rotation if you don't need alternate resources and, more importantly, have a performance requirement. A well-designed app shouldn't need to do this in most cases.
If you insist on going down the path of supressing default Android behavior, I'd modify Raghunandan's code and include a screen size attribute as well. As of API level 13, the screen size changes upon orientation change. So you must include screenSize unless you are only targeting API 12 and below.
<activity android:name=".MyActivity"
android:configChanges="orientation|screenSize"
android:label="#string/app_name">
To avoid activity from restarting
<activity android:name=".MyActivity"
android:configChanges="orientation|keyboardHidden"//add tthis in manifest
android:label="#string/app_name">
http://developer.android.com/guide/topics/resources/runtime-changes.html.
In normal circumstances when your screen is locked your activity is paused and when screen is unlocked activity resumes.
An issue when screen locked is: Current Activity may be stopped forcefully by the system if it finds shortage of memory, instead of moving Activity to background. In such a case, we should have to save (all the necessary data) the current state of the Activity.
Save you data in onSaveInstanceState() and restore data onRestoreInstanceState().
#Override
public void onSaveInstanceState(Bundle outState)
{
Log.v("$````$", "In Method: onSaveInstanceState()");
//if necessary,set a flag to check whether we have to restore or not
//handle necessary savings…
}
#Override
public void onRestoreInstanceState(Bundle inState)
{
Log.v("$````$", "In Method: onRestoreInstanceState()");
//if any saved state, restore from it…
}
In your onCreate()
IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_USER_PRESENT);
mReceiver = new ScreenReceiver();
registerReceiver(mReceiver, filter); //register
public class ScreenReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent)
{
if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF))
{
Log.v("$$$$$$", "In Method: ACTION_SCREEN_OFF");
// onPause() will be called.
}
else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON))
{
Log.v("$$$$$$", "In Method: ACTION_SCREEN_ON");
//onResume() will be called.
//Better check for whether the screen was already locked
// if locked, do not take any resuming action in onResume()
//Suggest you, not to take any resuming action here.
}
else if(intent.getAction().equals(Intent.ACTION_USER_PRESENT))
{
Log.v("$$$$$$", "In Method: ACTION_USER_PRESENT");
//Handle resuming events
}
}
In your onDestroy
#Override
public void onDestroy()
{
super.onDestroy();
Log.v("$$$$$$", "In Method: onDestroy()");
if (mReceiver != null)
{
unregisterReceiver(mReceiver); //unregister
mReceiver = null;
}
}

How to know when the app is open (and not when onCreate is called)?

I want to track app opens in android but the problem is that onCreate for the main activity could be called multiple times when the orientation change. is there another way to track app open ?
Other methods than onCreate() could be used :
OnStart()
OnRestart()
OnResume()
You'll want to consult this page of the documentation in order to decide which one fits your needs better as they all get called in different situations.
Alternatively, you could always handle the orientation change yourself, that way the onCreate() method wouldn't be called when the screen rotates:
Modifications to the Manifest file:
<activity
android:name=".ActivityName"
android:configChanges="keyboardHidden|orientation" />
Add this method in the Activity :
/** {#inheritDoc} */
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
}
Hope this answers your question.
Do you want to consider handling orientation changes by yourself? That way, onCreate is not called when orientation changes (which is not a very good idea if you have different layouts for potrait and landscape). You can then rely on onStart method which is called when the screen is turned off/on, or when the activity resumes.
The activity is open when onCreate is called, and closes when onDestroy is called. Each onCreate will always be paired with an onDestroy; when the orientation changes, the activity is (by default) first destroyed, then recreated in the new orientation.

Detecting screen orientation change in OnPause()

I'm using MonoDroid but an equivalent Java answer can still help.
I'm using a portrait layout and a landscape layout so, if possible, I want to use the Android screen orientation to automatically destory/create activities.
My app is using TextToSpeech so in the activity's OnPause() I am stopping it, which works well when the Home key is pressed or an incoming call is happening. However I don't want to stop the TextToSpeech on a screen orientation change from the user.
Is there a simple way of detecting this change so that TextToSpeech isn't interrupted?
My activity's OnStop() code:
protected override void OnPause()
{
// I need this for Home key, intercepting phone calls, etc.
// But how can I prevent this for a screen orientation change?
// Need to enable screen orientation to get my portrait/landscape views
if(Text2Speech.IsTextToSpeechInitialised && Text2Speech.TextToSpeech != null)
Text2Speech.TextToSpeech.Stop();
base.OnPause();
}
As others have said in the comments on the question, I think you'll want to handle configuration changes yourself here. There are other configuration changes that can cause your activity to be restarted as well, such as revealing the device's hardware keyboard.
In Mono for Android you can specify which of these you want to handle yourself in the ActivityAttribute:
[Activity(ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.KeyboardHidden)]
public class MainActivity : Activity

how to define the screen orientation before the activity is created?

I defined my activity to only be in portrait mode by :
android:screenOrientation="portrait"
When I take a picture with the camera function via intent, take this picture in landscape mode and turn the screen to portrait mode when saving it, i return to my activity again. What I dont understand is, that my activity for a short time is in landscape mode, is destroyed and then built again in portrait mode... as my onCreate ond onRestore functions need some time, the waiting time for the user is doubled...
Is there a workaround or something else?
You can also register your activity to explicitly handle any orientation changes by adding android:configChanges="orientation" to the activity definition and then overriding the onCofigurationChanged method in your activity like this:
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
I'm not sure if it would help with your specific problem, but I remember doing this when I wanted an activity to only display in portrait mode. Let me know if it helps :)
Add android:configChanges="orientation" to your <activity> tag. This way the activity will not be recreated on orientation change, but will just receive a callback onConfigurationChanged which you can ignore.

Categories

Resources