Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c6442335 authored by Siarhei Vishniakou's avatar Siarhei Vishniakou
Browse files

Dismiss Dialog in post instead of immediately

When ViewGroup is dispatching touch events to children, it can
happen that a child, upon receiving a touch event, may decide
to close the window. In this case, dispatchDetachedFromWindow will be
called, which would invalidate (=recycle) the TouchTargets. As the
ViewGroup would continue dispatching the event to other children, those
TouchTargets would already no longer be valid. This could cause an NPE.

In one particular example, there could be several views in a Dialog, and
one of the views would dismiss the Dialog in a click handler.
Since the click handler executes events immediately, this makes
ViewGroup.dispatchTouchEvent recurse on itself, and modify the internal
state. This would later cause the NPE.

Calling dismiss in a message avoids this issue by letting the event
handling finish before dispatching dispatchDetachedFromWindow, which
would cause another ACTION_CANCEL to be sent to the view.

Bug: 26611563
Test: created a sample app and recorded a sequence of touch events using
inputstudio in order to reproduce the crash. The repro rate was 100%.
Could no longer repro with this change.

Change-Id: Ia1d8070152c7ba2d14cddebcc5844994fe3ab5da
parent a9c406ba
Loading
Loading
Loading
Loading
+78 −50
Original line number Diff line number Diff line
@@ -68,6 +68,14 @@ public abstract class DialogPreference extends Preference implements
    /** Which button was clicked. */
    private int mWhichButtonClicked;

    /** Dismiss the dialog on the UI thread, but not inline with handlers */
    private final Runnable mDismissRunnable = new Runnable() {
        @Override
        public void run() {
            mDialog.dismiss();
        }
    };

    public DialogPreference(
            Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
@@ -315,10 +323,29 @@ public abstract class DialogPreference extends Preference implements
        if (needInputMethod()) {
            requestInputMethod(dialog);
        }
        dialog.setOnShowListener(new DialogInterface.OnShowListener() {
            @Override
            public void onShow(DialogInterface dialog) {
                removeDismissCallbacks();
            }
        });
        dialog.setOnDismissListener(this);
        dialog.show();
    }

    void postDismiss() {
        removeDismissCallbacks();
        View decorView = mDialog.getWindow().getDecorView();
        decorView.post(mDismissRunnable);
    }

    private void removeDismissCallbacks() {
        if (mDialog != null && mDialog.getWindow() != null
                && mDialog.getWindow().getDecorView() != null) {
            mDialog.getWindow().getDecorView().removeCallbacks(mDismissRunnable);
        }
    }

    /**
     * Returns whether the preference needs to display a soft input method when the dialog
     * is displayed. Default is false. Subclasses should override this method if they need
@@ -387,8 +414,9 @@ public abstract class DialogPreference extends Preference implements
        mWhichButtonClicked = which;
    }

    @Override
    public void onDismiss(DialogInterface dialog) {
        
        removeDismissCallbacks();
        getPreferenceManager().unregisterOnActivityDestroyListener(this);

        mDialog = null;
+22 −22
Original line number Diff line number Diff line
@@ -261,7 +261,7 @@ public class ListPreference extends DialogPreference {
                         * click, and dismisses the dialog.
                         */
                        ListPreference.this.onClick(dialog, DialogInterface.BUTTON_POSITIVE);
                        dialog.dismiss();
                        postDismiss();
                    }
        });