Dismiss dialog after screen orientation change - android

1) I launch a background task (via AsyncTask)
new FindJourneyTask().execute(); // FindJourneyTask extends AsyncTask
2) Still in the main thread (just before new thread is launched) I create a dialog with showDialog(dialogId)
// this method is in FindJourneyTask
protected void onPreExecute() {
showDialog(DIALOG_FINDING_JOURNEY);
}
3) Screen orientation changes and the Activity is recreated
4) How can I now dismiss the dialog from the FindJourneyTask? Calling dismissDialog(dialogId) does nothing.
// this method is in FindJourneyTask
protected void onPostExecute(FindJourneyResult result) {
dismissDialog(DIALOG_FINDING_JOURNEY); // does nothing
}

This is a common problem, and there are no real good solutions. The issue is that on screen orientation change, the entire Activity is destroyed and recreated. At the same time, the Dialog you previously had is re-created in the new Activity, but the old background task still refers to the old Activity when it tries to dismiss the dialog. The result is that it dismisses a dialog which was long ago destroyed, rather than dismissing the dialog the new orientation created.
There are three basic solutions:
Override the default orientation-handling code so that your Activity is not destroyed upon rotation. This is probably the least satisfactory answer, as it blocks a lot of code that is automatically run upon orientation changes.
Create a static member variable of your Activity that references the Activity itself, so you can call STATIC_ACTIVITY_VARIABLE.dismissDialog().
Code a solution in which the background task keeps track of the current Activity and updates itself as necessary.
These three solutions are discussed at length here: http://groups.google.com/group/android-developers/browse_thread/thread/bf046b95cf38832d/

There is a better solution to this problem now which involves using fragments.
If you create a dialog using DialogFragment, then this fragment will be responsible for maintaining your dialog's lifecycle. When you show a dialog, you supply a tag for your fragment (DialogFragment.show()). When you need to access your dialog, you just look for the necessary DialogFragment using FragmentManager.findFragmentByTag instead of having a reference to the dialog itself.
This way if device changes orientation, you will get a new fragment instead of the old one, and everything will work.
Here's some code based also in #peresisUser answer:
public void onSaveInstanceState(Bundle outState) {
AppCompatActivity activity = (AppCompatActivity) context;
FragmentManager fragmentManager = activity.getSupportFragmentManager();
DialogFragment dialogFragment = (DialogFragment) fragmentManager.findFragmentByTag("your_dialog_tag");
if(dialogFragment!=null) {
Dialog dialog = dialogFragment.getDialog();
if(dialog!=null && dialog.isShowing()) {
dialogFragment.dismiss();
}
}
}

This is long after the question was asked and answered, but i stumbled upon this problem also and wanted to share my solution...
I check in onSavedInstance() which runs on orientation change, whether the dialog is showing or not with dialog.isShowing(), and pass it into outState variable. Then in your onCreate(), you check this var if it's true. If it is, you simply dismiss your dialog with dialog.dismiss()
Hope this helps others :()

I tried adding setRetainInstance(true); on OnCreate function of DialogFragment. This will cause dialog to dismiss on rotation.

Just add this line to specific activity in your Manifest to solve this problem android:configChanges="orientation|screenSize|smallestScreenSize"
like this,
<activity
android:name=".PDFTools"
android:exported="false"
android:configChanges="orientation|screenSize|smallestScreenSize"
android:theme="#style/Theme.DocScanner.NoActionBar" />

Related

Showing DialogFragment throws exception when activity is not on screen

I have within an Activity, called CounterActivity, a counter that counts let's say from 100 down to 0 with a second interval. A DialogFrament is shown when the counter gets to 0 as follows:
MessageFragment dialog = new MessageFragment();
dialog.show(getSupportFragmentManager(), "MessageFragment");
The main layout of that activity has the following attribute set android:keepScreenOn="true",that way the screen will not timeout as long as the activity is visible. If I then open another application then onStop is called on CounterActivity. If the counter, which is still running in the background, gets to 0 instead of showing the DialogFragment the following exception is thrown:
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
You cannot do fragment transactions when your activity is not on screen for UI reasons (your activity may be killed then restored, thus, any UI change won't be made again).
Therefore you must avoid any UI change when your activity is not active. You have multiple solutions to handle the problem :
1-You can simply register a boolean "showDialogOnResume", then override your "public void onResume()" :
#Override
public void onResume() {
super.onResume();
if(showDialogOnResume) {
/*build your dialog here*/
dialog.show(getSupportFragmentManager(), "MessageFragment");
}
}
If you are doing this in activity (not in fragment) i hevily recommand to do override "onPostResume()" instead. Look this post and especially jed answer and pjv comment.
It may even happen that you are not calling :
super.onActivityResult();
On your "onActivityResult()"; as Sufian said in his comment to jed answer.
Basically : avoid any UI change during activity lifecycle. Allow it only when your activity is active. If you cannot do that (other threads like AsyncTasks), get the answer, save it somehow, and perform your change on your activity result. Because if the user never resume your activity, you don't need to do your changes.

Calling Fragment not paused when showing DialogFragment

In one part of my application, I show the user a ListView. When the user presses an item in the list, a DialogFragment is shown.
#Override
public void onClick() {
android.support.v4.app.FragmentTransaction ft = getFragment().getFragmentManager().beginTransaction();
ft.addToBackStack(null);
SingleSettingDialogFragment dialog = SingleSettingDialogFragment.newInstance(...);
dialog.show(ft, "Single");
}
The DialogFragment have the following structure:
#Override
public Dialog onCreateDialog(final Bundle savedInstanceState) {
super.onCreateDialog(savedInstanceSate);
AlertDialog dialog = new AlertDialog.Builder(...)
...
.create();
...
return dialog;
}
When the user exits the DialogFragment, I expect the onResume() method of the calling fragment to be called, but it is not.
So what's so special about DialogFragments? Why aren't the calling Fragment paused when the Dialog is shown? And how can it be achieved?
I haven't found any reference to this behaviour in the Docs, so references is a plus.
This may help you: link
In fact a FragmentDialog is not an activity on itself but it is part of the same activity which contains the calling fragment.
It means that the fragment is not paused when dialogFragment is shown.
Citing the source I gave you, a fragment is paused when: Another activity is in the foreground and has focus, but the activity in which this fragment lives is still visible (the foreground activity is partially transparent or doesn't cover the entire screen).
Hope that helps
One way to deal with this is to embed your DialogFragment within an Activity and display the activity as a Dialog, there's a tip in the following link that explains how:
http://developer.android.com/guide/topics/ui/dialogs.html
You can use this to update the underlying Fragment because when the Dialog (which is an Activity) is finished, onResume() will be called on the underlying fragment. Add code to update the state of the fragment in the onResume() method and that's all there is too it.
Other approach is that you can override OnDismiss() method and use call back listener in it which will call back the parent fragment.
These are the suggestions from my side, Hope it will get any kind of clue.

Android DialogFragment Dismissal

I've looked at several of the questions pertaining to handling an orientation change in a DialogFragment, and the common theme seems to be that the askers aren't getting any answers, at none that I understand...
So, I'd like to take a different approach... Is there a way I can programmatically dismiss the dialog... Looking at my LogCat, I can see that if the dialogfragment is active, and the screen orientation is changed, then my Activity restarts.
Along the way, the DialogFragment is also restarted, but this happens after onCreate and before onResume (as close as I can tell). It's fairly simple for me to detect this in my dialog, and what I'd like to do is abort the dialog view creation in this case. What I've tried so far is like this:
if (normal_conditions_detected)
{
View v = inflater.inflate (R.layout.dialog_layout, container, false);
final Dialog d = getDialog();
d.setTitle (R.string.sensor_config);
... more stuff
return v
}
else
{
getDialog().cancel();
return null;
}
This does avoid the null pointer nonsense I was getting, but now I get what I can only describe as an empty dialog, even with the cancel() command in there. Is there a way I can get my dialogFragment code to refuse to create the view?
When you rotate your device, activity executes
onSaveInstanceState
method and save the current state which means it saves your fragment state as well, after recreation it executes
onCreate(Bundle savedInstanceState)
method again, savedInstanceState holds your old data, in here you can make something like
if (savedInstanceState == null){
YourDialogFragment f = new YourDialogFragment()
f.show // etc
}
Only once your fragment will be created, instead of putting a control inside fragment, you can control it by the activity

Proper way of dismissing DialogFragment while application is in background

I started using DialogFragment, because they are working nicely through orientation changes, and stuff. But there is nasty problem I encountered.
I have AsyncTask that shows progress DialogFragment and dismisses it onPostExecute. Everything works fine, except when onPostExecute happens while application is in background (after pressing Home button, for example). Then I got this error on DialogFragment dismissing - "Can not perform this action after onSaveInstanceState". Doh. Regular dialogs works just fine. But not FragmentDialog.
So I wonder, what is the proper way of dismissing DialogFragment while application is in background? I haven't really worked with Fragments a lot, so I think that I'm just missing something.
DialogFragment has a method called dismissAllowingStateLoss()
This is what I did (df == dialogFragment):
Make sure that you call the dialog this way:
df.show(getFragmentManager(), "DialogFragment_FLAG");
When you want to dismis the dialog make this check:
if (df.isResumed()){
df.dismiss();
}
return;
Make sure that you have the following in the onResume() method of your fragment (not df)
#Override
public void onResume(){
Fragment f = getFragmentManager().findFragmentByTag("DialogFragment_FLAG");
if (f != null) {
DialogFragment df = (DialogFragment) f;
df.dismiss();
}
super.onResume();
}
This way, the dialog will be dismissed if it's visible.. if not visible the dialog is going to be dismisded next the fragment becomes visible (onResume)...
This is what I had to do to achieve what you want:
I have a Fragment activity on which i was showing a dialog fragment named fragment_RedemptionPayment which is globally declared at the top. The following code dismisses the DialogFragment if it was showing before the activity goes in background and comes back in foreground.
#Override
public void onResume() {
super.onResume();
if(fragment_RedemptionPayment.isVisible()){
fragment_RedemptionPayment.dismiss();
}
}
Another new way of checking the state before calling dismiss is this:
if(!dialog.isStateSaved){
dialog.dismiss()
} else {
//Change the UI to suit your functionality
}
In this way its is checked that state is saved or not, basically on pause and onSaveInstanceState has been called.
For Java you can use isStateSaved()
A solution that might work is setting Fragment.setRetainInstance(true) in your dialogfragment, but that's not the prettiest of fixes.
Sometimes I have noticed that I have to queue up my dialog actions to let the framework restore the state first. If you can get hold of the current Looper (Activity.getMainLooper()) and wrap that in a Handler you could try passing your dismissal to the back of the queue by posting a runnable on that queue.
I often end up using a separate fragment that it retaininstance(true) that has a ResultReceiver. So i pass on that result receiver to my jobs and handle callbacks in its onReceive (often as a router for other receivers). But that might be a bit more work than it is worth if you are using async tasks.

How to save a Progress Dialog state correctly?

In my app when i do some long work i'm using a Progress Dialog while the work isn't finished.
I'm looking for it in every place but without sucess. Everything that i founded is all about saving user interface elements states.
Then, i would like to know how i can save progress dialog state correctly ?
I want this working because when the orientation screen change the app crashes.
Shoul i use onSaveInstanceState() method ? How ? I try using saving as a bundle but without succes...
Any advice would be nice...
thanks
I think, your dialog crashes on orientation change because you are not using
#Override
protected Dialog onCreateDialog(int dialogID, Bundle args)
and
showDialog(dialogID);
to show your dialog.
I had the same problem with an alert dialog. As I see it, if you don't create dialog that way, it becomes linked to your activity, and when activity is dead, the system finds that a dialog still try to access that dead activity, not new one.
You need to add this to the manifest file for the activity in which you are showing the dialog:
android:configChanges="keyboardHidden|orientation"
to handle the screen orientation you must use onRetainNonConfigurationInstance()
you can find a more extensive explanation about screen orientation here: Faster Screen Orientation.
The way I do this in my applications is overriding the onConfigurationChanged() method in the Activity like so:
#Override
public void onConfigurationChanged(Configuration config) {
//Just switch the layout without respawning the activity
super.onConfigurationChanged(config);
}
This hasn't given me any issues as of yet and my Activity doesn't reload or restart when the orientation is changed.

Categories

Resources