11

I want to show an Alert Dialog via AlertDialogManager class to a non-activity class DeviceAdminReceiverSample's method onDisabled, but whenever I call alertDialog via that method it generates error with following text

Error

06-12 12:01:19.923: E/AndroidRuntime(468): FATAL EXCEPTION: main
06-12 12:01:19.923: E/AndroidRuntime(468): java.lang.RuntimeException: Unable to start           
receiver com.android.remotewipedata.DeviceAdminReceiverSample:   
android.view.WindowManager$BadTokenException: Unable to add window -- token null is not   
for an application

I know the issue is with context thing but I don't know what to put there so that it work, I tried this, getApplicationContext() but all vain. My code for both classes is below

AlertDialogManager

public class AlertDialogManager {

public void showAlertDialog(Context context, String title, String message,
        Boolean status) {
    final AlertDialog alertDialog = new AlertDialog.Builder(context).create();
    alertDialog.setTitle(title);
    alertDialog.setMessage(message);

    if (status != null)
        alertDialog.setButton("OK", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int which) {
                alertDialog.dismiss();
            }
        });
    alertDialog.show();
}

}

DeviceAdminReceiverSample

public class DeviceAdminReceiverSample extends DeviceAdminReceiver {
static final String TAG = "DeviceAdminReceiver";
AlertDialogManager alert = new AlertDialogManager();

/** Called when this application is no longer the device administrator. */
@Override
public void onDisabled(Context context, Intent intent) {
    super.onDisabled(context, intent);
    Toast.makeText(context, R.string.device_admin_disabled,
            Toast.LENGTH_LONG).show();
    // intent.putExtra("dialogMessage", "Device admin has been disabled");
    // intent.setClass(context, DialogActivity.class);
    // intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    // context.startActivity(intent);
    alert.showAlertDialog(context, "Alert",
            "Device admin has been disabled", true);
}
Saqib
  • 1,120
  • 5
  • 22
  • 40

7 Answers7

42

Just add this before your alertDialog.show();

alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);

or try following if above didn't work:

alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_PANEL); 

and use this permission:

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
kevoroid
  • 5,052
  • 5
  • 34
  • 43
Eliran Kuta
  • 4,148
  • 3
  • 24
  • 28
  • 1
    Thanks, it's working. Your answer should be vote up. – VAdaihiep Aug 31 '15 at 03:44
  • 2
    Very good finding. Thank you. Adding to this solution, as per documentation `Allows an application to open windows using the type TYPE_SYSTEM_ALERT, shown on top of all other applications. Very few applications should use this permission; these windows are intended for system-level interaction with the user.` So we also need to take care while using this solution. – Pankaj Kumar Oct 09 '15 at 06:33
  • Had the same issue, resolved it using this way. Thanks – darthvading Oct 20 '15 at 10:01
  • 1
    Bad solution, because hardBackButton will close activity, but not dialog. – Dmitry Nelepov Mar 18 '16 at 15:16
  • 3
    I think we should not use this.. As it will show alert in complete android system no matter in which application user is.. and second on touching outside the alert ,it is not dismiss. and if you want to do any action for your app then it could be crash – Jishant Apr 20 '16 at 11:30
15

The problem is 'You can show AlertDialogs from Activity only'. This is not an issue of context.

Although this is not a good idea to show dialog from receiver (better is to use Notification), But if you want to do so you can create an Activity as dialog and show

Community
  • 1
  • 1
Pankaj Kumar
  • 81,967
  • 29
  • 167
  • 186
  • 1
    Yes, indeed, it's impossible to show AlertDialog outside of Activity, here is how I solved this issue: https://medium.com/@vapoyan/android-show-allertdialog-before-the-application-starts-80588d6f2dda – Viktor Apoyan Aug 10 '20 at 06:49
10

If you always want to get the current activity from anywhere in the app you can register an ActivityLifecycleCallback on your Application instance.

Here's an untested implementation that might get you closer.

public class TestApp extends Application {

    private WeakReference<Activity> mActivity = null;

    @Override
    public void onCreate() {
        super.onCreate();
        registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
            @Override
            public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
                mActivity = new WeakReference<Activity>(activity);
            }

            @Override
            public void onActivityDestroyed(Activity activity) {
                mActivity.clear();
            }

            /** Unused implementation **/
            @Override
            public void onActivityStarted(Activity activity) {}

            @Override
            public void onActivityResumed(Activity activity) {}
            @Override
            public void onActivityPaused(Activity activity) {}

            @Override
            public void onActivityStopped(Activity activity) {}

            @Override
            public void onActivitySaveInstanceState(Activity activity, Bundle outState) {}
        });
    }

    public Activity getCurrentActivity() {
        return mActivity.get();
    }

}

Then to use this throughout your app you would do some call like this ...

Activity activity = ((TestApp)getApplicationContext()).getCurrentActivity(); 

The advantages are you can always keep track of your current activity, however its a little too overkill for just handling Dialogs from within the Activity.

Chris Sullivan
  • 576
  • 5
  • 10
1

call this method in activity class

public static void showAlert(Activity activity, String message) {

        TextView title = new TextView(activity);
        title.setText("Title");
        title.setPadding(10, 10, 10, 10);
        title.setGravity(Gravity.CENTER);
        title.setTextColor(Color.WHITE);
        title.setTextSize(20);

        AlertDialog.Builder builder = new AlertDialog.Builder(activity);
        // builder.setTitle("Title");
        builder.setCustomTitle(title);
        // builder.setIcon(R.drawable.alert_36);

        builder.setMessage(message);

        builder.setCancelable(false);
        builder.setNegativeButton("OK", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int id) {
                dialog.cancel();

            }

        });

        AlertDialog alert = builder.create();
        alert.show();
    }
Sunil Kumar
  • 7,086
  • 4
  • 32
  • 50
0

Here's what I made and use:

myDialog.java:

import android.app.Activity;
import android.content.DialogInterface;
import android.support.v7.app.AlertDialog;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;

public class myDialog {
    private Activity mActivity;

    myDialog(Activity a) {
        this.mActivity = a;
    }

    @SuppressWarnings("InflateParams")
    public void build(String title, String msg) {
        LayoutInflater inflater = LayoutInflater.from(mActivity);
        View subView = inflater.inflate(R.layout.dialog_box_text, null);
        final TextView message = subView.findViewById(R.id.message);
        message.setText(msg);
        AlertDialog.Builder builder = new AlertDialog.Builder(mActivity);
        builder.setTitle(title);
        builder.setView(subView);
        builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.dismiss();
            }
        });
        AlertDialog alert = builder.create();
        alert.show();
    }
}

dialog_box_text.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:weightSum="1"
    android:orientation="horizontal">
    <TextView
        android:id="@+id/message"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="  "
        android:maxLines="1"
        android:textColor="@color/colorBlack" />
</LinearLayout>

Example code:

public class MainActivity extends AppCompatActivity {
    private myDialog md;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        md = new myDialog(this);

...

        md.build("Title", "Message");
linuxgnuru
  • 142
  • 5
-2

You can define a public Context var in the MainActivity with initial value (this); As show here:

public class MainActivity< alertdail > extends AppCompatActivity {

    ////////////////////////////////////////////////////////////
    //Public var refers to Main Activity:
    Context mainActivity = this;
    ////////////////////////////////////////////////////////////

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate ( savedInstanceState );
        setContentView ( R.layout.activity_main );
        AlertDialogManager alert  =new AlertDialogManager ();
alert.showAlertDialog ( this,"Title","Message",true );


     }


    public class AlertDialogManager {

        public void showAlertDialog(Context context, String title, String message,
                                    Boolean status) {
            final AlertDialog alertDialog = new AlertDialog.Builder ( mainActivity ).create ( );
            alertDialog.setTitle ( title );
            alertDialog.setMessage ( message );

            if (status != null)
                alertDialog.setButton ( "OK", new DialogInterface.OnClickListener ( ) {
                    public void onClick(DialogInterface dialog, int which) {
                        alertDialog.dismiss ( );
                    }
                } );
            alertDialog.show ( );
        }

        public void showAlertDialog(Context c) {
        }
    }


    public class DeviceAdminReceiverSample extends DeviceAdminReceiver {
        static final String TAG = "DeviceAdminReceiver";
        AlertDialogManager alert = new AlertDialogManager ( );

        /**
         * Called when this application is no longer the device administrator.
         */
        @Override
        public void onDisabled(Context context, Intent intent) {
            super.onDisabled ( context, intent );
            Toast.makeText ( context, R.string.device_admin_disabled,
                    Toast.LENGTH_LONG ).show ( );
            // intent.putExtra("dialogMessage", "Device admin has been disabled");
            // intent.setClass(context, DialogActivity.class);
            // intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            // context.startActivity(intent);
            alert.showAlertDialog ( context, "Alert",
                    "Device admin has been disabled", true );
        }
    }
}
MJT7
  • 23
  • 2
-3

Here's a quick method of properly performing this task that has done the job for me. Basically, what you would do is just create a new thread.


  1. Declare a public and static variable with a type that matches the original activity class.

    public static Activity1 activity;

Activity1 is the class that the variable resides in.


  1. Upon calling the method onCreate();, set the variable to be equal to the context of the activity, otherwise known as this.

Example:

@Override 
    protected void onCreate( Bundle savedInstanceState ) {
    super.onCreate( savedInstanceState );
    activity = this;
}


3. Since we now have the context of the activity, we can use it to create a function with an alert dialog by using the runOnUiThread(); method inside of the function that will call the alert dialog. We would use a new Runnable() for the runnable action required for runOnUiThread();, and to have the alert dialog actually open, we would override the run function of a runnable item and place the code for the alert dialog in there.

Example function:

public static void exampleDialog(){
Activity1.activity.runOnUiThread(new Runnable){
@Override
    public void run(){
    //alert dialog code goes here.  For the context, use the activity variable from Activity1.
        }
    }
}

Hope this helps :)

lighthouse64
  • 49
  • 2
  • 10