I saw much people wanted such a feature, me either. These days I've
google-ed around and tried several ways to get it right, finally ended
up with following solution. No thread synchronization, no dialog-
themed activities, really simple and straight.

Some useful resource and background,
http://groups.google.com/group/android-developers/browse_thread/thread/5616a220c97a13fc/5e722e601f33d884#5e722e601f33d884
http://stackoverflow.com/questions/2028697/dialogs-alertdialogs-how-to-block-execution-while-dialog-is-up-net-style

Modal dialog is a typical widget in Win32, it could
1. block current workflow
2. capture all input (key, mouse) events, but still properly dispatch
other events, like paint
3. wait for response and quit

People who's familiar with Win32 programming possibly knows how to
implement a modal dialog. Generally it runs a nested message loop (by
GetMessage/PostMessage) when there is a modal dialog up. So, I tried
to implement my own modal dialog in this traditional way.

At the first, android didn't provide interfaces to inject into ui
thread message loop, or I didn't find one. When I looked into source,
Looper.loop(), I found it's exactly what I wanted. But still,
MessageQueue/Message haven't provided public interfaces. Fortunately,
we have reflection in java.

Basically, I just copied exactly what Looper.loop() did, it blocked
workflow and still properly handled events. I haven't tested nested
modal dialog, but theoretically it would work.

Here's my source code,

public class ModalDialog {
    private boolean mChoice = false;
    private boolean mQuitModal = false;
    private Method mMsgQueueNextMethod = null;
    private Field mMsgTargetFiled = null;

    public ModalDialog() {
    }

    public void showAlertDialog(Context context, String info) {
        if (!prepareModal()) {
            return;
        }

        // build alert dialog
        AlertDialog.Builder builder = new
AlertDialog.Builder(context);
        builder.setMessage(info);
        builder.setCancelable(false);
        builder.setPositiveButton("Yes", new
DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int id) {
                ModalDialog.this.mQuitModal = true;
                dialog.dismiss();
            }
        });

        AlertDialog alert = builder.create();
        alert.show();
        // run in modal mode
        doModal();
    }

    public boolean showConfirmDialog(Context context, String info) {
        if (!prepareModal()) {
            return false;
        }

        // reset choice
        mChoice = false;

        AlertDialog.Builder builder = new
AlertDialog.Builder(context);
        builder.setMessage(info);
        builder.setCancelable(false);
        builder.setPositiveButton("Yes", new
DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int id) {
                ModalDialog.this.mQuitModal = true;
                ModalDialog.this.mChoice = true;
                dialog.dismiss();
            }
        });

        builder.setNegativeButton("Cancel", new
DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int id) {
                ModalDialog.this.mQuitModal = true;
                ModalDialog.this.mChoice = false;
                dialog.cancel();
            }
        });

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

        doModal();
        return mChoice;
    }

    private boolean prepareModal() {
        Class<?> clsMsgQueue = null;
        Class<?> clsMessage = null;

        try {
            clsMsgQueue = Class.forName("android.os.MessageQueue");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            return false;
        }

        try {
            clsMessage = Class.forName("android.os.Message");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            return false;
        }

        try {
            mMsgQueueNextMethod =
clsMsgQueue.getDeclaredMethod("next", new Class[]{});
        } catch (SecurityException e) {
            e.printStackTrace();
            return false;
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
            return false;
        }
        mMsgQueueNextMethod.setAccessible(true);

        try {
            mMsgTargetFiled = clsMessage.getDeclaredField("target");
        } catch (SecurityException e) {
            e.printStackTrace();
            return false;
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
            return false;
        }
        mMsgTargetFiled.setAccessible(true);

        return true;
    }

    private void doModal() {
        mQuitModal = false;

        // get message queue associated with main UI thread
        MessageQueue queue = Looper.myQueue();

        while (!mQuitModal) {
            // call queue.next(), might block
            Message msg = null;
            try {
                msg = (Message)mMsgQueueNextMethod.invoke(queue, new
Object[]{});
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }

            if (null != msg) {
                Handler target = null;
                try {
                    target = (Handler)mMsgTargetFiled.get(msg);
                } catch (IllegalArgumentException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
                if (target == null) {
                    // No target is a magic identifier for the quit
message.
                    mQuitModal = true;
                }
                target.dispatchMessage(msg);
                msg.recycle();
            }
        }
    }
}

Hopefully this would help.

-- 
You received this message because you are subscribed to the Google
Groups "Android Developers" group.
To post to this group, send email to android-developers@googlegroups.com
To unsubscribe from this group, send email to
android-developers+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/android-developers?hl=en

Reply via email to