1
votes

I am trying to create a Dialog Box from an Non activity Class.

This is my Code

  public static void ShowDialogBox(final Context con, final Listener list) {
        AlertDialog.Builder dlgAlert = new AlertDialog.Builder(con);
        dlgAlert.setMessage("TEXT");
        dlgAlert.setTitle("TEXT");
        dlgAlert.setPositiveButton("TEXT"),
                new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {

                    }
                });
        dlgAlert.setCancelable(false);
        dlgAlert.create().show(); // THIS LINE GIVES ME AN ERROR
    }

This is the error which I am getting

android.view.WindowManager$BadTokenException: at android.view.ViewRootImpl.setView (ViewRootImpl.java:574) at android.view.WindowManagerGlobal.addView (WindowManagerGlobal.java:282) at android.view.WindowManagerImpl.addView (WindowManagerImpl.java:85)
at android.app.Dialog.show (Dialog.java:298) at PACKAGE NAME AND CLASS PACKAGE NAME AND CLASS at PACKAGE NAME AND CLASS PACKAGE NAME AND CLASS at PACKAGE NAME AND CLASS.onBackPressed (Class.java:95) at android.app.Activity.onKeyUp (Activity.java:2465) at android.view.KeyEvent.dispatch (KeyEvent.java:2646) at android.app.Activity.dispatchKeyEvent (Activity.java:2716) at android.support.v7.internal.view.WindowCallbackWrapper.dispatchKeyEvent (WindowCallbackWrapper.java:50) at android.support.v7.app.AppCompatDelegateImplBase$AppCompatWindowCallbackBase.dispatchKeyEvent (AppCompatDelegateImplBase.java:224) at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent (PhoneWindow.java:2280) at android.view.ViewRootImpl$ViewPostImeInputStage.processKeyEvent (ViewRootImpl.java:4038) at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess (ViewRootImpl.java:4000) at android.view.ViewRootImpl$InputStage.deliver (ViewRootImpl.java:3562) at android.view.ViewRootImpl$InputStage.onDeliverToNext (ViewRootImpl.java:3615) at android.view.ViewRootImpl$InputStage.forward (ViewRootImpl.java:3581) at android.view.ViewRootImpl$AsyncInputStage.forward (ViewRootImpl.java:3698) at android.view.ViewRootImpl$InputStage.apply (ViewRootImpl.java:3589)
at android.view.ViewRootImpl$AsyncInputStage.apply (ViewRootImpl.java:3755) at android.view.ViewRootImpl$InputStage.deliver (ViewRootImpl.java:3562) at android.view.ViewRootImpl$InputStage.onDeliverToNext (ViewRootImpl.java:3615) at android.view.ViewRootImpl$InputStage.forward (ViewRootImpl.java:3581) at android.view.ViewRootImpl$InputStage.apply (ViewRootImpl.java:3589) at android.view.ViewRootImpl$InputStage.deliver (ViewRootImpl.java:3562) at android.view.ViewRootImpl$InputStage.onDeliverToNext (ViewRootImpl.java:3615) at android.view.ViewRootImpl$InputStage.forward (ViewRootImpl.java:3581) at android.view.ViewRootImpl$AsyncInputStage.forward (ViewRootImpl.java:3731) at android.view.ViewRootImpl$ImeInputStage.onFinishedInputEvent (ViewRootImpl.java:3892) at android.view.inputmethod.InputMethodManager$PendingEvent.run (InputMethodManager.java:2208) at android.view.inputmethod.InputMethodManager.invokeFinishedInputEventCallback (InputMethodManager.java:1849) at android.view.inputmethod.InputMethodManager.finishedInputEvent (InputMethodManager.java:1840) at android.view.inputmethod.InputMethodManager$ImeInputEventSender.onInputEventFinished (InputMethodManager.java:2185) at android.view.InputEventSender.dispatchInputEventFinished (InputEventSender.java:141) at android.os.MessageQueue.nativePollOnce (Native Method) at android.os.MessageQueue.next (MessageQueue.java:143) at android.os.Looper.loop (Looper.java:122) at android.app.ActivityThread.main (ActivityThread.java:5254) at java.lang.reflect.Method.invoke (Native Method) at java.lang.reflect.Method.invoke (Method.java:372) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run (ZygoteInit.java:902) at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:697)

Here is the scenario of the user

Activity A -->Opens Activity B-->User presses Back button in Activity B--> On Back button pressed a listener is sent to Activity A --> And then the Dialog Box shown is called.

3
More information on the "Non activity Class" would be helpful.Barns
@Barns Thanks. It is More of an Utility class which I have created to optimize the repetition of the Code. Also the ShowDialogBox method is called from an Activity Class. It is like Activity Class calls ShowDialogBox shows an Dialog box. This is an Class which has various other methods such as Opening an activity, Initalizing something etc etc. Please let me know if further info is needed.Rajesh K
It is probably just an typo you made while simplifying your code for your post, but you have thisdlgAlert.setMessage("TEXT); with an open quotation on "TEXT in stead of "TEXT" unless TEXT is a variable and should be just TEXT ???Barns
:: Take a look at my second answer. It provides a bit more info about the error you get. It also provides a "quick fix" to your issue. I kept my first answer with the DialogFragmen because it is the preferred way to " outsource" the AlertDialog and other dialogs.Barns
@Barns Thanks have accepted you second Answer as it suits me more. But will surely try Dialog Fragment also.Rajesh K

3 Answers

1
votes

The issue you are having with the attempt to build your AlertDialog in a separate class is you are passing the AlertDialog the Context of your Activity. You get the error because the AlertDialog requires the WindowManager from the Activity which has the layout--not the Context. This is because Activit extends Context... not the other way around.

In order to make your code work you need to provide the AlertDialog.Builder access to the Activity. So change your code to something like this:

public class TestDialog {

    private static final String TAG = TestDialog.class.getSimpleName();

    Activity mActivity;

    public TestDialog(Activity activity){
        mActivity = activity;
    }


    public void showDialog(){
        AlertDialog.Builder b = new AlertDialog.Builder(mActivity);
        b.setTitle("Title");
        b.setMessage("message");
        b.setPositiveButton("OK", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                Log.e(TAG, "showDialog : onClick");
            }
        });
        b.create().show();
    }
}

Now you can call the AlertDialog from an Activity lets say in this case MainActivity like this:

TestDialog testDialog = new TestDialog(MainActivity.this);
testDialog.showDialog();

I have not tried this from a Fragment, so I do not know whether this works from a Fragment or whether you will continue to have issues with certain devices. For those reasons, I (and Google!) still strongly suggest that you use the DialogFragment instead, because is has been especially designed for this scenario. Take a look at the Google Docs.:

https://developer.android.com/guide/topics/ui/dialogs

1
votes

I generally prefer using DialogFragment instead of what you attempted in order to cut down on repetition. Here is an example of a DialogFragment with a custom layout which I have called R.layout.fragment_alert_dialog:

public class AlertDialogFragment extends DialogFragment {

    private static final String ARG_TITLE = "title";
    private static final String ARG_MESSAGE = "message";

    private String title;
    private String message;
    boolean endSuccess = false;

    private AlertFinishedDialogListener mListener;

    public AlertDialogFragment() {
    }

    public static AlertDialogFragment newInstance(String title, String message) {
        AlertDialogFragment fragment = new AlertDialogFragment();
        Bundle args = new Bundle();
        args.putString(ARG_TITLE, title);
        args.putString(ARG_MESSAGE, message);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments() != null) {
            title = getArguments().getString(ARG_TITLE);
            message = getArguments().getString(ARG_MESSAGE);
        }
    }

    @Override
    public Dialog onCreateDialog(Bundle saveIntsanceState){

        final Context context = getActivity();

        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());

        LayoutInflater inflater = getActivity().getLayoutInflater();

        View rootView = inflater.inflate(R.layout.fragment_alert_dialog, null, false);
        final TextView titleView = (TextView)rootView.findViewById(R.id.tvTitle);
        final TextView messView = (TextView)rootView.findViewById(R.id.tvMessage);

        titleView.setText(title);
        messView.setText(message);

        builder.setView(rootView)
//                .setTitle(title)
                .setPositiveButton(R.string.ok_button_dialog_title, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {

                        endSuccess = true;
                        if(mListener == null) mListener = (AlertFinishedDialogListener) context;
                        mListener.onAlertFinishedDialog();
                    }
                });
        return builder.create();
    }


    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        try {
            if(mListener == null) mListener = (AlertFinishedDialogListener) context;
        }
        catch (Exception ex){
            throw new RuntimeException(context.toString()
                    + " must implement OnFragmentInteractionListener");
        }
    }

    @Override
    public void onDetach() {
        super.onDetach();
        mListener = null;
    }

    public interface AlertFinishedDialogListener {
        void onAlertFinishedDialog();
    }
}

It incorporates a Listener just in case you need to be notified when the DialogFragment is completed.

First you need to implement the callback: implements AlertDialogFragment.AlertFinishedDialogListener{

In order to call the AlertDialogFragment you do this from your Activity (also Fragment if necessary).

private void startAlertDialogFragment(String title, String mess){
    AlertDialogFragment alert = AlertDialogFragment.newInstance(title, mess);
    alert.show(getFragmentManager(), "alertDialogFragment132");
}

@Override
public void onAlertFinishedDialog() {
    Log.e(TAG, "onAlertFinishedDialog");
}
1
votes

Problem

You can show dialogs from activity context only. except TYPE_SYSTEM_ALERT or TYPE_APPLICATION_OVERLAY, which is not recommended if your app does not show emergency notifications to user.

Solution

If you have activity context available then you can show dialog from any class like service, broadcast receiver, or even any class you imagine.

Here is my workaround that can show dialog from any class like i said.

Here is a snippet what i do to show dialog from any class. (Could it be more simpler!)

import android.app.Dialog;
import android.content.DialogInterface;

public class SampleClass {
    void anyMethod() {
        Dialog dialog = ApplicationContext.getInstance().showDialog("title", "yourMessage", "Cancel", "Ok", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                // TODO: handle button 1 clicked 
            }
        }, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                // TODO: handle button 2 clicked
            }
        });
    }
}

Now you will implement to make this work.

1. make application class which you will register in android manifest application tag

  <application
    android:name=".ApplicationContext"
    ...
    >
      ...
  </application>

2. In this application class you will hold live activity object. that will further useful for showing dialog.

ApplicationContext.java

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Application;
import android.content.DialogInterface;


public class ApplicationContext extends Application {
    private static ApplicationContext mInstance;
    private Activity liveActivity;

    @Override
    public void onCreate() {
        super.onCreate();
        mInstance = this;
    }

    @Override
    public void onTerminate() {
        super.onTerminate();
        mInstance = null;
    }

    public static synchronized ApplicationContext getInstance() {
        return mInstance;
    }

    public Activity getLiveActivity() {
        return liveActivity;
    }

    public void setLiveActivity(Activity liveActivity) {
        this.liveActivity = liveActivity;
    }

    /*
     * Show Dialog with Title, Message, Button1, Button2 with Button1 and Button2 Listener
     */
    public AlertDialog showDialog(String title, String msg,
                                  String btn1, String btn2,
                                  DialogInterface.OnClickListener listener1,
                                  DialogInterface.OnClickListener listener2) {
        if (liveActivity == null) return null;
        AlertDialog.Builder builder = new AlertDialog.Builder(liveActivity)
                .setTitle(title)
                .setMessage(msg)
                .setCancelable(false)
                .setPositiveButton(btn1, listener1);
        if (btn2 != null)
            builder.setNegativeButton(btn2, listener2);

        AlertDialog alert = builder.create();
        alert.show();
        return alert;
    }
}

Just one more step

3. You will extend all your activity by this base activity class (You can edit your base activity if you already have one.)

import android.support.v7.app.AppCompatActivity;

public class BaseActivity extends AppCompatActivity {
    @Override
    protected void onResume() {
        super.onResume();
        ApplicationContext.getInstance().setLiveActivity(this);
    }

    @Override
    protected void onPause() {
        super.onPause();
        ApplicationContext.getInstance().setLiveActivity(null);
    }
}

Here you go !!!