Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MDC Slider events observable #558

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ buildscript {
'androidXTestEspresso': 'androidx.test.espresso:espresso-core:3.1.1',
'androidXTestEspressoContrib': 'androidx.test.espresso:espresso-contrib:3.1.1',

'googleMaterial': 'com.google.android.material:material:1.0.0',
'googleMaterial': 'com.google.android.material:material:1.3.0',

'rxJava': 'io.reactivex.rxjava3:rxjava:3.0.3',
'rxAndroid': 'io.reactivex.rxjava3:rxandroid:3.0.0',
Expand Down
14 changes: 8 additions & 6 deletions rxbinding-material/src/androidTest/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.jakewharton.rxbinding4.material">

<application>
<activity
android:name=".RxSwipeDismissBehaviorTestActivity"
android:theme="@style/Theme.AppCompat"
/>
</application>
<application>
<activity
android:name=".RxSwipeDismissBehaviorTestActivity"
android:theme="@style/Theme.AppCompat" />
<activity
android:name=".RxSliderTestActivity"
android:theme="@style/Theme.MaterialComponents" />
</application>
</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package com.jakewharton.rxbinding4.material

import android.util.Log
import android.view.MotionEvent
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.rule.ActivityTestRule
import com.google.android.material.slider.Slider
import com.jakewharton.rxbinding4.MotionEventUtil
import com.jakewharton.rxbinding4.RecordingObserver
import com.jakewharton.rxbinding4.widget.*
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import org.junit.Assert
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Rule
import org.junit.Test

class RxSliderTest {
@Rule @JvmField
val activityRule = ActivityTestRule(RxSliderTestActivity::class.java)
private val instrumentation = InstrumentationRegistry.getInstrumentation()
lateinit var slider: Slider

@Before
fun setUp() {
slider = activityRule.activity.slider
}

@Test
fun changes() {
val o = RecordingObserver<Float>()
slider.changes() //
.subscribeOn(AndroidSchedulers.mainThread()) //
.subscribe(o)
assertEquals(0.0f, o.takeNext())
instrumentation.sendPointerSync(MotionEventUtil.motionEventAtPosition(slider, MotionEvent.ACTION_DOWN, 0, 50))
instrumentation.waitForIdleSync()
o.assertNoMoreEvents()
instrumentation.sendPointerSync(MotionEventUtil.motionEventAtPosition(slider, MotionEvent.ACTION_MOVE, 100, 50))
instrumentation.waitForIdleSync()
assertEquals(1.0f, o.takeNext())
instrumentation.sendPointerSync(MotionEventUtil.motionEventAtPosition(slider, MotionEvent.ACTION_MOVE, 0, 50))
instrumentation.waitForIdleSync()
assertEquals(0.0f, o.takeNext())
instrumentation.runOnMainSync { slider.value = 0.85f }
instrumentation.waitForIdleSync()
assertEquals(0.85f, o.takeNext())
o.dispose()
instrumentation.sendPointerSync(MotionEventUtil.motionEventAtPosition(slider, MotionEvent.ACTION_MOVE, 100, 50))
instrumentation.waitForIdleSync()
o.assertNoMoreEvents()
instrumentation.runOnMainSync { slider.value = 0.85f }
instrumentation.waitForIdleSync()
o.assertNoMoreEvents()
}

@Test
fun systemChanges() {
val o = RecordingObserver<Float>()
slider.systemChanges() //
.subscribeOn(AndroidSchedulers.mainThread()) //
.subscribe(o)
assertEquals(0.0f, o.takeNext())
instrumentation.sendPointerSync(MotionEventUtil.motionEventAtPosition(slider, MotionEvent.ACTION_MOVE, 100, 50))
instrumentation.waitForIdleSync()
o.assertNoMoreEvents()
instrumentation.runOnMainSync { slider.value = 0.85f }
instrumentation.waitForIdleSync()
assertEquals(0.85f, o.takeNext())
o.dispose()
instrumentation.sendPointerSync(MotionEventUtil.motionEventAtPosition(slider, MotionEvent.ACTION_MOVE, 100, 50))
instrumentation.waitForIdleSync()
o.assertNoMoreEvents()
instrumentation.runOnMainSync { slider.value = 0.85f }
instrumentation.waitForIdleSync()
o.assertNoMoreEvents()
}

@Test
fun userChanges() {
val o = RecordingObserver<Float>()
slider.userChanges() //
.subscribeOn(AndroidSchedulers.mainThread()) //
.subscribe(o)
assertEquals(0.0f, o.takeNext())
instrumentation.sendPointerSync(MotionEventUtil.motionEventAtPosition(slider, MotionEvent.ACTION_DOWN, 0, 50))
instrumentation.waitForIdleSync()
o.assertNoMoreEvents()
instrumentation.sendPointerSync(MotionEventUtil.motionEventAtPosition(slider, MotionEvent.ACTION_MOVE, 100, 50))
instrumentation.waitForIdleSync()
assertEquals(1.0f, o.takeNext())
instrumentation.sendPointerSync(MotionEventUtil.motionEventAtPosition(slider, MotionEvent.ACTION_MOVE, 0, 50))
instrumentation.waitForIdleSync()
assertEquals(0.0f, o.takeNext())
instrumentation.runOnMainSync { slider.value = 0.85f }
instrumentation.waitForIdleSync()
o.assertNoMoreEvents()
o.dispose()
instrumentation.sendPointerSync(MotionEventUtil.motionEventAtPosition(slider, MotionEvent.ACTION_MOVE, 100, 50))
instrumentation.waitForIdleSync()
o.assertNoMoreEvents()
instrumentation.runOnMainSync { slider.value = 0.85f }
instrumentation.waitForIdleSync()
o.assertNoMoreEvents()
}

@Test
fun touchEvents() {
val o = RecordingObserver<SliderTouchEvent>()
slider.touchEvents() //
.subscribeOn(AndroidSchedulers.mainThread()) //
.subscribe(o)
instrumentation.sendPointerSync(MotionEventUtil.motionEventAtPosition(slider, MotionEvent.ACTION_DOWN, 0, 50))
instrumentation.waitForIdleSync()
assertEquals(SliderStartTrackingTouchEvent(slider), o.takeNext())
instrumentation.sendPointerSync(MotionEventUtil.motionEventAtPosition(slider, MotionEvent.ACTION_MOVE, 100, 50))
instrumentation.waitForIdleSync()
instrumentation.sendPointerSync(MotionEventUtil.motionEventAtPosition(slider, MotionEvent.ACTION_UP, 100, 50))
instrumentation.waitForIdleSync()
assertEquals(SliderStopTrackingTouchEvent(slider), o.takeNext())
instrumentation.runOnMainSync { slider.value = 0f }
o.dispose()
instrumentation.sendPointerSync(MotionEventUtil.motionEventAtPosition(slider, MotionEvent.ACTION_DOWN, 0, 50))
instrumentation.waitForIdleSync()
o.assertNoMoreEvents()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.jakewharton.rxbinding4.material

import android.app.Activity
import android.os.Bundle
import com.google.android.material.slider.Slider

class RxSliderTestActivity : Activity() {
lateinit var slider: Slider

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

slider = Slider(this)
setContentView(slider)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
@file:JvmName("RxSlider")
@file:JvmMultifileClass

package com.jakewharton.rxbinding4.material

import androidx.annotation.CheckResult
import com.google.android.material.slider.Slider
import com.jakewharton.rxbinding4.InitialValueObservable
import com.jakewharton.rxbinding4.internal.checkMainThread
import io.reactivex.rxjava3.android.MainThreadDisposable
import io.reactivex.rxjava3.core.Observer


/**
* Create an observable of progress value changes on `view`.
*
* *Warning:* The created observable keeps a strong reference to `view`. Unsubscribe
* to free this reference.
*
* *Note:* A value will be emitted immediately on subscribe.
*/
@CheckResult
fun Slider.changes(): InitialValueObservable<Float> {
return SliderChangeObservable(this, null)
}

/**
* Create an observable of progress value changes on `view` that were made only from the
* user.
*
* *Warning:* The created observable keeps a strong reference to `view`. Unsubscribe
* to free this reference.
*
* *Note:* A value will be emitted immediately on subscribe.
*/
@CheckResult
fun Slider.userChanges(): InitialValueObservable<Float> {
return SliderChangeObservable(this, true)
}

/**
* Create an observable of progress value changes on `view` that were made only from the
* system.
*
* *Warning:* The created observable keeps a strong reference to `view`. Unsubscribe
* to free this reference.
*
* *Note:* A value will be emitted immediately on subscribe.
*/
@CheckResult
fun Slider.systemChanges(): InitialValueObservable<Float> {
return SliderChangeObservable(this, false)
}

private class SliderChangeObservable(
private val view: Slider,
private val shouldBeFromUser: Boolean?
) : InitialValueObservable<Float>() {

override fun subscribeListener(observer: Observer<in Float>) {
if (!checkMainThread(observer)) {
return
}
val listener = Listener(view, shouldBeFromUser, observer)
view.addOnChangeListener(listener)
observer.onSubscribe(listener)
}

override val initialValue get() = view.value

private class Listener(
private val view: Slider,
private val shouldBeFromUser: Boolean?,
private val observer: Observer<in Float>
) : MainThreadDisposable(), Slider.OnChangeListener {

override fun onValueChange(slider: Slider, value: Float, fromUser: Boolean) {
if (!isDisposed && (shouldBeFromUser == null || shouldBeFromUser == fromUser)) {
observer.onNext(value)
}
}

override fun onDispose() {
view.clearOnChangeListeners()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.jakewharton.rxbinding4.material

import com.google.android.material.slider.Slider

sealed class SliderTouchEvent {
abstract val view: Slider
}

data class SliderStartTrackingTouchEvent(
override val view: Slider
) : SliderTouchEvent()

data class SliderStopTrackingTouchEvent(
override val view: Slider
) : SliderTouchEvent()
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.jakewharton.rxbinding4.material

import androidx.annotation.CheckResult
import com.google.android.material.slider.Slider
import com.jakewharton.rxbinding4.internal.checkMainThread
import io.reactivex.rxjava3.android.MainThreadDisposable
import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.core.Observer

/**
* Create an observable of touch events for `view`.
*
* *Warning:* The created observable keeps a strong reference to `view`. Unsubscribe
* to free this reference.
*/
@CheckResult
fun Slider.touchEvents(): Observable<SliderTouchEvent> {
return SliderTouchEventsObservable(this)
}

private class SliderTouchEventsObservable(
private val view: Slider
) : Observable<SliderTouchEvent>() {

override fun subscribeActual(observer: Observer<in SliderTouchEvent>) {
if (!checkMainThread(observer)) {
return
}
val listener = Listener(view, observer)
observer.onSubscribe(listener)
view.addOnSliderTouchListener(listener)
}

private class Listener(
private val view: Slider,
private val observer: Observer<in SliderTouchEvent>
) : MainThreadDisposable(), Slider.OnSliderTouchListener {

override fun onStartTrackingTouch(slider: Slider) {
observer.onNext(SliderStartTrackingTouchEvent(view))
}

override fun onStopTrackingTouch(slider: Slider) {
observer.onNext(SliderStopTrackingTouchEvent(view))
}

override fun onDispose() {
view.clearOnSliderTouchListeners()
}
}
}