diff --git a/build.gradle b/build.gradle
index b151c3e5..913c4040 100644
--- a/build.gradle
+++ b/build.gradle
@@ -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',
diff --git a/rxbinding-material/src/androidTest/AndroidManifest.xml b/rxbinding-material/src/androidTest/AndroidManifest.xml
index 78ca0d47..dd2fcc6a 100644
--- a/rxbinding-material/src/androidTest/AndroidManifest.xml
+++ b/rxbinding-material/src/androidTest/AndroidManifest.xml
@@ -3,10 +3,12 @@
-
-
-
+
+
+
+
diff --git a/rxbinding-material/src/androidTest/java/com/jakewharton/rxbinding4/material/RxSliderTest.kt b/rxbinding-material/src/androidTest/java/com/jakewharton/rxbinding4/material/RxSliderTest.kt
new file mode 100644
index 00000000..37fb1b77
--- /dev/null
+++ b/rxbinding-material/src/androidTest/java/com/jakewharton/rxbinding4/material/RxSliderTest.kt
@@ -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()
+ 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()
+ 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()
+ 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()
+ 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()
+ }
+}
diff --git a/rxbinding-material/src/androidTest/java/com/jakewharton/rxbinding4/material/RxSliderTestActivity.kt b/rxbinding-material/src/androidTest/java/com/jakewharton/rxbinding4/material/RxSliderTestActivity.kt
new file mode 100644
index 00000000..974b8391
--- /dev/null
+++ b/rxbinding-material/src/androidTest/java/com/jakewharton/rxbinding4/material/RxSliderTestActivity.kt
@@ -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)
+ }
+}
\ No newline at end of file
diff --git a/rxbinding-material/src/main/java/com/jakewharton/rxbinding4/material/SliderChangeObservable.kt b/rxbinding-material/src/main/java/com/jakewharton/rxbinding4/material/SliderChangeObservable.kt
new file mode 100644
index 00000000..13edc1db
--- /dev/null
+++ b/rxbinding-material/src/main/java/com/jakewharton/rxbinding4/material/SliderChangeObservable.kt
@@ -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 {
+ 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 {
+ 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 {
+ return SliderChangeObservable(this, false)
+}
+
+private class SliderChangeObservable(
+ private val view: Slider,
+ private val shouldBeFromUser: Boolean?
+) : InitialValueObservable() {
+
+ override fun subscribeListener(observer: Observer) {
+ 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
+ ) : 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()
+ }
+ }
+}
\ No newline at end of file
diff --git a/rxbinding-material/src/main/java/com/jakewharton/rxbinding4/material/SliderTouchEvent.kt b/rxbinding-material/src/main/java/com/jakewharton/rxbinding4/material/SliderTouchEvent.kt
new file mode 100644
index 00000000..f8128bbf
--- /dev/null
+++ b/rxbinding-material/src/main/java/com/jakewharton/rxbinding4/material/SliderTouchEvent.kt
@@ -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()
\ No newline at end of file
diff --git a/rxbinding-material/src/main/java/com/jakewharton/rxbinding4/material/SliderTouchEventsObservable.kt b/rxbinding-material/src/main/java/com/jakewharton/rxbinding4/material/SliderTouchEventsObservable.kt
new file mode 100644
index 00000000..48d30a5d
--- /dev/null
+++ b/rxbinding-material/src/main/java/com/jakewharton/rxbinding4/material/SliderTouchEventsObservable.kt
@@ -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 {
+ return SliderTouchEventsObservable(this)
+}
+
+private class SliderTouchEventsObservable(
+ private val view: Slider
+) : Observable() {
+
+ override fun subscribeActual(observer: Observer) {
+ 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
+ ) : 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()
+ }
+ }
+}
\ No newline at end of file