Android Dialog with dynamic text (API level < 8) killed on rotation - android

I want to create a dialog with a string that I build at runtime. It looks like API level 8 allows you to call showDialog with a bundle, but I have to write an app that will run on the older OSs.
How do I create a dialog with something like a simple error string and make sure it doesn't die when I rotate the screen.
I realize if I override onCreateDialog, it will do it for me. The problem is, this just takes the int constant. I need to pass a string to it so it knows what to put in the dialog.
If I build my dialog myself and then call .show() on it, it won't live through a screen orientation change.

If you're targeting API Level <8, then it's sort of a pain.
Set the string message to a property on your Activity
Use onSaveInstanceState(Bundle) and onRestoreInstanceState(Bundle) to manage your property through configuration changes (such as re-orientation)
In onPrepareDialog(int, Dialog), set the message of the dialog to this property. If you don't set this in onPrepareDialog, it'll re-display the previous dialog (in case your message needs to change between dialogs.)
Code:
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// Save dialog message
if(dialogMessage != null) {
outState.putString(STATE_KEY_DIALOG_MESSAGE, dialogMessage);
}
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
// Load dialog message
if(savedInstanceState.containsKey(STATE_KEY_DIALOG_MESSAGE)) {
dialogMessage = savedInstanceState.getString(STATE_KEY_DIALOG_MESSAGE);
}
}
/** onCreateDialog as normal **/
#Override
protected void onPrepareDialog(int id, Dialog dialog) {
super.onPrepareDialog(id, dialog);
switch(id) {
case DIALOG_MESSAGE:
// Decorate dialog appropriately
AlertDialog messageDialog = (AlertDialog) dialog;
messageDialog.setMessage(dialogMessage);
}
}

You could just pass the string in the constructor.
public class MyDialog extends Dialog {
public MyDialog(Context context, String msg) {
super(context);
TextView textView = new TextView(context);
textView.setText(msg);
setContentView(textView);
}
}

Related

Android - Determinate ProgressDialog inside DialogFragment

I know that Google's Material Design guidelines don't recommend using a ProgressDialog, instead using another less intrusive way to display progress, but I need to use a ProgressDialog for a specific Activity of my app.
So, the thing is that I want to wrap a ProgressDialog inside a DialogFragment, and thus my code is as follows:
public class MaterialProgressDialogFragment extends DialogFragment {
private int total;
private MaterialDialog myDialog;
#NonNull
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return myDialog;
}
public void incrementProgress(int by) {
myDialog.incrementProgress(by);
}
public void setTotal(int total) {
this.total = total;
}
public void setUp(Context context) {
myDialog = new MaterialDialog.Builder(context)
.title("Progress")
.content("Processing...")
.progress(false, total, true)
.build();
}
}
Because what I want to build is a determinate ProgressDialog, I want to be able to update its progress throghout the life of my app. For this I have made a method called setProgress(progress), but myDialog is always null, as well as the value returned from getDialog().
What am I doing wrong?
Thank you
EDIT: I'm showing the dialog inside my fragment's onCreateActivity() method, like follows:
MaterialProgressDialogFragment dialogFragment = new MaterialProgressDialogFragment();
dialogFragment.setTotal(100);
dialogFragment.setUp(getActivity());
dialogFragment.show(getSupportFragmentManager(), "");
dialog.incrementProgress(50);
Everything works as expected until the last line, which causes the app to throw an exception.
It isn't totally clear what the variable dialog inside setProgress(int progress) method is. But if you mean getDialog() by that, it takes time to create dialog by dialog fragment, and between the dialogFragment.show(getSupportFragmentManager(), "") and onCreateDialog(Bundle savedInstanceState) callback there will be some time interval, during which getDialog() will return null.
EDIT:
Ok, now it is more clear. But by the code above, you're violating the fragment framework rules. You should create your dialog in onCreateDialog(Bundle savedInstanceState) method, because otherwise you'll have problems with lifecycle (for example, if you'll rotate your screen, the app will crash).
I suggest you to use something like that:
public class MaterialProgressDialogFragment extends DialogFragment {
public static final String TOTAL_KEY = "total";
public static ProgressDialogFragment newInstance(int total) {
Bundle args = new Bundle();
args.putInt(TOTAL_KEY, total);
ProgressDialogFragment pdf = new ProgressDialogFragment();
pdf.setArguments(args);
return pdf;
}
#NonNull
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
MaterialDialog myDialog = new MaterialDialog.Builder(context)
.title("Progress")
.content("Processing...")
.progress(false, getTotal(), true)
.build();
return myDialog;
}
public void incrementProgress(int by) {
if (getDialog()!=null)
((MaterialDialog)getDialog()).incrementProgress(by);
}
public int getTotal() {
return getArguments().getInt(TOTAL_KEY);
}
}
You should save total variable to arguments, because it will be destroyed on configuration change (for example screen rotation).
Then just create and show it by:
MaterialProgressDialogFragment dialogFragment = MaterialProgressDialogFragment.newInstance(100);
dialogFragment.show(getSupportFragmentManager(), "");
When you want to change your progress, call:
dialog.incrementProgress(50);
But remember, dialog won't be created immediately, so if you'll call this right after show(), it won't take effect, because getDialog() will return null. If you want to just test it, call it delayed:
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
dialog.incrementProgress(50);
}
}, 200);
But anyway in real app you will change your progress from some background process.

Prevent Dialog (or DialogFragment) from closing when app goes to background

It's pretty common for my app to show a progress or AlertDialog to the user. If the user puts the app into the background and then returns later, I want the Dialog to still be shown. Is there a way to make Android handle this? I'd like it to either not close the dialog, or if it does reopen it automatically when the Activity resumes.
So far it's looking like no. I haven't found a ton of results about this (most people run into issues with orientation change, which my app does not allow) but very few ask about going into the background. I have tried every permutation of DialogFragment and regular Dialog, but they all disappear when the home button is pressed and the app is opened from the task manager.
I don't even have any code to show because it's all in the testing phase of various examples online. I suspect I will have to manage this myself, by checking in onResume() if something should be shown. If this is the case I can live with it, but I'd like to know for sure.
First lets clear something, like you can see in the next images, your activity or fragment can be destroyed for many reasons, so you have to deal with what you want saving "the state of your dialog".
Now the code:
public class CustomProgressDialog extends Dialog {
private static final String SHOWING_PROGRESS_DIALOG = "showing_progress_dialog";
private static final String STRING_PROGRESS_DIALOG = "string_progress_dialog";
private static final String SHOWING_POP_UP_DIALOG = "showing_pop_up_dialog";
private static final String STRING_POP_UP_DIALOG = "string_pop_up_dialog";
public TextView textView;
public CustomProgressDialog(Context context) {
super(context, android.R.style.Theme_Translucent_NoTitleBar);
setContentView(R.layout.progress_layout);
setCancelable(false);
textView = (TextView) findViewById(R.id.progress_textView);
}
}
public class MasterActivity extends FragmentActivity {
private CustomProgressDialog progressDialog;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_master);
progressDialog = new CustomProgressDialog(this);
if (savedInstanceState != null) {
boolean showingDialog = savedInstanceState.getBoolean(SHOWING_PROGRESS_DIALOG);
if (showingDialog) {
String msg = savedInstanceState.getString(STRING_PROGRESS_DIALOG, getResources().getString(R.string.progress_default_text));
progressDialog.textView.setText(msg);
progressDialog.show();
}
boolean mShowing_PopUpdialog = savedInstanceState.getBoolean(SHOWING_POP_UP_DIALOG);
String temp_msg = savedInstanceState.getString(STRING_POP_UP_DIALOG, "");
if (mShowing_PopUpdialog)
showPopUpDialog(temp_msg);
}
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (progressDialog.isShowing()) {
outState.putBoolean(SHOWING_PROGRESS_DIALOG, true);
outState.putString(STRING_PROGRESS_DIALOG, progressDialog.textView.getText().toString());
}
if (alert != null && alert.isShowing()) {
outState.putBoolean(SHOWING_POP_UP_DIALOG, true);
outState.putString(STRING_POP_UP_DIALOG, mString_dialog);
}
}
}
Try this in your DialogFragment's onPause() do this
#Override
public void onPause() {
super.onPause();
getActivity().getSupportFragmentManager().beginTransaction()
.addToBackStack("mydialogfragment").commit();
Log.v("dialog", "dialog is going down");
}
Then in your Activity's onResume() you call the DialogFragment back to life
#Override
protected void onResume() {
super.onResume();
getSupportFragmentManager().popBackStack();
Log.v("activity", "onresume called - i am bringing back the dialog");
}
Why i think this might work is a DialogFragment is a Fragment, and a Fragment's lifecycle is controlled by the Parent Activity as user #0mach0 diagram shows, so all you do is you push it do the backstack and call it back. so it should work
Try showing the dialog using parentFragmentManager. Worked for my DialogFragment
.show(parentFragmentManager, "TAG")

ProgressDialog setMessage is blank

This has been driving me nuts and I cannot find an answer anywhere. A very simple spinner dialog, but the setMessage is not working, it's blank!
public class MainActivity extends FragmentActivity {
ProgressDialog loadingProgress;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
loadingProgress = new ProgressDialog(this);
loadingProgress.setIndeterminate(true);
loadingProgress.setMessage("Loading");
loadingProgress.setProgressStyle(ProgressDialog.STYLE_SPINNER);
}
In AsyncTask I just show the ProgressDialog:
protected void onPreExecute() {
super.onPreExecute();
loadingProgress.show();
}
The result is this:
Blank, nothing... Doesn't matter if it's simulator or device... Any ideas why? Your help is appreciated.
I've tested your code and saw the "Loading" string is displayed very well.
Seeing that your screenshot has the space of textview, i think it will be the problem of text color or theme.
If setMessage works, you can detect in "Dump View Hierarchy for UI Automator" tool of device tab in Eclipse like following screenshot.
Here is the code I am using in my app. It works well -- a spinner with a message. The main differences between what you show and this are: 1) I'm using a DialogFragment; 2) I don't call setProgressStyle. Not sure which (if either) of these matter, but this code definitely is working for me.
public void showProgressDialog(int stringResId, boolean isCancelable) {
Bundle arguments = new Bundle();
arguments.putString(EXTRA_MESSAGE, getString(stringResId));
arguments.putBoolean(EXTRA_CANCELABLE, isCancelable);
DialogFragment fragment = new ProgressDialogFragment();
fragment.setArguments(arguments);
showDialog(fragment);
}
public static class ProgressDialogFragment extends DialogFragment {
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Bundle arguments = getArguments();
String message = arguments.getString(EXTRA_MESSAGE, null);
boolean isCancelable = arguments.getBoolean(EXTRA_CANCELABLE, true);
ProgressDialog dialog = new ProgressDialog(getActivity());
dialog.setIndeterminate(true);
if (message != null) {
dialog.setMessage(message);
}
dialog.setCancelable(isCancelable);
dialog.setCanceledOnTouchOutside(isCancelable);
return dialog;
}
}

How to change Show/Remove Dialog and onPrepareDialog for DialogFragments

I'm working on an Android project. I need to use Android 1.6 or above.
My project was working, but now it is showing me some warnings about Dialogs like
"The method dismissDialog(int) from the type Activity is deprecated"
"The method showDialog(int) from the type Activity is deprecated", etc.
So I want to "update" my project to solve these warnings.
I have read and made some test projects to learn about Fragments and DialogFragment.
I have created my own ProgressDialog and I want to use it on my real project, but I have some problems.
public class MyProgressDialog extends DialogFragment {
public MyProgressDialog(){
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Context context = getActivity();
ProgressDialog dialog = new ProgressDialog(context);
Resources resources = context.getResources();
String message = resources.getText(R.string.wait).toString();
dialog.setMessage(message);
dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
return dialog;
}
}
Earlier in my project, I created the ProgressDialog and then, in onPrepareDialog() method, I called an AsyncTask to connect the server, downloaded the data, etc. Then in onPostExecute of the AsyncTask, I dismissed the ProgressDialog and started the new Activity. But now I can't do that because onPrepareDialog is deprecated.
Calling ActionAsyncTask on onPrepareDialog of Activy
#Override
protected void onPrepareDialog(int id, Dialog dialog) {
switch(id){
case Constants.PROGRESS_DIALOG:
new ActionAsyncTask().execute();
break;
}
}
onPostExecute of ActionAsyncTask
#Override
protected void onPostExecute(Integer result) {
dismissDialog(Constants.PROGRESS_DIALOG);
}
How can solve this? What is the right way to do this? I want to write the best code for this, the most efficient code.
Thanks.

Prevent dialog dismissal on screen rotation in Android

I am trying to prevent dialogs built with Alert builder from being dismissed when the Activity is restarted.
If I overload the onConfigurationChanged method I can successfully do this and reset the layout to correct orientation but I lose sticky text feature of edittext. So in solving the dialog problem I have created this edittext problem.
If I save the strings from the edittext and reassign them in the onCofiguration change they still seem to default to initial value not what was entered before rotation. Even if I force an invalidate does seem to update them.
I really need to solve either the dialog problem or the edittext problem.
Thanks for the help.
The best way to avoid this problem nowadays is by using a DialogFragment.
Create a new class which extends DialogFragment. Override onCreateDialog and return your old Dialog or an AlertDialog.
Then you can show it with DialogFragment.show(fragmentManager, tag).
Here's an example with a Listener:
public class MyDialogFragment extends DialogFragment {
public interface YesNoListener {
void onYes();
void onNo();
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if (!(activity instanceof YesNoListener)) {
throw new ClassCastException(activity.toString() + " must implement YesNoListener");
}
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return new AlertDialog.Builder(getActivity())
.setTitle(R.string.dialog_my_title)
.setMessage(R.string.dialog_my_message)
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
((YesNoListener) getActivity()).onYes();
}
})
.setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
((YesNoListener) getActivity()).onNo();
}
})
.create();
}
}
And in the Activity you call:
new MyDialogFragment().show(getSupportFragmentManager(), "tag"); // or getFragmentManager() in API 11+
This answer helps explain these other three questions (and their answers):
Android Best way of avoid Dialogs to dismiss after a device rotation
Android DialogFragment vs Dialog
How can I show a DialogFragment using compatibility package?
// Prevent dialog dismiss when orientation changes
private static void doKeepDialog(Dialog dialog){
WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
lp.copyFrom(dialog.getWindow().getAttributes());
lp.width = WindowManager.LayoutParams.WRAP_CONTENT;
lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
dialog.getWindow().setAttributes(lp);
}
public static void doLogout(final Context context){
final AlertDialog dialog = new AlertDialog.Builder(context)
.setIcon(android.R.drawable.ic_dialog_alert)
.setTitle(R.string.titlelogout)
.setMessage(R.string.logoutconfirm)
.setPositiveButton("Yes", new DialogInterface.OnClickListener()
{
#Override
public void onClick(DialogInterface dialog, int which) {
...
}
})
.setNegativeButton("No", null)
.show();
doKeepDialog(dialog);
}
If you're changing the layout on orientation change I wouldn't put android:configChanges="orientation" in your manifest because you're recreating the views anyway.
Save the current state of your activity (like text entered, shown dialog, data displayed etc.) using these methods:
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
}
That way the activity goes through onCreate again and afterwards calls the onRestoreInstanceState method where you can set your EditText value again.
If you want to store more complex Objects you can use
#Override
public Object onRetainNonConfigurationInstance() {
}
Here you can store any object and in onCreate you just have to call getLastNonConfigurationInstance(); to get the Object.
Just add android:configChanges="orientation" with your activity
element in AndroidManifest.xml
Example:
<activity
android:name=".YourActivity"
android:configChanges="orientation"
android:label="#string/app_name"></activity>
A very easy approach is to create the dialogs from the method onCreateDialog() (see note below). You show them through showDialog(). This way, Android handles the rotation for you and you do not have to call dismiss() in onPause() to avoid a WindowLeak and then you neither have to restore the dialog. From the docs:
Show a dialog managed by this activity. A call to onCreateDialog(int, Bundle) will be made with the same id the first time this is called for a given id. From thereafter, the dialog will be automatically saved and restored.
See Android docs showDialog() for more info. Hope it helps somebody!
Note: If using AlertDialog.Builder, do not call show() from onCreateDialog(), call create() instead. If using ProgressDialog, just create the object, set the parameters you need and return it. In conclusion, show() inside onCreateDialog() causes problems, just create de Dialog instance and return it. This should work! (I have experienced issues using showDialog() from onCreate() -actually not showing the dialog-, but if you use it in onResume() or in a listener callback it works well).
This question was answered a long time ago.
Yet this is non-hacky and simple solution I use for myself.
I did this helper class for myself, so you can use it in your application too.
Usage is:
PersistentDialogFragment.newInstance(
getBaseContext(),
RC_REQUEST_CODE,
R.string.message_text,
R.string.positive_btn_text,
R.string.negative_btn_text)
.show(getSupportFragmentManager(), PersistentDialogFragment.TAG);
Or
PersistentDialogFragment.newInstance(
getBaseContext(),
RC_EXPLAIN_LOCATION,
"Dialog title",
"Dialog Message",
"Positive Button",
"Negative Button",
false)
.show(getSupportFragmentManager(), PersistentDialogFragment.TAG);
public class ExampleActivity extends Activity implements PersistentDialogListener{
#Override
void onDialogPositiveClicked(int requestCode) {
switch(requestCode) {
case RC_REQUEST_CODE:
break;
}
}
#Override
void onDialogNegativeClicked(int requestCode) {
switch(requestCode) {
case RC_REQUEST_CODE:
break;
}
}
}
Definitely, the best approach is by using DialogFragment.
Here is mine solution of wrapper class that helps to prevent different dialogs from being dismissed within one Fragment (or Activity with small refactoring). Also, it helps to avoid massive code refactoring if for some reasons there are a lot of AlertDialogs scattered among the code with slight differences between them in terms of actions, appearance or something else.
public class DialogWrapper extends DialogFragment {
private static final String ARG_DIALOG_ID = "ARG_DIALOG_ID";
private int mDialogId;
/**
* Display dialog fragment.
* #param invoker The fragment which will serve as {#link AlertDialog} alert dialog provider
* #param dialogId The ID of dialog that should be shown
*/
public static <T extends Fragment & DialogProvider> void show(T invoker, int dialogId) {
Bundle args = new Bundle();
args.putInt(ARG_DIALOG_ID, dialogId);
DialogWrapper dialogWrapper = new DialogWrapper();
dialogWrapper.setArguments(args);
dialogWrapper.setTargetFragment(invoker, 0);
dialogWrapper.show(invoker.getActivity().getSupportFragmentManager(), null);
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mDialogId = getArguments().getInt(ARG_DIALOG_ID);
}
#NonNull
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return getDialogProvider().getDialog(mDialogId);
}
private DialogProvider getDialogProvider() {
return (DialogProvider) getTargetFragment();
}
public interface DialogProvider {
Dialog getDialog(int dialogId);
}
}
When it comes to Activity you can invoke getContext() inside onCreateDialog(), cast it to the DialogProvider interface and request a specific dialog by mDialogId. All logic to dealing with a target fragment should be deleted.
Usage from fragment:
public class MainFragment extends Fragment implements DialogWrapper.DialogProvider {
private static final int ID_CONFIRMATION_DIALOG = 0;
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
Button btnHello = (Button) view.findViewById(R.id.btnConfirm);
btnHello.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
DialogWrapper.show(MainFragment.this, ID_CONFIRMATION_DIALOG);
}
});
}
#Override
public Dialog getDialog(int dialogId) {
switch (dialogId) {
case ID_CONFIRMATION_DIALOG:
return createConfirmationDialog(); //Your AlertDialog
default:
throw new IllegalArgumentException("Unknown dialog id: " + dialogId);
}
}
}
You can read the complete article on my blog How to prevent Dialog being dismissed? and play with the source code.
It seems that this is still an issue, even when "doing everything right" and using DialogFragment etc.
There is a thread on Google Issue Tracker which claims that it is due to an old dismiss message being left in the message queue. The provided workaround is quite simple:
#Override
public void onDestroyView() {
/* Bugfix: https://issuetracker.google.com/issues/36929400 */
if (getDialog() != null && getRetainInstance())
getDialog().setDismissMessage(null);
super.onDestroyView();
}
Incredible that this is still needed 7 years after that issue was first reported.
You can combine the Dialog's onSave/onRestore methods with the Activity's onSave/onRestore methods to keep the state of the Dialog.
Note: This method works for those "simple" Dialogs, such as displaying an alert message. It won't reproduce the contents of a WebView embedded in a Dialog. If you really want to prevent a complex dialog from dismissal during rotation, try Chung IW's method.
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
myDialog.onRestoreInstanceState(savedInstanceState.getBundle("DIALOG"));
// Put your codes to retrieve the EditText contents and
// assign them to the EditText here.
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// Put your codes to save the EditText contents and put them
// to the outState Bundle here.
outState.putBundle("DIALOG", myDialog.onSaveInstanceState());
}
I had a similar problem: when the screen orientation changed, the dialog's onDismiss listener was called even though the user didn't dismiss the dialog. I was able to work around this by instead using the onCancel listener, which triggered both when the user pressed the back button and when the user touched outside of the dialog.
In case nothing helps, and you need a solution that works, you can go on the safe side, and each time you open a dialog save its basic info to the activity ViewModel (and remove it from this list when you dismiss dialog). This basic info could be dialog type and some id (the information you need in order to open this dialog). This ViewModel is not destroyed during changes of Activity lifecycle. Let's say user opens a dialog to leave a reference to a restaurant. So dialog type would be LeaveReferenceDialog and the id would be the restaurant id. When opening this dialog, you save this information in an Object that you can call DialogInfo, and add this object to the ViewModel of the Activity. This information will allow you to reopen the dialog when the activity onResume() is being called:
// On resume in Activity
override fun onResume() {
super.onResume()
// Restore dialogs that were open before activity went to background
restoreDialogs()
}
Which calls:
fun restoreDialogs() {
mainActivityViewModel.setIsRestoringDialogs(true) // lock list in view model
for (dialogInfo in mainActivityViewModel.openDialogs)
openDialog(dialogInfo)
mainActivityViewModel.setIsRestoringDialogs(false) // open lock
}
When IsRestoringDialogs in ViewModel is set to true, dialog info will not be added to the list in view model, and it's important because we're now restoring dialogs which are already in that list. Otherwise, changing the list while using it would cause an exception. So:
// Create new dialog
override fun openLeaveReferenceDialog(restaurantId: String) {
var dialog = LeaveReferenceDialog()
// Add id to dialog in bundle
val bundle = Bundle()
bundle.putString(Constants.RESTAURANT_ID, restaurantId)
dialog.arguments = bundle
dialog.show(supportFragmentManager, "")
// Add dialog info to list of open dialogs
addOpenDialogInfo(DialogInfo(LEAVE_REFERENCE_DIALOG, restaurantId))
}
Then remove dialog info when dismissing it:
// Dismiss dialog
override fun dismissLeaveReferenceDialog(Dialog dialog, id: String) {
if (dialog?.isAdded()){
dialog.dismiss()
mainActivityViewModel.removeOpenDialog(LEAVE_REFERENCE_DIALOG, id)
}
}
And in the ViewModel of the Activity:
fun addOpenDialogInfo(dialogInfo: DialogInfo){
if (!isRestoringDialogs){
val dialogWasInList = removeOpenDialog(dialogInfo.type, dialogInfo.id)
openDialogs.add(dialogInfo)
}
}
fun removeOpenDialog(type: Int, id: String) {
if (!isRestoringDialogs)
for (dialogInfo in openDialogs)
if (dialogInfo.type == type && dialogInfo.id == id)
openDialogs.remove(dialogInfo)
}
You actually reopen all the dialogs that were open before, in the same order. But how do they retain their information? Each dialog has a ViewModel of its own, which is also not destroyed during the activity lifecycle. So when you open the dialog, you get the ViewModel and init the UI using this ViewModel of the dialog as always.
Yes, I agree with the solution of using DialogFragment given by #Brais Gabin, just want to suggest some changes to the solution given by him.
While defining our custom class that extends DialogFragment, we require some interfaces to manage the actions ultimately by the activity or the fragment that has invoked the dialog. But setting these listener interfaces in the onAttach(Context context) method may sometimes cause ClassCastException that may crash the app.
So to avoid this exception, we can create a method to set the listener interfaces and call just it after creating the object of the dialog fragment.
Here is a sample code that could help you understand more-
AlertRetryDialog.class
public class AlertRetryDialog extends DialogFragment {
public interface Listener{
void onRetry();
}
Listener listener;
public void setListener(Listener listener)
{
this.listener=listener;
}
#NonNull
#Override
public Dialog onCreateDialog(#Nullable Bundle savedInstanceState) {
AlertDialog.Builder builder=new AlertDialog.Builder(getActivity());
builder.setMessage("Please Check Your Network Connection").setPositiveButton("Retry", new
DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
//Screen rotation will cause the listener to be null
//Always do a null check of your interface listener before calling its method
if(listener!=null&&listener instanceof HomeFragment)
listener.onRetry();
}
}).setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
return builder.create();
}
}
And in the Activity or in the Fragment you call-
AlertRetryDialog alertRetryDialog = new AlertRetryDialog();
alertRetryDialog.setListener(HomeFragment.this);
alertRetryDialog.show(getFragmentManager(), "tag");
And implement the methods of your listener interface in your Activity or the Fragment-
public class YourActivity or YourFragment implements AlertRetryDialog.Listener{
//here's my listener interface's method
#Override
public void onRetry()
{
//your code for action
}
}
Always make sure that you do a null check of the listener interfaces before calling any of its methods to prevent NullPointerException (Screen rotation will cause the listener interfaces to be null).
Please do let me know if you find this answer helpful. Thank You.
Just use
ConfigurationChanges = Android.Content.PM.ConfigChanges.Orientation | Android.Content.PM.ConfigChanges.ScreenSize
and app will know how to handle rotation and screen size.

Categories

Resources