Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

onUserInteraction not working in DialogPreference

It is expected that onUserInteraction is being called for any user interaction. it works fine in PreferenceActivity. However, when a DialogPreference is popup, onUserInteraction is not called anymore even there is user interaction such as touch event.

It seems that DialogPreference is not the only case. Whenever Dialog is shown, it does not report the user interaction to activity.

But what can I do if I really need it. Thank You.

like image 663
Bear Avatar asked Feb 12 '13 09:02

Bear


2 Answers

Here's a more self-contained and more complete Kotlin implementation:

/**
 * Sets up the receiver's [window][Dialog.getWindow] to call [Activity.onUserInteraction]
 * at appropriate times, mirroring the calls made in [Activity] itself.
 * This method should be called immediately after [Dialog.show].
 */
fun Dialog.reportUserInteraction() {
  window?.let { window ->
    val activity = window.decorView.activity

    val wrappedCallback = window.callback
    window.callback = object : Window.Callback by wrappedCallback {
      override fun dispatchGenericMotionEvent(event: MotionEvent): Boolean {
        activity.onUserInteraction()
        return wrappedCallback.dispatchGenericMotionEvent(event)
      }

      override fun dispatchKeyEvent(event: KeyEvent): Boolean {
        activity.onUserInteraction()
        return wrappedCallback.dispatchKeyEvent(event)
      }

      override fun dispatchKeyShortcutEvent(event: KeyEvent): Boolean {
        activity.onUserInteraction()
        return wrappedCallback.dispatchKeyShortcutEvent(event)
      }

      override fun dispatchTouchEvent(event: MotionEvent): Boolean {
        if (event.action == ACTION_DOWN) activity.onUserInteraction()
        return wrappedCallback.dispatchTouchEvent(event)
      }

      override fun dispatchTrackballEvent(event: MotionEvent): Boolean {
        activity.onUserInteraction()
        return wrappedCallback.dispatchTrackballEvent(event)
      }
    }
  }
}

Relies on:

val View.activity: Activity
  get() = context.activityOrNull!!

val Context.activityOrNull: Activity?
  get() {
    var context = this
    while (true) {
      if (context is Application) {
        return null
      }
      if (context is Activity) {
        return context
      }
      if (context is ContextWrapper) {
        val baseContext = context.baseContext
        // Prevent Stack Overflow.
        if (baseContext === this) {
          return null
        }
        context = baseContext
      } else {
        return null
      }
    }
  }
like image 111
rjrjr Avatar answered Oct 11 '22 01:10

rjrjr


As far as I know, the onUserInteraction() is simply not called while the user is interacting with a dialog (even started from Activity in which you're monitoring interactions).

Two solutions I know are:

  • Subclass Dialog/DialogPreference class and override dispatchTouchEvent().

  • Implement Window.Callback interface and set it as Dialogs window callback by issuing:

    dialog.getWindow().setCallback(callbackImplementation);
    

    Note: this implementation should process all received events by calling appropriate dialog methods or handle the events in your own way (e.g. by manually calling onUserInteraction()).

Edit

You have couple of ways to get Activity from the custom PreferenceDialog instance.

  1. Call DialogPreference.getPreferenceManager() method which returns PreferenceManager. It has a getActivity() method but it's package-private so you would have to put your custom DialogPreference in android.preference package to access it.

  2. In the PreferenceActivity.onCreate(), after inflating the preferences, use findPreference() to find your custom DialogPreference by key. Then cast it to your custom class and set activity to this via an accessor.

I would go with the second option.

like image 41
andr Avatar answered Oct 11 '22 00:10

andr