John Smith
John Smith

Reputation: 307

AlertDialog box in a non-Activity view

I am trying to modify an existing application to include an alert dialog in a View class. The alert dialog works fine if I run it in an activity (another application). However, when I add it to a View class in the existing application it fails with a (Unable to add window -- token null) error. I tried "this" (current View), getContext(), and getApplicationContext(). I read that not all Views are attached to an activity, thus getContext fails. Are there any alternatives to this? Can I use some sort of generic alert dialog that does not rely on an Activity?

        Dialog dialog;
        final ArrayList<Integer> itemsSelected = new ArrayList<Integer>();
        AlertDialog.Builder builder = new AlertDialog.Builder(context);
        builder.setTitle("Selection:");
        builder.setMultiChoiceItems(items.toArray(new String[items.size()]), null,
                new DialogInterface.OnMultiChoiceClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int selectedItemId,
                            boolean isSelected) {
                        if (isSelected) {
                            itemsSelected.add(selectedItemId);
                        } else if (itemsSelected.contains(selectedItemId)) {
                            itemsSelected.remove(Integer.valueOf(selectedItemId));
                        }   
                    }   
                })  
        .setPositiveButton("OK", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int id) {
                // OK
            }   
        })  
        .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int id) {
                // Cancel
            }   
        }); 
        dialog = builder.create();
        dialog.show();

Upvotes: 0

Views: 342

Answers (1)

TheWanderer
TheWanderer

Reputation: 17824

No, but you can use an Activity as a sort of proxy. Here's an example class (in Kotlin):

/**
 * Activity used solely for showing a dialog outside of an activity context
 */
class DialogActivity : AppCompatActivity() {
    companion object {
        const val EXTRA_TITLE = "title"
        const val EXTRA_MESSAGE = "message"
        const val EXTRA_YES_RES = "yes_res"
        const val EXTRA_NO_RES = "no_res"

        var yesAct: DialogInterface.OnClickListener? = null
        var noAct: DialogInterface.OnClickListener? = null
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        try {
            val title = intent.getIntExtra(EXTRA_TITLE, android.R.string.untitled)
            val message = intent.getIntExtra(EXTRA_MESSAGE, android.R.string.untitled)
            val yesRes = intent.getIntExtra(EXTRA_YES_RES, 0)
            val noRes = intent.getIntExtra(EXTRA_NO_RES, 0)

            val builder = AlertDialog.Builder(this)
            builder.setTitle(title)
            builder.setMessage(message)

            if (yesRes != 0) builder.setPositiveButton(yesRes, yesAct)
            if (noRes != 0) builder.setNegativeButton(noRes, noAct)

            builder.setOnCancelListener {
                finish()
            }

            builder.setOnDismissListener {
                finish()
            }

            builder.show()

        } catch (e: Exception) {
            e.printStackTrace()
            finish()
        }
    }

    override fun onDestroy() {
        super.onDestroy()

        yesAct = null
        noAct = null
    }

    /**
     * Builder class
     * Create a DialogActivity instance using this
     */
    class Builder(private val context: Context) {
        var title = android.R.string.untitled
        var message = android.R.string.untitled
        var yesRes = android.R.string.yes
        var noRes = android.R.string.no

        var yesAction: DialogInterface.OnClickListener? = null
        var noAction: DialogInterface.OnClickListener? = null

        fun start() {
            val intent = Intent(context, DialogActivity::class.java)
            intent.putExtra(EXTRA_TITLE, title)
            intent.putExtra(EXTRA_MESSAGE, message)
            intent.putExtra(EXTRA_YES_RES, yesRes)
            intent.putExtra(EXTRA_NO_RES, noRes)
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)

            yesAct = yesAction
            noAct = noAction

            context.startActivity(intent)
        }
    }
}

To use it, use

DialogActivity.Builder builder = new DialogActivity.Builder(context);
builder.title = whatever;
builder.message = whatever;
//etc
builder.start();

If you need to set listeners for the yes/no actions, unfortunately, I couldn't find any other way but to have this be done through the static variables, yesAct and noAct.

Use this as the theme for the Activity:

<style name="AppTheme.Null" parent="AppTheme">
    <item name="android:windowAnimationStyle">@null</item>
    <item name="android:activityOpenEnterAnimation">@null</item>
    <item name="android:activityOpenExitAnimation">@null</item>
    <item name="android:activityCloseEnterAnimation">@null</item>
    <item name="android:activityCloseExitAnimation">@null</item>
    <item name="android:windowIsTranslucent">true</item>
    <item name="android:windowBackground">@android:color/transparent</item>
    <item name="android:windowContentOverlay">@null</item>
    <item name="android:windowNoTitle">true</item>
    <item name="windowNoTitle">true</item>
    <item name="android:windowIsFloating">true</item>
    <item name="android:backgroundDimEnabled">false</item>
</style>

Upvotes: 1

Related Questions