0

I'm trying to write an observable that would generate repeated events while the user holds down a view. My code below works well, but only the first time (e.g. if the user presses the button again, nothing happens). Can you please advise what am I doing wrong and what is best practice for this?

val touches = RxView.touches(previousButton)
touches
        .filter({ event -> event.action == MotionEvent.ACTION_DOWN })
        .flatMap({
            Observable.interval(500, 50, TimeUnit.MILLISECONDS)
                    .takeUntil(touches.filter({event -> event.action == MotionEvent.ACTION_UP}))
        }).subscribe({ println("down") })
Alexandru Cristescu
  • 3,898
  • 3
  • 19
  • 22

1 Answers1

0

The problem is that the RxView.touches observable cannot exist for more than 1 source. This means when the subscription inside of the flatMap happens it breaks the original subscription used to trigger the flatMap, making it never occur again.

There are two possible ways around this:

  1. Use .publish(...) to share the source of events instead of using touches.
  2. Map the events into a Boolean on/off observable, then switchMap the appropriate actions based on the current value of the observable.

1.

touches.publish { src ->
    src.filter(...)
       .flatMap {
           Observable.interval(...)
                     .takeUntil(src.filter(...))
       }
}

2.

touches.filter {
           it.action == MotionEvent.ACTION_DOWN 
                or it.action == MotionEvent.ACTION_UP
       }
       .map { it.action == MotionEvent.ACTION_DOWN }
       .distinctUntilChanged() // Avoid repeating events
       .switchMap { state ->
           if (state) {
               Observable.interval(...)
           } else {
               Observable.never()
           }
       }
Kiskae
  • 24,655
  • 2
  • 77
  • 74
  • I'm going to accept it since the second method worked for me. I am curious though why I couldn't get the first one tow work, even with autoconnect? Here is the code that did NOT work: ```touches.publish().autoConnect(2).let { sharedTouches -> sharedTouches.filter({ event -> event.action == MotionEvent.ACTION_DOWN }) .flatMap({ Observable.interval(....) .takeUntil(sharedTouches.filter({ event -> event.action == MotionEvent.ACTION_UP })) }).subscribe(...) } ``` – Alexandru Cristescu Nov 19 '17 at 19:53
  • Because it would only subscribe after 2 inner subscriptions, but the secondary subscription only happens after the first subscription emits something. So it ends up in a deadlock waiting for a subscription that never happens. – Kiskae Nov 19 '17 at 22:34