Merge "Suppress existing TypeParameterUnusedInFormals and prevent new uses." into pi-preview1-androidx-dev
diff --git a/app-toolkit/settings.gradle b/app-toolkit/settings.gradle
index fcec40d..d065af0 100644
--- a/app-toolkit/settings.gradle
+++ b/app-toolkit/settings.gradle
@@ -64,6 +64,7 @@
 includeProject(":paging:integration-tests:testapp", new File(supportRoot, "paging/integration-tests/testapp"))
 includeProject(":paging:paging-common", new File(supportRoot, "paging/common"))
 includeProject(":paging:paging-runtime", new File(supportRoot, "paging/runtime"))
+includeProject(":paging:paging-rxjava2", new File(supportRoot, "paging/rxjava2"))
 includeProject(":room:integration-tests:testapp", new File(supportRoot, "room/integration-tests/testapp"))
 includeProject(":room:integration-tests:kotlintestapp", new File(supportRoot, "room/integration-tests/kotlintestapp"))
 includeProject(":room:room-common", new File(supportRoot, "room/common"))
diff --git a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
index 37869e4..f879a9f 100644
--- a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
@@ -50,6 +50,8 @@
      */
     val PAGING = Version("2.0.0-alpha1")
 
+    val PAGING_RX = Version("1.0.0-alpha1")
+
     private val LIFECYCLES = Version("2.0.0-alpha1")
 
     /**
diff --git a/paging/common/build.gradle b/paging/common/build.gradle
index af21da4..f4a592e 100644
--- a/paging/common/build.gradle
+++ b/paging/common/build.gradle
@@ -41,4 +41,4 @@
     inceptionYear = "2017"
     description = "Android Paging-Common"
     url = SupportLibraryExtension.ARCHITECTURE_URL
-}
\ No newline at end of file
+}
diff --git a/paging/common/src/main/java/androidx/paging/PositionalDataSource.java b/paging/common/src/main/java/androidx/paging/PositionalDataSource.java
index 4f7cd3f..a1de00f 100644
--- a/paging/common/src/main/java/androidx/paging/PositionalDataSource.java
+++ b/paging/common/src/main/java/androidx/paging/PositionalDataSource.java
@@ -459,13 +459,34 @@
     @SuppressWarnings("deprecation")
     static class ContiguousWithoutPlaceholdersWrapper<Value>
             extends ContiguousDataSource<Integer, Value> {
-
         @NonNull
-        final PositionalDataSource<Value> mPositionalDataSource;
+        final PositionalDataSource<Value> mSource;
 
         ContiguousWithoutPlaceholdersWrapper(
-                @NonNull PositionalDataSource<Value> positionalDataSource) {
-            mPositionalDataSource = positionalDataSource;
+                @NonNull PositionalDataSource<Value> source) {
+            mSource = source;
+        }
+
+        @Override
+        public void addInvalidatedCallback(
+                @NonNull InvalidatedCallback onInvalidatedCallback) {
+            mSource.addInvalidatedCallback(onInvalidatedCallback);
+        }
+
+        @Override
+        public void removeInvalidatedCallback(
+                @NonNull InvalidatedCallback onInvalidatedCallback) {
+            mSource.removeInvalidatedCallback(onInvalidatedCallback);
+        }
+
+        @Override
+        public void invalidate() {
+            mSource.invalidate();
+        }
+
+        @Override
+        public boolean isInvalid() {
+            return mSource.isInvalid();
         }
 
         @NonNull
@@ -493,7 +514,7 @@
             // Note enablePlaceholders will be false here, but we don't have a way to communicate
             // this to PositionalDataSource. This is fine, because only the list and its position
             // offset will be consumed by the LoadInitialCallback.
-            mPositionalDataSource.dispatchLoadInitial(false, convertPosition, initialLoadSize,
+            mSource.dispatchLoadInitial(false, convertPosition, initialLoadSize,
                     pageSize, mainThreadExecutor, receiver);
         }
 
@@ -502,7 +523,7 @@
                 @NonNull Executor mainThreadExecutor,
                 @NonNull PageResult.Receiver<Value> receiver) {
             int startIndex = currentEndIndex + 1;
-            mPositionalDataSource.dispatchLoadRange(
+            mSource.dispatchLoadRange(
                     PageResult.APPEND, startIndex, pageSize, mainThreadExecutor, receiver);
         }
 
@@ -514,12 +535,12 @@
             int startIndex = currentBeginIndex - 1;
             if (startIndex < 0) {
                 // trigger empty list load
-                mPositionalDataSource.dispatchLoadRange(
+                mSource.dispatchLoadRange(
                         PageResult.PREPEND, startIndex, 0, mainThreadExecutor, receiver);
             } else {
                 int loadSize = Math.min(pageSize, startIndex + 1);
                 startIndex = startIndex - loadSize + 1;
-                mPositionalDataSource.dispatchLoadRange(
+                mSource.dispatchLoadRange(
                         PageResult.PREPEND, startIndex, loadSize, mainThreadExecutor, receiver);
             }
         }
diff --git a/paging/common/src/main/java/androidx/paging/WrapperItemKeyedDataSource.java b/paging/common/src/main/java/androidx/paging/WrapperItemKeyedDataSource.java
index 72a6d5c..1583f9d 100644
--- a/paging/common/src/main/java/androidx/paging/WrapperItemKeyedDataSource.java
+++ b/paging/common/src/main/java/androidx/paging/WrapperItemKeyedDataSource.java
@@ -25,13 +25,6 @@
 class WrapperItemKeyedDataSource<K, A, B> extends ItemKeyedDataSource<K, B> {
     private final ItemKeyedDataSource<K, A> mSource;
     private final Function<List<A>, List<B>> mListFunction;
-    private final InvalidatedCallback mInvalidatedCallback = new DataSource.InvalidatedCallback() {
-        @Override
-        public void onInvalidated() {
-            invalidate();
-            removeCallback();
-        }
-    };
 
     private final IdentityHashMap<B, K> mKeyMap = new IdentityHashMap<>();
 
@@ -39,11 +32,26 @@
             Function<List<A>, List<B>> listFunction) {
         mSource = source;
         mListFunction = listFunction;
-        mSource.addInvalidatedCallback(mInvalidatedCallback);
     }
 
-    private void removeCallback() {
-        mSource.removeInvalidatedCallback(mInvalidatedCallback);
+    @Override
+    public void addInvalidatedCallback(@NonNull InvalidatedCallback onInvalidatedCallback) {
+        mSource.addInvalidatedCallback(onInvalidatedCallback);
+    }
+
+    @Override
+    public void removeInvalidatedCallback(@NonNull InvalidatedCallback onInvalidatedCallback) {
+        mSource.removeInvalidatedCallback(onInvalidatedCallback);
+    }
+
+    @Override
+    public void invalidate() {
+        mSource.invalidate();
+    }
+
+    @Override
+    public boolean isInvalid() {
+        return mSource.isInvalid();
     }
 
     private List<B> convertWithStashedKeys(List<A> source) {
diff --git a/paging/common/src/main/java/androidx/paging/WrapperPageKeyedDataSource.java b/paging/common/src/main/java/androidx/paging/WrapperPageKeyedDataSource.java
index 7675026..4c947d2 100644
--- a/paging/common/src/main/java/androidx/paging/WrapperPageKeyedDataSource.java
+++ b/paging/common/src/main/java/androidx/paging/WrapperPageKeyedDataSource.java
@@ -25,23 +25,31 @@
 class WrapperPageKeyedDataSource<K, A, B> extends PageKeyedDataSource<K, B> {
     private final PageKeyedDataSource<K, A> mSource;
     private final Function<List<A>, List<B>> mListFunction;
-    private final InvalidatedCallback mInvalidatedCallback = new DataSource.InvalidatedCallback() {
-        @Override
-        public void onInvalidated() {
-            invalidate();
-            removeCallback();
-        }
-    };
 
     WrapperPageKeyedDataSource(PageKeyedDataSource<K, A> source,
             Function<List<A>, List<B>> listFunction) {
         mSource = source;
         mListFunction = listFunction;
-        mSource.addInvalidatedCallback(mInvalidatedCallback);
     }
 
-    private void removeCallback() {
-        mSource.removeInvalidatedCallback(mInvalidatedCallback);
+    @Override
+    public void addInvalidatedCallback(@NonNull InvalidatedCallback onInvalidatedCallback) {
+        mSource.addInvalidatedCallback(onInvalidatedCallback);
+    }
+
+    @Override
+    public void removeInvalidatedCallback(@NonNull InvalidatedCallback onInvalidatedCallback) {
+        mSource.removeInvalidatedCallback(onInvalidatedCallback);
+    }
+
+    @Override
+    public void invalidate() {
+        mSource.invalidate();
+    }
+
+    @Override
+    public boolean isInvalid() {
+        return mSource.isInvalid();
     }
 
     @Override
diff --git a/paging/common/src/main/java/androidx/paging/WrapperPositionalDataSource.java b/paging/common/src/main/java/androidx/paging/WrapperPositionalDataSource.java
index 257f6c7..3b739ea 100644
--- a/paging/common/src/main/java/androidx/paging/WrapperPositionalDataSource.java
+++ b/paging/common/src/main/java/androidx/paging/WrapperPositionalDataSource.java
@@ -25,23 +25,30 @@
     private final PositionalDataSource<A> mSource;
     private final Function<List<A>, List<B>> mListFunction;
 
-    private final InvalidatedCallback mInvalidatedCallback = new DataSource.InvalidatedCallback() {
-        @Override
-        public void onInvalidated() {
-            invalidate();
-            removeCallback();
-        }
-    };
-
     WrapperPositionalDataSource(PositionalDataSource<A> source,
             Function<List<A>, List<B>> listFunction) {
         mSource = source;
         mListFunction = listFunction;
-        mSource.addInvalidatedCallback(mInvalidatedCallback);
     }
 
-    private void removeCallback() {
-        mSource.removeInvalidatedCallback(mInvalidatedCallback);
+    @Override
+    public void addInvalidatedCallback(@NonNull InvalidatedCallback onInvalidatedCallback) {
+        mSource.addInvalidatedCallback(onInvalidatedCallback);
+    }
+
+    @Override
+    public void removeInvalidatedCallback(@NonNull InvalidatedCallback onInvalidatedCallback) {
+        mSource.removeInvalidatedCallback(onInvalidatedCallback);
+    }
+
+    @Override
+    public void invalidate() {
+        mSource.invalidate();
+    }
+
+    @Override
+    public boolean isInvalid() {
+        return mSource.isInvalid();
     }
 
     @Override
diff --git a/paging/common/src/test/java/androidx/paging/ItemKeyedDataSourceTest.kt b/paging/common/src/test/java/androidx/paging/ItemKeyedDataSourceTest.kt
index bdb001a..bd03782 100644
--- a/paging/common/src/test/java/androidx/paging/ItemKeyedDataSourceTest.kt
+++ b/paging/common/src/test/java/androidx/paging/ItemKeyedDataSourceTest.kt
@@ -337,17 +337,20 @@
 
     private abstract class WrapperDataSource<K, A, B>(private val source: ItemKeyedDataSource<K, A>)
             : ItemKeyedDataSource<K, B>() {
-        private val invalidatedCallback = DataSource.InvalidatedCallback {
-            invalidate()
-            removeCallback()
+        override fun addInvalidatedCallback(onInvalidatedCallback: InvalidatedCallback) {
+            source.addInvalidatedCallback(onInvalidatedCallback)
         }
 
-        init {
-            source.addInvalidatedCallback(invalidatedCallback)
+        override fun removeInvalidatedCallback(onInvalidatedCallback: InvalidatedCallback) {
+            source.removeInvalidatedCallback(onInvalidatedCallback)
         }
 
-        private fun removeCallback() {
-            removeInvalidatedCallback(invalidatedCallback)
+        override fun invalidate() {
+            source.invalidate()
+        }
+
+        override fun isInvalid(): Boolean {
+            return source.isInvalid
         }
 
         override fun loadInitial(params: LoadInitialParams<K>, callback: LoadInitialCallback<B>) {
@@ -399,7 +402,7 @@
         // verify that it's possible to wrap an ItemKeyedDataSource, and add info to its data
 
         val orig = ItemDataSource(items = ITEMS_BY_NAME_ID)
-        val wrapper = DecoratedWrapperDataSource(orig)
+        val wrapper = createWrapper(orig)
 
         // load initial
         @Suppress("UNCHECKED_CAST")
@@ -425,6 +428,10 @@
         wrapper.loadBefore(ItemKeyedDataSource.LoadParams(key, 10), loadCallback)
         verify(loadCallback).onResult(ITEMS_BY_NAME_ID.subList(10, 20).map { DecoratedItem(it) })
         verifyNoMoreInteractions(loadCallback)
+
+        // verify invalidation
+        orig.invalidate()
+        assertTrue(wrapper.isInvalid)
     }
 
     @Test
@@ -442,6 +449,24 @@
         it.map { DecoratedItem(it) }
     }
 
+    @Test
+    fun testInvalidateToWrapper() {
+        val orig = ItemDataSource()
+        val wrapper = orig.map { DecoratedItem(it) }
+
+        orig.invalidate()
+        assertTrue(wrapper.isInvalid)
+    }
+
+    @Test
+    fun testInvalidateFromWrapper() {
+        val orig = ItemDataSource()
+        val wrapper = orig.map { DecoratedItem(it) }
+
+        wrapper.invalidate()
+        assertTrue(orig.isInvalid)
+    }
+
     companion object {
         private val ITEM_COMPARATOR = compareBy<Item>({ it.name }).thenByDescending({ it.id })
         private val KEY_COMPARATOR = compareBy<Key>({ it.name }).thenByDescending({ it.id })
diff --git a/paging/common/src/test/java/androidx/paging/PageKeyedDataSourceTest.kt b/paging/common/src/test/java/androidx/paging/PageKeyedDataSourceTest.kt
index d86b87a..9f211fc 100644
--- a/paging/common/src/test/java/androidx/paging/PageKeyedDataSourceTest.kt
+++ b/paging/common/src/test/java/androidx/paging/PageKeyedDataSourceTest.kt
@@ -17,6 +17,7 @@
 package androidx.paging
 
 import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
 import org.junit.Assert.fail
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -160,17 +161,20 @@
 
     private abstract class WrapperDataSource<K, A, B>(private val source: PageKeyedDataSource<K, A>)
             : PageKeyedDataSource<K, B>() {
-        private val invalidatedCallback = DataSource.InvalidatedCallback {
-            invalidate()
-            removeCallback()
+        override fun addInvalidatedCallback(onInvalidatedCallback: InvalidatedCallback) {
+            source.addInvalidatedCallback(onInvalidatedCallback)
         }
 
-        init {
-            source.addInvalidatedCallback(invalidatedCallback)
+        override fun removeInvalidatedCallback(onInvalidatedCallback: InvalidatedCallback) {
+            source.removeInvalidatedCallback(onInvalidatedCallback)
         }
 
-        private fun removeCallback() {
-            removeInvalidatedCallback(invalidatedCallback)
+        override fun invalidate() {
+            source.invalidate()
+        }
+
+        override fun isInvalid(): Boolean {
+            return source.isInvalid
         }
 
         override fun loadInitial(params: LoadInitialParams<K>,
@@ -247,6 +251,10 @@
         verify(loadCallback).onResult(expectedInitial.data.map { it.toString() },
                 expectedInitial.prev)
         verifyNoMoreInteractions(loadCallback)
+
+        // verify invalidation
+        orig.invalidate()
+        assertTrue(wrapper.isInvalid)
     }
 
     @Test
@@ -258,11 +266,30 @@
     fun testListConverterWrappedDataSource() = verifyWrappedDataSource {
         it.mapByPage { it.map { it.toString() } }
     }
+
     @Test
     fun testItemConverterWrappedDataSource() = verifyWrappedDataSource {
         it.map { it.toString() }
     }
 
+    @Test
+    fun testInvalidateToWrapper() {
+        val orig = ItemDataSource()
+        val wrapper = orig.map { it.toString() }
+
+        orig.invalidate()
+        assertTrue(wrapper.isInvalid)
+    }
+
+    @Test
+    fun testInvalidateFromWrapper() {
+        val orig = ItemDataSource()
+        val wrapper = orig.map { it.toString() }
+
+        wrapper.invalidate()
+        assertTrue(orig.isInvalid)
+    }
+
     companion object {
         // first load is 2nd page to ensure we test prepend as well as append behavior
         private val INIT_KEY: String = "key 2"
diff --git a/paging/common/src/test/java/androidx/paging/PositionalDataSourceTest.kt b/paging/common/src/test/java/androidx/paging/PositionalDataSourceTest.kt
index acf565b..f18b41a 100644
--- a/paging/common/src/test/java/androidx/paging/PositionalDataSourceTest.kt
+++ b/paging/common/src/test/java/androidx/paging/PositionalDataSourceTest.kt
@@ -17,6 +17,7 @@
 package androidx.paging
 
 import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
 import org.junit.Assert.fail
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -219,17 +220,20 @@
 
     private abstract class WrapperDataSource<in A, B>(private val source: PositionalDataSource<A>)
             : PositionalDataSource<B>() {
-        private val invalidatedCallback = DataSource.InvalidatedCallback {
-            invalidate()
-            removeCallback()
+        override fun addInvalidatedCallback(onInvalidatedCallback: InvalidatedCallback) {
+            source.addInvalidatedCallback(onInvalidatedCallback)
         }
 
-        init {
-            source.addInvalidatedCallback(invalidatedCallback)
+        override fun removeInvalidatedCallback(onInvalidatedCallback: InvalidatedCallback) {
+            source.removeInvalidatedCallback(onInvalidatedCallback)
         }
 
-        private fun removeCallback() {
-            removeInvalidatedCallback(invalidatedCallback)
+        override fun invalidate() {
+            source.invalidate()
+        }
+
+        override fun isInvalid(): Boolean {
+            return source.isInvalid
         }
 
         override fun loadInitial(params: LoadInitialParams, callback: LoadInitialCallback<B>) {
@@ -292,6 +296,10 @@
         orig.invalidate()
         verify(invalCallback).onInvalidated()
         verifyNoMoreInteractions(invalCallback)
+
+        // verify invalidation
+        orig.invalidate()
+        assertTrue(wrapper.isInvalid)
     }
 
     @Test
@@ -308,4 +316,40 @@
     fun testItemConverterWrappedDataSource() = verifyWrappedDataSource {
         it.map { it.toString() }
     }
+
+    @Test
+    fun testInvalidateToWrapper() {
+        val orig = ListDataSource(listOf(0, 1, 2))
+        val wrapper = orig.map { it.toString() }
+
+        orig.invalidate()
+        assertTrue(wrapper.isInvalid)
+    }
+
+    @Test
+    fun testInvalidateFromWrapper() {
+        val orig = ListDataSource(listOf(0, 1, 2))
+        val wrapper = orig.map { it.toString() }
+
+        wrapper.invalidate()
+        assertTrue(orig.isInvalid)
+    }
+
+    @Test
+    fun testInvalidateToWrapper_contiguous() {
+        val orig = ListDataSource(listOf(0, 1, 2))
+        val wrapper = orig.wrapAsContiguousWithoutPlaceholders()
+
+        orig.invalidate()
+        assertTrue(wrapper.isInvalid)
+    }
+
+    @Test
+    fun testInvalidateFromWrapper_contiguous() {
+        val orig = ListDataSource(listOf(0, 1, 2))
+        val wrapper = orig.wrapAsContiguousWithoutPlaceholders()
+
+        wrapper.invalidate()
+        assertTrue(orig.isInvalid)
+    }
 }
diff --git a/paging/runtime/src/main/java/androidx/paging/LivePagedListBuilder.java b/paging/runtime/src/main/java/androidx/paging/LivePagedListBuilder.java
index f8af819..672e40f 100644
--- a/paging/runtime/src/main/java/androidx/paging/LivePagedListBuilder.java
+++ b/paging/runtime/src/main/java/androidx/paging/LivePagedListBuilder.java
@@ -30,7 +30,7 @@
  * {@link PagedList.Config}.
  * <p>
  * The required parameters are in the constructor, so you can simply construct and build, or
- * optionally enable extra features (such as initial load key, or BoundaryCallback.
+ * optionally enable extra features (such as initial load key, or BoundaryCallback).
  *
  * @param <Key> Type of input valued used to load data from the DataSource. Must be integer if
  *             you're using PositionalDataSource.
diff --git a/paging/rxjava2/build.gradle b/paging/rxjava2/build.gradle
new file mode 100644
index 0000000..f4c5308
--- /dev/null
+++ b/paging/rxjava2/build.gradle
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+import androidx.build.SupportLibraryExtension
+
+plugins {
+    id("SupportAndroidLibraryPlugin")
+    id("kotlin-android")
+}
+
+dependencies {
+    api(project(":arch:core-runtime"))
+    api(project(":paging:paging-common"))
+
+    api(RX_JAVA)
+    androidTestImplementation(JUNIT)
+    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+    androidTestImplementation(TEST_RUNNER)
+    androidTestImplementation(ESPRESSO_CORE)
+    androidTestImplementation(KOTLIN_STDLIB)
+}
+
+supportLibrary {
+    name = "Android Paging RXJava2"
+    publish = true
+    mavenVersion = LibraryVersions.PAGING_RX
+    mavenGroup = LibraryGroups.PAGING
+    inceptionYear = "2018"
+    description = "Android Paging RXJava2"
+    url = SupportLibraryExtension.ARCHITECTURE_URL
+}
diff --git a/paging/rxjava2/src/androidTest/java/androidx/paging/RxPagedListBuilderTest.kt b/paging/rxjava2/src/androidTest/java/androidx/paging/RxPagedListBuilderTest.kt
new file mode 100644
index 0000000..083cb96
--- /dev/null
+++ b/paging/rxjava2/src/androidTest/java/androidx/paging/RxPagedListBuilderTest.kt
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.paging
+
+import android.support.test.filters.SmallTest
+import io.reactivex.Observable
+import io.reactivex.observers.TestObserver
+import io.reactivex.schedulers.TestScheduler
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@SmallTest
+@RunWith(JUnit4::class)
+class RxPagedListBuilderTest {
+
+    /**
+     * Creates a data source that will sequentially supply the passed lists
+     */
+    private fun testDataSourceSequence(data: List<List<String>>): DataSource.Factory<Int, String> {
+        return object : DataSource.Factory<Int, String>() {
+            var localData = data
+            override fun create(): DataSource<Int, String> {
+                val currentList = localData.first()
+                localData = localData.drop(1)
+                return ListDataSource<String>(currentList)
+            }
+        }
+    }
+
+    @Test
+    fun basic() {
+        val factory = testDataSourceSequence(listOf(listOf("a", "b"), listOf("c", "d")))
+        val scheduler = TestScheduler()
+        val observable = RxPagedListBuilder(factory, 10)
+                .setFetchScheduler(scheduler)
+                .setNotifyScheduler(scheduler)
+                .buildObservable()
+        val observer = TestObserver<PagedList<String>>()
+
+        observable.subscribe(observer)
+
+        // initial state
+        observer.assertNotComplete()
+        observer.assertValueCount(0)
+
+        // load first item
+        scheduler.triggerActions()
+        observer.assertValueCount(1)
+        assertEquals(listOf("a", "b"), observer.values().first())
+
+        // invalidate triggers second load
+        observer.values().first().dataSource.invalidate()
+        scheduler.triggerActions()
+        observer.assertValueCount(2)
+        assertEquals(listOf("c", "d"), observer.values().last())
+    }
+
+    @Test
+    fun checkSchedulers() {
+        val factory = testDataSourceSequence(listOf(listOf("a", "b"), listOf("c", "d")))
+        val notifyScheduler = TestScheduler()
+        val fetchScheduler = TestScheduler()
+        val observable: Observable<PagedList<String>> = RxPagedListBuilder(
+                factory, 10)
+                .setFetchScheduler(fetchScheduler)
+                .setNotifyScheduler(notifyScheduler)
+                .buildObservable()
+        val observer = TestObserver<PagedList<String>>()
+        observable.subscribe(observer)
+
+        // notify has nothing to do
+        notifyScheduler.triggerActions()
+        observer.assertValueCount(0)
+
+        // fetch creates list, but observer doesn't see
+        fetchScheduler.triggerActions()
+        observer.assertValueCount(0)
+
+        // now notify reveals item
+        notifyScheduler.triggerActions()
+        observer.assertValueCount(1)
+    }
+}
diff --git a/samples/SupportSliceDemos/src/main/res/layout/slice_view.xml b/paging/rxjava2/src/main/AndroidManifest.xml
similarity index 65%
copy from samples/SupportSliceDemos/src/main/res/layout/slice_view.xml
copy to paging/rxjava2/src/main/AndroidManifest.xml
index 36f6750..6329afe 100644
--- a/samples/SupportSliceDemos/src/main/res/layout/slice_view.xml
+++ b/paging/rxjava2/src/main/AndroidManifest.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright 2018 The Android Open Source Project
+  ~ Copyright (C) 2018 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
   ~ you may not use this file except in compliance with the License.
@@ -15,8 +14,6 @@
   ~ limitations under the License.
   -->
 
-<androidx.slice.widget.SliceView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    style="@style/CustomSliceView"/>
\ No newline at end of file
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.arch.paging.rxjava2">
+</manifest>
diff --git a/paging/rxjava2/src/main/java/androidx/paging/RxPagedListBuilder.java b/paging/rxjava2/src/main/java/androidx/paging/RxPagedListBuilder.java
new file mode 100644
index 0000000..f98bc1e
--- /dev/null
+++ b/paging/rxjava2/src/main/java/androidx/paging/RxPagedListBuilder.java
@@ -0,0 +1,330 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.paging;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.arch.core.executor.ArchTaskExecutor;
+
+import java.util.concurrent.Executor;
+
+import io.reactivex.BackpressureStrategy;
+import io.reactivex.Flowable;
+import io.reactivex.Observable;
+import io.reactivex.ObservableEmitter;
+import io.reactivex.ObservableOnSubscribe;
+import io.reactivex.Scheduler;
+import io.reactivex.functions.Cancellable;
+import io.reactivex.schedulers.Schedulers;
+
+/**
+ * Builder for {@code Observable<PagedList>} or {@code Flowable<PagedList>}, given a
+ * {@link DataSource.Factory} and a {@link PagedList.Config}.
+ * <p>
+ * The required parameters are in the constructor, so you can simply construct and build, or
+ * optionally enable extra features (such as initial load key, or BoundaryCallback).
+ * <p>
+ * The returned observable/flowable will already be subscribed on the
+ * {@link #setFetchScheduler(Scheduler)}, and will perform all loading on that scheduler. It will
+ * already be observed on {@link #setNotifyScheduler(Scheduler)}, and will dispatch new PagedLists,
+ * as well as their updates to that scheduler.
+ *
+ * @param <Key> Type of input valued used to load data from the DataSource. Must be integer if
+ *             you're using PositionalDataSource.
+ * @param <Value> Item type being presented.
+ */
+public final class RxPagedListBuilder<Key, Value> {
+    private Key mInitialLoadKey;
+    private PagedList.Config mConfig;
+    private DataSource.Factory<Key, Value> mDataSourceFactory;
+    private PagedList.BoundaryCallback mBoundaryCallback;
+    private Executor mNotifyExecutor;
+    private Executor mFetchExecutor;
+    private Scheduler mFetchScheduler;
+    private Scheduler mNotifyScheduler;
+
+    /**
+     * Creates a RxPagedListBuilder with required parameters.
+     *
+     * @param dataSourceFactory DataSource factory providing DataSource generations.
+     * @param config Paging configuration.
+     */
+    public RxPagedListBuilder(@NonNull DataSource.Factory<Key, Value> dataSourceFactory,
+            @NonNull PagedList.Config config) {
+        //noinspection ConstantConditions
+        if (config == null) {
+            throw new IllegalArgumentException("PagedList.Config must be provided");
+        }
+        //noinspection ConstantConditions
+        if (dataSourceFactory == null) {
+            throw new IllegalArgumentException("DataSource.Factory must be provided");
+        }
+        mDataSourceFactory = dataSourceFactory;
+        mConfig = config;
+    }
+
+    /**
+     * Creates a RxPagedListBuilder with required parameters.
+     * <p>
+     * This method is a convenience for:
+     * <pre>
+     * RxPagedListBuilder(dataSourceFactory,
+     *         new PagedList.Config.Builder().setPageSize(pageSize).build())
+     * </pre>
+     *
+     * @param dataSourceFactory DataSource.Factory providing DataSource generations.
+     * @param pageSize Size of pages to load.
+     */
+    @SuppressWarnings("unused")
+    public RxPagedListBuilder(@NonNull DataSource.Factory<Key, Value> dataSourceFactory,
+            int pageSize) {
+        this(dataSourceFactory, new PagedList.Config.Builder().setPageSize(pageSize).build());
+    }
+
+    /**
+     * First loading key passed to the first PagedList/DataSource.
+     * <p>
+     * When a new PagedList/DataSource pair is created after the first, it acquires a load key from
+     * the previous generation so that data is loaded around the position already being observed.
+     *
+     * @param key Initial load key passed to the first PagedList/DataSource.
+     * @return this
+     */
+    @SuppressWarnings("unused")
+    @NonNull
+    public RxPagedListBuilder<Key, Value> setInitialLoadKey(@Nullable Key key) {
+        mInitialLoadKey = key;
+        return this;
+    }
+
+    /**
+     * Sets a {@link PagedList.BoundaryCallback} on each PagedList created, typically used to load
+     * additional data from network when paging from local storage.
+     * <p>
+     * Pass a BoundaryCallback to listen to when the PagedList runs out of data to load. If this
+     * method is not called, or {@code null} is passed, you will not be notified when each
+     * DataSource runs out of data to provide to its PagedList.
+     * <p>
+     * If you are paging from a DataSource.Factory backed by local storage, you can set a
+     * BoundaryCallback to know when there is no more information to page from local storage.
+     * This is useful to page from the network when local storage is a cache of network data.
+     * <p>
+     * Note that when using a BoundaryCallback with a {@code Observable<PagedList>}, method calls
+     * on the callback may be dispatched multiple times - one for each PagedList/DataSource
+     * pair. If loading network data from a BoundaryCallback, you should prevent multiple
+     * dispatches of the same method from triggering multiple simultaneous network loads.
+     *
+     * @param boundaryCallback The boundary callback for listening to PagedList load state.
+     * @return this
+     */
+    @SuppressWarnings("unused")
+    @NonNull
+    public RxPagedListBuilder<Key, Value> setBoundaryCallback(
+            @Nullable PagedList.BoundaryCallback<Value> boundaryCallback) {
+        mBoundaryCallback = boundaryCallback;
+        return this;
+    }
+
+    /**
+     * Sets scheduler which will be used for observing new PagedLists, as well as loading updates
+     * within the PagedLists.
+     * <p>
+     * The built observable will be {@link Observable#observeOn(Scheduler) observed on} this
+     * scheduler, so that the thread receiving PagedLists will also receive the internal updates to
+     * the PagedList.
+     *
+     * @param scheduler Scheduler for background DataSource loading.
+     * @return this
+     */
+    public RxPagedListBuilder<Key, Value> setNotifyScheduler(
+            final @NonNull Scheduler scheduler) {
+        mNotifyScheduler = scheduler;
+        final Scheduler.Worker worker = scheduler.createWorker();
+        mNotifyExecutor = new Executor() {
+            @Override
+            public void execute(@NonNull Runnable command) {
+                // We use a worker here since the page load notifications
+                // should not be dispatched in parallel
+                worker.schedule(command);
+            }
+        };
+        return this;
+    }
+
+    /**
+     * Sets scheduler which will be used for background fetching of PagedLists, as well as on-demand
+     * fetching of pages inside.
+     *
+     * @param scheduler Scheduler for background DataSource loading.
+     * @return this
+     */
+    @SuppressWarnings({"unused", "WeakerAccess"})
+    @NonNull
+    public RxPagedListBuilder<Key, Value> setFetchScheduler(
+            final @NonNull Scheduler scheduler) {
+        mFetchExecutor = new Executor() {
+            @Override
+            public void execute(@NonNull Runnable command) {
+                // We use scheduleDirect since the page loads that use
+                // executor are intentionally parallel.
+                scheduler.scheduleDirect(command);
+            }
+        };
+        mFetchScheduler = scheduler;
+        return this;
+    }
+
+    /**
+     * Constructs a {@code Observable<PagedList>}.
+     * <p>
+     * The returned Observable will already be observed on the
+     * {@link #setNotifyScheduler(Scheduler) notify scheduler}, and subscribed on the
+     * {@link #setFetchScheduler(Scheduler) fetch scheduler}.
+     *
+     * @return The Observable of PagedLists
+     */
+    @NonNull
+    public Observable<PagedList<Value>> buildObservable() {
+        if (mNotifyExecutor == null) {
+            mNotifyExecutor = ArchTaskExecutor.getMainThreadExecutor();
+            mNotifyScheduler = Schedulers.from(mNotifyExecutor);
+        }
+        if (mFetchExecutor == null) {
+            mFetchExecutor = ArchTaskExecutor.getIOThreadExecutor();
+            mFetchScheduler = Schedulers.from(mFetchExecutor);
+        }
+        return Observable.create(new PagingObservableOnSubscribe<>(
+                mInitialLoadKey,
+                mConfig,
+                mBoundaryCallback,
+                mDataSourceFactory,
+                mNotifyExecutor,
+                mFetchExecutor))
+                        .observeOn(mNotifyScheduler)
+                        .subscribeOn(mFetchScheduler);
+    }
+
+    /**
+     * Constructs a {@code Flowable<PagedList>}.
+     *
+     * The returned Observable will already be observed on the
+     * {@link #setNotifyScheduler(Scheduler) notify scheduler}, and subscribed on the
+     * {@link #setFetchScheduler(Scheduler) fetch scheduler}.
+     *
+     * @param backpressureStrategy BackpressureStrategy for the Flowable to use.
+     * @return The Flowable of PagedLists
+     */
+    @NonNull
+    public Flowable<PagedList<Value>> buildFlowable(BackpressureStrategy backpressureStrategy) {
+        return buildObservable()
+                .toFlowable(backpressureStrategy);
+    }
+
+    static class PagingObservableOnSubscribe<Key, Value>
+            implements ObservableOnSubscribe<PagedList<Value>>, DataSource.InvalidatedCallback,
+            Cancellable,
+            Runnable {
+
+        @Nullable
+        private final Key mInitialLoadKey;
+        @NonNull
+        private final PagedList.Config mConfig;
+        @Nullable
+        private final PagedList.BoundaryCallback mBoundaryCallback;
+        @NonNull
+        private final DataSource.Factory<Key, Value> mDataSourceFactory;
+        @NonNull
+        private final Executor mNotifyExecutor;
+        @NonNull
+        private final Executor mFetchExecutor;
+
+        @Nullable
+        private PagedList<Value> mList;
+        @Nullable
+        private DataSource<Key, Value> mDataSource;
+
+        private ObservableEmitter<PagedList<Value>> mEmitter;
+
+        private PagingObservableOnSubscribe(@Nullable Key initialLoadKey,
+                @NonNull PagedList.Config config,
+                @Nullable PagedList.BoundaryCallback boundaryCallback,
+                @NonNull DataSource.Factory<Key, Value> dataSourceFactory,
+                @NonNull Executor notifyExecutor,
+                @NonNull Executor fetchExecutor) {
+            mInitialLoadKey = initialLoadKey;
+            mConfig = config;
+            mBoundaryCallback = boundaryCallback;
+            mDataSourceFactory = dataSourceFactory;
+            mNotifyExecutor = notifyExecutor;
+            mFetchExecutor = fetchExecutor;
+        }
+
+        @Override
+        public void subscribe(ObservableEmitter<PagedList<Value>> emitter)
+                throws Exception {
+            mEmitter = emitter;
+            mEmitter.setCancellable(this);
+
+            // known that subscribe is already on fetchScheduler
+            mEmitter.onNext(createPagedList());
+        }
+
+        @Override
+        public void cancel() throws Exception {
+            if (mDataSource != null) {
+                mDataSource.removeInvalidatedCallback(this);
+            }
+        }
+
+        @Override
+        public void run() {
+            // fetch data, run on fetchExecutor
+            mEmitter.onNext(createPagedList());
+        }
+
+        @Override
+        public void onInvalidated() {
+            if (!mEmitter.isDisposed()) {
+                mFetchExecutor.execute(this);
+            }
+        }
+
+        private PagedList<Value> createPagedList() {
+            @Nullable Key initializeKey = mInitialLoadKey;
+            if (mList != null) {
+                //noinspection unchecked
+                initializeKey = (Key) mList.getLastKey();
+            }
+
+            do {
+                if (mDataSource != null) {
+                    mDataSource.removeInvalidatedCallback(this);
+                }
+                mDataSource = mDataSourceFactory.create();
+                mDataSource.addInvalidatedCallback(this);
+
+                mList = new PagedList.Builder<>(mDataSource, mConfig)
+                        .setNotifyExecutor(mNotifyExecutor)
+                        .setFetchExecutor(mFetchExecutor)
+                        .setBoundaryCallback(mBoundaryCallback)
+                        .setInitialKey(initializeKey)
+                        .build();
+            } while (mList.isDetached());
+            return mList;
+        }
+    }
+}
diff --git a/room/integration-tests/testapp/build.gradle b/room/integration-tests/testapp/build.gradle
index 22fd802..ae30df3 100644
--- a/room/integration-tests/testapp/build.gradle
+++ b/room/integration-tests/testapp/build.gradle
@@ -40,12 +40,12 @@
     implementation(project(":room:room-runtime"))
     implementation(project(":arch:core-runtime"))
     implementation(project(":arch:core-common"))
-    implementation(project(":paging:paging-common"))
     implementation(project(":lifecycle:lifecycle-extensions"))
     implementation(project(":lifecycle:lifecycle-runtime"))
     implementation(project(":lifecycle:lifecycle-common"))
     implementation(project(":room:room-rxjava2"))
     implementation(project(":paging:paging-runtime"))
+    implementation(project(":paging:paging-rxjava2"))
 
     implementation(SUPPORT_RECYCLERVIEW, libs.support_exclude_config)
     implementation(SUPPORT_APPCOMPAT, libs.support_exclude_config)
diff --git a/room/integration-tests/testapp/src/main/AndroidManifest.xml b/room/integration-tests/testapp/src/main/AndroidManifest.xml
index 245f9c0..98536d1 100644
--- a/room/integration-tests/testapp/src/main/AndroidManifest.xml
+++ b/room/integration-tests/testapp/src/main/AndroidManifest.xml
@@ -37,5 +37,14 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
+        <activity
+            android:name=".RoomPagedListRxActivity"
+            android:label="PagedList Observable"
+            android:theme="@style/Theme.AppCompat">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
     </application>
-  </manifest>
+</manifest>
diff --git a/room/integration-tests/testapp/src/main/java/androidx/room/integration/testapp/CustomerViewModel.java b/room/integration-tests/testapp/src/main/java/androidx/room/integration/testapp/CustomerViewModel.java
index eca2552..d0ac7d8 100644
--- a/room/integration-tests/testapp/src/main/java/androidx/room/integration/testapp/CustomerViewModel.java
+++ b/room/integration-tests/testapp/src/main/java/androidx/room/integration/testapp/CustomerViewModel.java
@@ -25,6 +25,7 @@
 import androidx.paging.DataSource;
 import androidx.paging.LivePagedListBuilder;
 import androidx.paging.PagedList;
+import androidx.paging.RxPagedListBuilder;
 import androidx.room.Room;
 import androidx.room.integration.testapp.database.Customer;
 import androidx.room.integration.testapp.database.LastNameAscCustomerDataSource;
@@ -32,6 +33,9 @@
 
 import java.util.UUID;
 
+import io.reactivex.BackpressureStrategy;
+import io.reactivex.Flowable;
+
 /**
  * Sample database-backed view model of Customers
  */
@@ -48,20 +52,17 @@
         mDatabase = Room.databaseBuilder(this.getApplication(),
                 SampleDatabase.class, "customerDatabase").build();
 
-        ArchTaskExecutor.getInstance().executeOnDiskIO(new Runnable() {
-            @Override
-            public void run() {
-                // fill with some simple data
-                int customerCount = mDatabase.getCustomerDao().countCustomers();
-                if (customerCount == 0) {
-                    Customer[] initialCustomers = new Customer[10];
-                    for (int i = 0; i < 10; i++) {
-                        initialCustomers[i] = createCustomer();
-                    }
-                    mDatabase.getCustomerDao().insertAll(initialCustomers);
+        ArchTaskExecutor.getInstance().executeOnDiskIO(() -> {
+            // fill with some simple data
+            int customerCount = mDatabase.getCustomerDao().countCustomers();
+            if (customerCount == 0) {
+                Customer[] initialCustomers = new Customer[10];
+                for (int i = 0; i < 10; i++) {
+                    initialCustomers[i] = createCustomer();
                 }
-
+                mDatabase.getCustomerDao().insertAll(initialCustomers);
             }
+
         });
     }
 
@@ -74,12 +75,13 @@
     }
 
     void insertCustomer() {
-        ArchTaskExecutor.getInstance().executeOnDiskIO(new Runnable() {
-            @Override
-            public void run() {
-                mDatabase.getCustomerDao().insert(createCustomer());
-            }
-        });
+        ArchTaskExecutor.getInstance().executeOnDiskIO(
+                () -> mDatabase.getCustomerDao().insert(createCustomer()));
+    }
+
+    void clearAllCustomers() {
+        ArchTaskExecutor.getInstance().executeOnDiskIO(
+                () -> mDatabase.getCustomerDao().removeAll());
     }
 
     private static <K> LiveData<PagedList<Customer>> getLivePagedList(
@@ -93,6 +95,20 @@
                 .build();
     }
 
+    private static <K> Flowable<PagedList<Customer>> getPagedListFlowable(
+            DataSource.Factory<K, Customer> dataSourceFactory) {
+        PagedList.Config config = new PagedList.Config.Builder()
+                .setPageSize(10)
+                .setEnablePlaceholders(false)
+                .build();
+        return new RxPagedListBuilder<>(dataSourceFactory, config)
+                .buildFlowable(BackpressureStrategy.LATEST);
+    }
+
+    Flowable<PagedList<Customer>> getPagedListFlowable() {
+        return getPagedListFlowable(mDatabase.getCustomerDao().loadPagedAgeOrder());
+    }
+
     LiveData<PagedList<Customer>> getLivePagedList(int position) {
         if (mLiveCustomerList == null) {
             mLiveCustomerList =
diff --git a/room/integration-tests/testapp/src/main/java/androidx/room/integration/testapp/RoomPagedListRxActivity.java b/room/integration-tests/testapp/src/main/java/androidx/room/integration/testapp/RoomPagedListRxActivity.java
new file mode 100644
index 0000000..16d24a5
--- /dev/null
+++ b/room/integration-tests/testapp/src/main/java/androidx/room/integration/testapp/RoomPagedListRxActivity.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.integration.testapp;
+
+import android.os.Bundle;
+import android.widget.Button;
+
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.lifecycle.ViewModelProviders;
+import androidx.recyclerview.widget.RecyclerView;
+
+import io.reactivex.disposables.CompositeDisposable;
+
+/**
+ * Sample {@code Flowable<PagedList>} activity which uses Room.
+ */
+public class RoomPagedListRxActivity extends AppCompatActivity {
+
+    private PagedListCustomerAdapter mAdapter;
+
+    private final CompositeDisposable mDisposable = new CompositeDisposable();
+    private CustomerViewModel mViewModel;
+
+    @Override
+    protected void onCreate(final Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_recycler_view);
+        mViewModel = ViewModelProviders.of(this)
+                .get(CustomerViewModel.class);
+
+        RecyclerView recyclerView = findViewById(R.id.recyclerview);
+        mAdapter = new PagedListCustomerAdapter();
+        recyclerView.setAdapter(mAdapter);
+
+        final Button addButton = findViewById(R.id.addButton);
+        addButton.setOnClickListener(v -> mViewModel.insertCustomer());
+
+        final Button clearButton = findViewById(R.id.clearButton);
+        clearButton.setOnClickListener(v -> mViewModel.clearAllCustomers());
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+
+        mDisposable.add(mViewModel.getPagedListFlowable()
+                .subscribe(list -> mAdapter.submitList(list)));
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+        mDisposable.clear();
+    }
+}
diff --git a/room/integration-tests/testapp/src/main/java/androidx/room/integration/testapp/database/CustomerDao.java b/room/integration-tests/testapp/src/main/java/androidx/room/integration/testapp/database/CustomerDao.java
index 311a593..1aa0163 100644
--- a/room/integration-tests/testapp/src/main/java/androidx/room/integration/testapp/database/CustomerDao.java
+++ b/room/integration-tests/testapp/src/main/java/androidx/room/integration/testapp/database/CustomerDao.java
@@ -44,6 +44,12 @@
     void insertAll(Customer[] customers);
 
     /**
+     * Delete all customers
+     */
+    @Query("DELETE FROM customer")
+    void removeAll();
+
+    /**
      * @return DataSource.Factory of customers, ordered by last name. Use
      * {@link androidx.paging.LivePagedListBuilder} to get a LiveData of PagedLists.
      */
diff --git a/room/integration-tests/testapp/src/main/res/layout/activity_recycler_view.xml b/room/integration-tests/testapp/src/main/res/layout/activity_recycler_view.xml
index 8934cbb..9995aa5 100644
--- a/room/integration-tests/testapp/src/main/res/layout/activity_recycler_view.xml
+++ b/room/integration-tests/testapp/src/main/res/layout/activity_recycler_view.xml
@@ -22,7 +22,7 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     tools:context="androidx.room.integration.testapp.RoomPagedListActivity">
-    <android.support.v7.widget.RecyclerView
+    <androidx.recyclerview.widget.RecyclerView
         android:id="@+id/recyclerview"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
@@ -35,6 +35,14 @@
         android:paddingTop="@dimen/activity_vertical_margin"
     />
     <Button
+        android:id="@+id/clearButton"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentBottom="true"
+        android:layout_alignParentLeft="true"
+        android:layout_alignParentStart="true"
+        android:text="@string/clear"/>
+    <Button
         android:id="@+id/addButton"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
diff --git a/room/integration-tests/testapp/src/main/res/values/strings.xml b/room/integration-tests/testapp/src/main/res/values/strings.xml
index a18723e..2bba589 100644
--- a/room/integration-tests/testapp/src/main/res/values/strings.xml
+++ b/room/integration-tests/testapp/src/main/res/values/strings.xml
@@ -15,6 +15,7 @@
 -->
 
 <resources>
+    <string name="clear">Clear</string>
     <string name="insert">Insert</string>
     <string name="loading">loading</string>
 </resources>
diff --git a/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SliceBrowser.java b/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SliceBrowser.java
index 673eea1..4facfb3 100644
--- a/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SliceBrowser.java
+++ b/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SliceBrowser.java
@@ -280,8 +280,7 @@
 
     private SliceView createSliceView() {
         SliceView v = TEST_THEMES
-                ? (SliceView) getLayoutInflater().inflate(
-                R.layout.slice_view, mContainer, false)
+                ? new SliceView(this)
                 : new SliceView(getApplicationContext());
         v.setOnSliceActionListener(this);
         v.setOnClickListener(new View.OnClickListener() {
diff --git a/samples/SupportSliceDemos/src/main/res/values/colors.xml b/samples/SupportSliceDemos/src/main/res/values/colors.xml
index 86b4304..c991181 100644
--- a/samples/SupportSliceDemos/src/main/res/values/colors.xml
+++ b/samples/SupportSliceDemos/src/main/res/values/colors.xml
@@ -19,4 +19,8 @@
     <color name="colorPrimary">#3F51B5</color>
     <color name="colorPrimaryDark">#303F9F</color>
     <color name="colorAccent">#FF4081</color>
+
+    <!-- slice view custom theme -->
+    <color name="textColor">#D9000000</color>
+    <color name="tintColor">#FF12A4Af</color>
 </resources>
diff --git a/samples/SupportSliceDemos/src/main/res/layout/slice_view.xml b/samples/SupportSliceDemos/src/main/res/values/dimens.xml
similarity index 75%
rename from samples/SupportSliceDemos/src/main/res/layout/slice_view.xml
rename to samples/SupportSliceDemos/src/main/res/values/dimens.xml
index 36f6750..fe67a7f 100644
--- a/samples/SupportSliceDemos/src/main/res/layout/slice_view.xml
+++ b/samples/SupportSliceDemos/src/main/res/values/dimens.xml
@@ -15,8 +15,7 @@
   ~ limitations under the License.
   -->
 
-<androidx.slice.widget.SliceView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    style="@style/CustomSliceView"/>
\ No newline at end of file
+<resources>
+    <dimen name="textSize">10sp</dimen>
+    <dimen name="smallTextSize">9sp</dimen>
+</resources>
\ No newline at end of file
diff --git a/samples/SupportSliceDemos/src/main/res/values/styles.xml b/samples/SupportSliceDemos/src/main/res/values/styles.xml
index dc139b6..d276a41 100644
--- a/samples/SupportSliceDemos/src/main/res/values/styles.xml
+++ b/samples/SupportSliceDemos/src/main/res/values/styles.xml
@@ -23,6 +23,7 @@
         <item name="colorPrimary">@color/colorPrimary</item>
         <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
         <item name="colorAccent">@color/colorAccent</item>
+        <item name="sliceViewStyle">@style/CustomSliceView</item>
     </style>
 
     <style name="AppTheme.NoActionBar">
@@ -33,6 +34,18 @@
     <style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light"/>
 
     <style name="CustomSliceView" parent="Widget.SliceView">
+        <item name="tintColor">@color/tintColor</item>
+
+        <item name="titleColor">@color/textColor</item>
+        <item name="subtitleColor">@color/textColor</item>
+
+        <item name="headerTitleSize">@dimen/textSize</item>
+        <item name="headerSubtitleSize">@dimen/textSize</item>
+        <item name="titleSize">@dimen/textSize</item>
+        <item name="subtitleSize">@dimen/textSize</item>
+        <item name="gridTitleSize">@dimen/textSize</item>
+        <item name="gridSubtitleSize">@dimen/smallTextSize</item>
+
         <item name="android:paddingLeft">8dp</item>
         <item name="android:paddingTop">8dp</item>
         <item name="android:paddingRight">8dp</item>
diff --git a/slices/view/src/main/java/androidx/slice/widget/GridRowView.java b/slices/view/src/main/java/androidx/slice/widget/GridRowView.java
index 6292062..1bb90e7 100644
--- a/slices/view/src/main/java/androidx/slice/widget/GridRowView.java
+++ b/slices/view/src/main/java/androidx/slice/widget/GridRowView.java
@@ -359,7 +359,7 @@
                     mObserver.onSliceAction(info, actionItem);
                 }
             } catch (PendingIntent.CanceledException e) {
-                Log.w(TAG, "PendingIntent for slice cannot be sent", e);
+                Log.e(TAG, "PendingIntent for slice cannot be sent", e);
             }
         }
     }
diff --git a/slices/view/src/main/java/androidx/slice/widget/LargeSliceAdapter.java b/slices/view/src/main/java/androidx/slice/widget/LargeSliceAdapter.java
index 9247b78..e09405c 100644
--- a/slices/view/src/main/java/androidx/slice/widget/LargeSliceAdapter.java
+++ b/slices/view/src/main/java/androidx/slice/widget/LargeSliceAdapter.java
@@ -19,7 +19,6 @@
 import static android.app.slice.Slice.HINT_HORIZONTAL;
 import static android.app.slice.Slice.SUBTYPE_MESSAGE;
 import static android.app.slice.Slice.SUBTYPE_SOURCE;
-import static android.app.slice.SliceItem.FORMAT_IMAGE;
 import static android.app.slice.SliceItem.FORMAT_INT;
 import static android.app.slice.SliceItem.FORMAT_TEXT;
 
@@ -65,6 +64,8 @@
     private SliceView.OnSliceActionListener mSliceObserver;
     private int mColor;
     private AttributeSet mAttrs;
+    private int mDefStyleAttr;
+    private int mDefStyleRes;
     private List<SliceItem> mSliceActions;
     private boolean mShowLastUpdated;
     private long mLastUpdated;
@@ -119,8 +120,10 @@
     /**
      * Sets the attribute set to use for views in the list.
      */
-    public void setStyle(AttributeSet attrs) {
+    public void setStyle(AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         mAttrs = attrs;
+        mDefStyleAttr = defStyleAttr;
+        mDefStyleRes = defStyleRes;
         notifyDataSetChanged();
     }
 
@@ -246,7 +249,7 @@
 
             final boolean isHeader = position == HEADER_INDEX;
             mSliceChildView.setTint(mColor);
-            mSliceChildView.setStyle(mAttrs);
+            mSliceChildView.setStyle(mAttrs, mDefStyleAttr, mDefStyleRes);
             mSliceChildView.setSliceItem(item, isHeader, position, mSliceObserver);
             if (isHeader && mSliceChildView instanceof RowView) {
                 mSliceChildView.setSliceActions(mSliceActions);
@@ -299,12 +302,8 @@
             while (items.hasNext()) {
                 SliceItem i = items.next();
                 builder.append(i.getFormat());
-                //i.removeHint(Slice.HINT_SELECTED);
                 builder.append(i.getHints());
                 switch (i.getFormat()) {
-                    case FORMAT_IMAGE:
-                        builder.append(i.getIcon());
-                        break;
                     case FORMAT_TEXT:
                         builder.append(i.getText());
                         break;
diff --git a/slices/view/src/main/java/androidx/slice/widget/LargeTemplateView.java b/slices/view/src/main/java/androidx/slice/widget/LargeTemplateView.java
index 0c0aba8..6632097 100644
--- a/slices/view/src/main/java/androidx/slice/widget/LargeTemplateView.java
+++ b/slices/view/src/main/java/androidx/slice/widget/LargeTemplateView.java
@@ -166,9 +166,9 @@
     }
 
     @Override
-    public void setStyle(AttributeSet attrs) {
-        super.setStyle(attrs);
-        mAdapter.setStyle(attrs);
+    public void setStyle(AttributeSet attrs, int defStyleAttrs, int defStyleRes) {
+        super.setStyle(attrs, defStyleAttrs, defStyleRes);
+        mAdapter.setStyle(attrs, defStyleAttrs, defStyleRes);
     }
 
     @Override
diff --git a/slices/view/src/main/java/androidx/slice/widget/RowView.java b/slices/view/src/main/java/androidx/slice/widget/RowView.java
index e782485..0528d4b 100644
--- a/slices/view/src/main/java/androidx/slice/widget/RowView.java
+++ b/slices/view/src/main/java/androidx/slice/widget/RowView.java
@@ -436,6 +436,7 @@
                         mObserver.onSliceAction(info, actionContent.getSliceItem());
                     }
                 } catch (CanceledException e) {
+                    Log.e(TAG, "PendingIntent for slice cannot be sent", e);
                     toggle.setSelected(!isChecked);
                 }
             }
@@ -517,7 +518,7 @@
                             mObserver.onSliceAction(info, finalAction.getSliceItem());
                         }
                     } catch (CanceledException e) {
-                        e.printStackTrace();
+                        Log.e(TAG, "PendingIntent for slice cannot be sent", e);
                     }
                 }
             });
@@ -541,7 +542,7 @@
                     }
                     mRowContent.getSlice().getAction().send();
                 } catch (CanceledException e) {
-                    Log.w(TAG, "PendingIntent for slice cannot be sent", e);
+                    Log.e(TAG, "PendingIntent for slice cannot be sent", e);
                 }
             }
         });
@@ -564,7 +565,7 @@
                     mObserver.onSliceAction(info, mRowAction.getSliceItem());
                 }
             } catch (CanceledException e) {
-                Log.w(TAG, "PendingIntent for slice cannot be sent", e);
+                Log.e(TAG, "PendingIntent for slice cannot be sent", e);
             }
         } else if (mToggles.size() == 1) {
             // If there is only one toggle and no row action, just toggle it.
diff --git a/slices/view/src/main/java/androidx/slice/widget/ShortcutView.java b/slices/view/src/main/java/androidx/slice/widget/ShortcutView.java
index b068c26..75cd842 100644
--- a/slices/view/src/main/java/androidx/slice/widget/ShortcutView.java
+++ b/slices/view/src/main/java/androidx/slice/widget/ShortcutView.java
@@ -39,6 +39,7 @@
 import android.graphics.drawable.ShapeDrawable;
 import android.graphics.drawable.shapes.OvalShape;
 import android.net.Uri;
+import android.util.Log;
 import android.widget.ImageView;
 
 import androidx.annotation.RestrictTo;
@@ -131,7 +132,7 @@
                     mObserver.onSliceAction(ei, interactedItem);
                 }
             } catch (CanceledException e) {
-                e.printStackTrace();
+                Log.e(TAG, "PendingIntent for slice cannot be sent", e);
             }
         }
         return true;
diff --git a/slices/view/src/main/java/androidx/slice/widget/SliceChildView.java b/slices/view/src/main/java/androidx/slice/widget/SliceChildView.java
index 2e52246..2400e73 100644
--- a/slices/view/src/main/java/androidx/slice/widget/SliceChildView.java
+++ b/slices/view/src/main/java/androidx/slice/widget/SliceChildView.java
@@ -138,9 +138,9 @@
     /**
      * Populates style information for this view.
      */
-    public void setStyle(AttributeSet attrs) {
+    public void setStyle(AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         TypedArray a = getContext().getTheme().obtainStyledAttributes(attrs, R.styleable.SliceView,
-                R.attr.sliceViewStyle, R.style.Widget_SliceView);
+                defStyleAttr, defStyleRes);
         try {
             int themeColor = a.getColor(R.styleable.SliceView_tintColor, -1);
             mTintColor = themeColor != -1 ? themeColor : mTintColor;
diff --git a/slices/view/src/main/java/androidx/slice/widget/SliceView.java b/slices/view/src/main/java/androidx/slice/widget/SliceView.java
index c9acef4..3eeda45 100644
--- a/slices/view/src/main/java/androidx/slice/widget/SliceView.java
+++ b/slices/view/src/main/java/androidx/slice/widget/SliceView.java
@@ -141,6 +141,8 @@
     private int mActionRowHeight;
 
     private AttributeSet mAttrs;
+    private int mDefStyleAttr;
+    private int mDefStyleRes;
     private int mThemeTintColor = -1;
 
     private OnSliceActionListener mSliceObserver;
@@ -175,8 +177,11 @@
 
     private void init(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         mAttrs = attrs;
+        mDefStyleAttr = defStyleAttr;
+        mDefStyleRes = defStyleRes;
         TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.SliceView,
                 defStyleAttr, defStyleRes);
+
         try {
             mThemeTintColor = a.getColor(R.styleable.SliceView_tintColor, -1);
         } finally {
@@ -236,7 +241,7 @@
                     mSliceObserver.onSliceAction(eventInfo, mListContent.getPrimaryAction());
                 }
             } catch (PendingIntent.CanceledException e) {
-                e.printStackTrace();
+                Log.e(TAG, "PendingIntent for slice cannot be sent", e);
             }
         } else if (mOnClickListener != null) {
             mOnClickListener.onClick(this);
@@ -550,7 +555,7 @@
         if (mCurrentView instanceof LargeTemplateView) {
             ((LargeTemplateView) mCurrentView).setScrollable(mIsScrollable);
         }
-        mCurrentView.setStyle(mAttrs);
+        mCurrentView.setStyle(mAttrs, mDefStyleAttr, mDefStyleRes);
         mCurrentView.setTint(getTintColor());
 
         // Check if the slice content is expired and show when it was last updated