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