Merge "Run KspTypeTests with javac" into androidx-main
diff --git a/paging/common/api/current.txt b/paging/common/api/current.txt
index c0867a3..5a10493 100644
--- a/paging/common/api/current.txt
+++ b/paging/common/api/current.txt
@@ -291,23 +291,22 @@
 
   public final class PagingData<T> {
     method public static <T> androidx.paging.PagingData<T> empty();
-    method @CheckResult public androidx.paging.PagingData<T> filter(kotlin.jvm.functions.Function1<? super T,java.lang.Boolean> predicate);
-    method @CheckResult public <R> androidx.paging.PagingData<R> flatMap(kotlin.jvm.functions.Function1<? super T,? extends java.lang.Iterable<? extends R>> transform);
     method public static <T> androidx.paging.PagingData<T> from(java.util.List<? extends T> data);
-    method @CheckResult public androidx.paging.PagingData<T> insertFooterItem(T item);
-    method @CheckResult public androidx.paging.PagingData<T> insertHeaderItem(T item);
-    method @CheckResult public static <T extends R, R> androidx.paging.PagingData<R> insertSeparators(androidx.paging.PagingData<T> pagingData, kotlin.jvm.functions.Function2<? super T,? super T,? extends R> generator);
-    method @CheckResult public <R> androidx.paging.PagingData<R> map(kotlin.jvm.functions.Function1<? super T,? extends R> transform);
     field public static final androidx.paging.PagingData.Companion Companion;
   }
 
   public static final class PagingData.Companion {
     method public <T> androidx.paging.PagingData<T> empty();
     method public <T> androidx.paging.PagingData<T> from(java.util.List<? extends T> data);
-    method @CheckResult public <T extends R, R> androidx.paging.PagingData<R> insertSeparators(androidx.paging.PagingData<T> pagingData, kotlin.jvm.functions.Function2<? super T,? super T,? extends R> generator);
   }
 
-  public final class PagingDataKt {
+  public final class PagingDataTransforms {
+    method @CheckResult public static <T> androidx.paging.PagingData<T> filter(androidx.paging.PagingData<T>, java.util.concurrent.Executor executor, kotlin.jvm.functions.Function1<? super T,java.lang.Boolean> predicate);
+    method @CheckResult public static <T, R> androidx.paging.PagingData<R> flatMap(androidx.paging.PagingData<T>, java.util.concurrent.Executor executor, kotlin.jvm.functions.Function1<? super T,? extends java.lang.Iterable<? extends R>> transform);
+    method @CheckResult public static <T> androidx.paging.PagingData<T> insertFooterItem(androidx.paging.PagingData<T>, T item);
+    method @CheckResult public static <T> androidx.paging.PagingData<T> insertHeaderItem(androidx.paging.PagingData<T>, T item);
+    method @CheckResult public static <R, T extends R> androidx.paging.PagingData<R> insertSeparators(androidx.paging.PagingData<T>, java.util.concurrent.Executor executor, kotlin.jvm.functions.Function2<? super T,? super T,? extends R> generator);
+    method @CheckResult public static <T, R> androidx.paging.PagingData<R> map(androidx.paging.PagingData<T>, java.util.concurrent.Executor executor, kotlin.jvm.functions.Function1<? super T,? extends R> transform);
   }
 
   public abstract class PagingSource<Key, Value> {
diff --git a/paging/common/api/public_plus_experimental_current.txt b/paging/common/api/public_plus_experimental_current.txt
index 9641951..3b7ae06 100644
--- a/paging/common/api/public_plus_experimental_current.txt
+++ b/paging/common/api/public_plus_experimental_current.txt
@@ -292,23 +292,22 @@
 
   public final class PagingData<T> {
     method public static <T> androidx.paging.PagingData<T> empty();
-    method @CheckResult public androidx.paging.PagingData<T> filter(kotlin.jvm.functions.Function1<? super T,java.lang.Boolean> predicate);
-    method @CheckResult public <R> androidx.paging.PagingData<R> flatMap(kotlin.jvm.functions.Function1<? super T,? extends java.lang.Iterable<? extends R>> transform);
     method public static <T> androidx.paging.PagingData<T> from(java.util.List<? extends T> data);
-    method @CheckResult public androidx.paging.PagingData<T> insertFooterItem(T item);
-    method @CheckResult public androidx.paging.PagingData<T> insertHeaderItem(T item);
-    method @CheckResult public static <T extends R, R> androidx.paging.PagingData<R> insertSeparators(androidx.paging.PagingData<T> pagingData, kotlin.jvm.functions.Function2<? super T,? super T,? extends R> generator);
-    method @CheckResult public <R> androidx.paging.PagingData<R> map(kotlin.jvm.functions.Function1<? super T,? extends R> transform);
     field public static final androidx.paging.PagingData.Companion Companion;
   }
 
   public static final class PagingData.Companion {
     method public <T> androidx.paging.PagingData<T> empty();
     method public <T> androidx.paging.PagingData<T> from(java.util.List<? extends T> data);
-    method @CheckResult public <T extends R, R> androidx.paging.PagingData<R> insertSeparators(androidx.paging.PagingData<T> pagingData, kotlin.jvm.functions.Function2<? super T,? super T,? extends R> generator);
   }
 
-  public final class PagingDataKt {
+  public final class PagingDataTransforms {
+    method @CheckResult public static <T> androidx.paging.PagingData<T> filter(androidx.paging.PagingData<T>, java.util.concurrent.Executor executor, kotlin.jvm.functions.Function1<? super T,java.lang.Boolean> predicate);
+    method @CheckResult public static <T, R> androidx.paging.PagingData<R> flatMap(androidx.paging.PagingData<T>, java.util.concurrent.Executor executor, kotlin.jvm.functions.Function1<? super T,? extends java.lang.Iterable<? extends R>> transform);
+    method @CheckResult public static <T> androidx.paging.PagingData<T> insertFooterItem(androidx.paging.PagingData<T>, T item);
+    method @CheckResult public static <T> androidx.paging.PagingData<T> insertHeaderItem(androidx.paging.PagingData<T>, T item);
+    method @CheckResult public static <R, T extends R> androidx.paging.PagingData<R> insertSeparators(androidx.paging.PagingData<T>, java.util.concurrent.Executor executor, kotlin.jvm.functions.Function2<? super T,? super T,? extends R> generator);
+    method @CheckResult public static <T, R> androidx.paging.PagingData<R> map(androidx.paging.PagingData<T>, java.util.concurrent.Executor executor, kotlin.jvm.functions.Function1<? super T,? extends R> transform);
   }
 
   public abstract class PagingSource<Key, Value> {
diff --git a/paging/common/api/restricted_current.txt b/paging/common/api/restricted_current.txt
index c0867a3..5a10493 100644
--- a/paging/common/api/restricted_current.txt
+++ b/paging/common/api/restricted_current.txt
@@ -291,23 +291,22 @@
 
   public final class PagingData<T> {
     method public static <T> androidx.paging.PagingData<T> empty();
-    method @CheckResult public androidx.paging.PagingData<T> filter(kotlin.jvm.functions.Function1<? super T,java.lang.Boolean> predicate);
-    method @CheckResult public <R> androidx.paging.PagingData<R> flatMap(kotlin.jvm.functions.Function1<? super T,? extends java.lang.Iterable<? extends R>> transform);
     method public static <T> androidx.paging.PagingData<T> from(java.util.List<? extends T> data);
-    method @CheckResult public androidx.paging.PagingData<T> insertFooterItem(T item);
-    method @CheckResult public androidx.paging.PagingData<T> insertHeaderItem(T item);
-    method @CheckResult public static <T extends R, R> androidx.paging.PagingData<R> insertSeparators(androidx.paging.PagingData<T> pagingData, kotlin.jvm.functions.Function2<? super T,? super T,? extends R> generator);
-    method @CheckResult public <R> androidx.paging.PagingData<R> map(kotlin.jvm.functions.Function1<? super T,? extends R> transform);
     field public static final androidx.paging.PagingData.Companion Companion;
   }
 
   public static final class PagingData.Companion {
     method public <T> androidx.paging.PagingData<T> empty();
     method public <T> androidx.paging.PagingData<T> from(java.util.List<? extends T> data);
-    method @CheckResult public <T extends R, R> androidx.paging.PagingData<R> insertSeparators(androidx.paging.PagingData<T> pagingData, kotlin.jvm.functions.Function2<? super T,? super T,? extends R> generator);
   }
 
-  public final class PagingDataKt {
+  public final class PagingDataTransforms {
+    method @CheckResult public static <T> androidx.paging.PagingData<T> filter(androidx.paging.PagingData<T>, java.util.concurrent.Executor executor, kotlin.jvm.functions.Function1<? super T,java.lang.Boolean> predicate);
+    method @CheckResult public static <T, R> androidx.paging.PagingData<R> flatMap(androidx.paging.PagingData<T>, java.util.concurrent.Executor executor, kotlin.jvm.functions.Function1<? super T,? extends java.lang.Iterable<? extends R>> transform);
+    method @CheckResult public static <T> androidx.paging.PagingData<T> insertFooterItem(androidx.paging.PagingData<T>, T item);
+    method @CheckResult public static <T> androidx.paging.PagingData<T> insertHeaderItem(androidx.paging.PagingData<T>, T item);
+    method @CheckResult public static <R, T extends R> androidx.paging.PagingData<R> insertSeparators(androidx.paging.PagingData<T>, java.util.concurrent.Executor executor, kotlin.jvm.functions.Function2<? super T,? super T,? extends R> generator);
+    method @CheckResult public static <T, R> androidx.paging.PagingData<R> map(androidx.paging.PagingData<T>, java.util.concurrent.Executor executor, kotlin.jvm.functions.Function1<? super T,? extends R> transform);
   }
 
   public abstract class PagingSource<Key, Value> {
diff --git a/paging/common/src/main/kotlin/androidx/paging/PagingData.kt b/paging/common/src/main/kotlin/androidx/paging/PagingData.kt
index 61548a2..7a320d3 100644
--- a/paging/common/src/main/kotlin/androidx/paging/PagingData.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/PagingData.kt
@@ -16,10 +16,8 @@
 
 package androidx.paging
 
-import androidx.annotation.CheckResult
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.flow.map
 
 /**
  * Container for Paged data from a single generation of loads.
@@ -31,80 +29,6 @@
     internal val flow: Flow<PageEvent<T>>,
     internal val receiver: UiReceiver
 ) {
-    /**
-     * Returns a [PagingData] containing the result of applying the given [transform] to each
-     * element, as it is loaded.
-     *
-     * @see map
-     */
-    @JvmName("map")
-    @CheckResult
-    fun <R : Any> mapSync(transform: (T) -> R): PagingData<R> = transform { event ->
-        event.map { transform(it) }
-    }
-
-    /**
-     * Returns a [PagingData] of all elements returned from applying the given [transform]
-     * to each element, as it is loaded.
-     *
-     * @see flatMap
-     */
-    @JvmName("flatMap")
-    @CheckResult
-    fun <R : Any> flatMapSync(transform: (T) -> Iterable<R>): PagingData<R> = transform { event ->
-        event.flatMap { transform(it) }
-    }
-
-    /**
-     * Returns a [PagingData] containing only elements matching the given [predicate]
-     *
-     * @see filter
-     */
-    @JvmName("filter")
-    @CheckResult
-    fun filterSync(predicate: (T) -> Boolean): PagingData<T> = transform { event ->
-        event.filter { predicate(it) }
-    }
-
-    /**
-     * Returns a [PagingData] containing each original element, with the passed header [item] added
-     * to the start of the list.
-     *
-     * The header [item] is added to a loaded page which marks the end of the data stream in the
-     * prepend direction by returning null in [PagingSource.LoadResult.Page.prevKey]. It will be
-     * removed if the first page in the list is dropped, which can happen in the case of loaded
-     * pages exceeding [PagedList.Config.maxSize].
-     *
-     * Note: This operation is not idempotent, calling it multiple times will continually add
-     * more headers to the start of the list, which can be useful if multiple header items are
-     * required.
-     *
-     * @see [insertFooterItem]
-     */
-    @CheckResult
-    fun insertHeaderItem(item: T) = insertSeparators { before, _ ->
-        if (before == null) item else null
-    }
-
-    /**
-     * Returns a [PagingData] containing each original element, with the passed footer [item] added
-     * to the end of the list.
-     *
-     * The footer [item] is added to a loaded page which marks the end of the data stream in the
-     * append direction, either by returning null in [PagingSource.LoadResult.Page.nextKey]. It
-     * will be removed if the first page in the list is dropped, which can happen in the case of
-     * loaded* pages exceeding [PagedList.Config.maxSize].
-     *
-     * Note: This operation is not idempotent, calling it multiple times will continually add
-     * more footers to the end of the list, which can be useful if multiple footer items are
-     * required.
-     *
-     * @see [insertHeaderItem]
-     */
-    @CheckResult
-    fun insertFooterItem(item: T) = insertSeparators { _, after ->
-        if (after == null) item else null
-    }
 
     companion object {
         internal val NOOP_RECEIVER = object : UiReceiver {
@@ -116,7 +40,7 @@
         }
 
         @Suppress("MemberVisibilityCanBePrivate") // synthetic access
-        internal val EMPTY = PagingData<Any>(
+        internal val EMPTY = PagingData(
             flow = flowOf(PageEvent.Insert.EMPTY_REFRESH_LOCAL),
             receiver = NOOP_RECEIVER
         )
@@ -156,172 +80,5 @@
             ),
             receiver = NOOP_RECEIVER
         )
-
-        // NOTE: samples in the doc below are manually imported from Java code in the samples
-        // project, since Java cannot be linked with @sample.
-        // DO NOT CHANGE THE BELOW COMMENT WITHOUT MAKING THE CORRESPONDING CHANGE IN `samples/`
-        /**
-         * Returns a [PagingData] containing each original element, with an optional separator
-         * generated by [generator], given the elements before and after (or null, in boundary
-         * conditions).
-         *
-         * Note that this transform is applied asynchronously, as pages are loaded. Potential
-         * separators between pages are only computed once both pages are loaded.
-         *
-         * **Kotlin callers should instead use the suspending extension function variant of
-         * insertSeparators**
-         *
-         * ```
-         * /*
-         *  * Create letter separators in an alphabetically sorted list.
-         *  *
-         *  * For example, if the input is:
-         *  *     "apple", "apricot", "banana", "carrot"
-         *  *
-         *  * The operator would output:
-         *  *     "A", "apple", "apricot", "B", "banana", "C", "carrot"
-         *  */
-         * pagingDataStream.map((pagingData) ->
-         *         // map outer stream, so we can perform transformations on each paging generation
-         *         PagingData.insertSeparators(pagingData,
-         *                 (@Nullable String before, @Nullable String after) -> {
-         *                     if (after != null && (before == null
-         *                             || before.charAt(0) != after.charAt(0))) {
-         *                         // separator - after is first item that starts with its first letter
-         *                         return Character.toString(Character.toUpperCase(after.charAt(0)));
-         *                     } else {
-         *                         // no separator - either end of list, or first
-         *                         // letters of items are the same
-         *                         return null;
-         *                     }
-         *                 }));
-         *
-         * /*
-         *  * Create letter separators in an alphabetically sorted list of Items, with UiModel objects.
-         *  *
-         *  * For example, if the input is (each an `Item`):
-         *  *     "apple", "apricot", "banana", "carrot"
-         *  *
-         *  * The operator would output a list of UiModels corresponding to:
-         *  *     "A", "apple", "apricot", "B", "banana", "C", "carrot"
-         *  */
-         * pagingDataStream.map((itemPagingData) -> {
-         *     // map outer stream, so we can perform transformations on each paging generation
-         *
-         *     // first convert items in stream to UiModel.Item
-         *     PagingData<UiModel.ItemModel> itemModelPagingData =
-         *             itemPagingData.map(UiModel.ItemModel::new);
-         *
-         *     // Now insert UiModel.Separators, which makes the PagingData of generic type UiModel
-         *     return PagingData.insertSeparators(
-         *             itemModelPagingData,
-         *             (@Nullable UiModel.ItemModel before, @Nullable UiModel.ItemModel after) -> {
-         *                 if (after != null && (before == null
-         *                         || before.item.label.charAt(0) != after.item.label.charAt(0))) {
-         *                     // separator - after is first item that starts with its first letter
-         *                     return new UiModel.SeparatorModel(
-         *                             Character.toUpperCase(after.item.label.charAt(0)));
-         *                 } else {
-         *                     // no separator - either end of list, or first
-         *                     // letters of items are the same
-         *                     return null;
-         *                 }
-         *             });
-         * });
-         *
-         * public class UiModel {
-         *     static class ItemModel extends UiModel {
-         *         public Item item;
-         *         ItemModel(Item item) {
-         *             this.item = item;
-         *         }
-         *     }
-         *     static class SeparatorModel extends UiModel {
-         *         public char character;
-         *         SeparatorModel(char character) {
-         *             this.character = character;
-         *         }
-         *     }
-         * }
-         */
-        @JvmStatic
-        @CheckResult
-        fun <T : R, R : Any> insertSeparators(
-            pagingData: PagingData<T>,
-            generator: (T?, T?) -> R?
-        ): PagingData<R> {
-            return pagingData.insertSeparators { before, after -> generator(before, after) }
-        }
     }
 }
-
-private inline fun <T : Any, R : Any> PagingData<T>.transform(
-    crossinline transform: suspend (PageEvent<T>) -> PageEvent<R>
-) = PagingData(
-    flow = flow.map { transform(it) },
-    receiver = receiver
-)
-
-/**
- * Returns a [PagingData] containing the result of applying the given [transform] to each
- * element, as it is loaded.
- */
-@CheckResult
-@JvmSynthetic
-fun <T : Any, R : Any> PagingData<T>.map(
-    transform: suspend (T) -> R
-): PagingData<R> = transform { it.map(transform) }
-
-/**
- * Returns a [PagingData] of all elements returned from applying the given [transform]
- * to each element, as it is loaded.
- */
-@CheckResult
-@JvmSynthetic
-fun <T : Any, R : Any> PagingData<T>.flatMap(
-    transform: suspend (T) -> Iterable<R>
-): PagingData<R> = transform { it.flatMap(transform) }
-
-/**
- * Returns a [PagingData] containing only elements matching the given [predicate]
- */
-@CheckResult
-@JvmSynthetic
-fun <T : Any> PagingData<T>.filter(
-    predicate: suspend (T) -> Boolean
-): PagingData<T> = transform { it.filter(predicate) }
-
-/**
- * Returns a [PagingData] containing each original element, with an optional separator
- * generated by [generator], given the elements before and after (or null, in boundary
- * conditions).
- *
- * Note that this transform is applied asynchronously, as pages are loaded. Potential
- * separators between pages are only computed once both pages are loaded.
- *
- * @sample androidx.paging.samples.insertSeparatorsSample
- * @sample androidx.paging.samples.insertSeparatorsUiModelSample
- */
-@CheckResult
-@JvmSynthetic
-fun <T : R, R : Any> PagingData<T>.insertSeparators(
-    generator: suspend (T?, T?) -> R?
-): PagingData<R> {
-    // This function must be an extension method, as it indirectly imposes a constraint on
-    // the type of T (because T extends R). Ideally it would be declared not be an
-    // extension, to make this method discoverable for Java callers, but we need to support
-    // the common UI model pattern for separators:
-    //     class UiModel
-    //     class ItemModel: UiModel
-    //     class SeparatorModel: UiModel
-    return PagingData(
-        flow = flow.insertEventSeparators(generator),
-        receiver = receiver
-    )
-}
-
-internal interface UiReceiver {
-    fun accessHint(viewportHint: ViewportHint)
-    fun retry()
-    fun refresh()
-}
diff --git a/paging/common/src/main/kotlin/androidx/paging/PagingDataTransforms.kt b/paging/common/src/main/kotlin/androidx/paging/PagingDataTransforms.kt
new file mode 100644
index 0000000..5f42dc1
--- /dev/null
+++ b/paging/common/src/main/kotlin/androidx/paging/PagingDataTransforms.kt
@@ -0,0 +1,284 @@
+/*
+ * Copyright 2019 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.
+ */
+
+@file:JvmName("PagingDataTransforms")
+
+package androidx.paging
+
+import androidx.annotation.CheckResult
+import kotlinx.coroutines.asCoroutineDispatcher
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.withContext
+import java.util.concurrent.Executor
+
+private inline fun <T : Any, R : Any> PagingData<T>.transform(
+    crossinline transform: suspend (PageEvent<T>) -> PageEvent<R>
+) = PagingData(
+    flow = flow.map { transform(it) },
+    receiver = receiver
+)
+
+/**
+ * Returns a [PagingData] containing the result of applying the given [transform] to each
+ * element, as it is loaded.
+ */
+@CheckResult
+@JvmSynthetic
+fun <T : Any, R : Any> PagingData<T>.map(
+    transform: suspend (T) -> R
+): PagingData<R> = transform { it.map(transform) }
+
+/**
+ * Returns a [PagingData] containing the result of applying the given [transform] to each
+ * element, as it is loaded.
+ *
+ * @see PagingData.map
+ */
+@CheckResult
+fun <T : Any, R : Any> PagingData<T>.map(
+    executor: Executor,
+    transform: (T) -> R,
+): PagingData<R> = transform { event ->
+    withContext(executor.asCoroutineDispatcher()) {
+        event.map { transform(it) }
+    }
+}
+
+/**
+ * Returns a [PagingData] of all elements returned from applying the given [transform]
+ * to each element, as it is loaded.
+ */
+@CheckResult
+@JvmSynthetic
+fun <T : Any, R : Any> PagingData<T>.flatMap(
+    transform: suspend (T) -> Iterable<R>
+): PagingData<R> = transform { it.flatMap(transform) }
+
+/**
+ * Returns a [PagingData] of all elements returned from applying the given [transform]
+ * to each element, as it is loaded.
+ *
+ * @see flatMap
+ */
+@CheckResult
+fun <T : Any, R : Any> PagingData<T>.flatMap(
+    executor: Executor,
+    transform: (T) -> Iterable<R>
+): PagingData<R> = transform { event ->
+    withContext(executor.asCoroutineDispatcher()) {
+        event.flatMap { transform(it) }
+    }
+}
+
+/**
+ * Returns a [PagingData] containing only elements matching the given [predicate]
+ */
+@CheckResult
+@JvmSynthetic
+fun <T : Any> PagingData<T>.filter(
+    predicate: suspend (T) -> Boolean
+): PagingData<T> = transform { it.filter(predicate) }
+
+/**
+ * Returns a [PagingData] containing only elements matching the given [predicate].
+ *
+ * @see filter
+ */
+@CheckResult
+@JvmName("filter")
+fun <T : Any> PagingData<T>.filter(
+    executor: Executor,
+    predicate: (T) -> Boolean
+): PagingData<T> = transform { event ->
+    withContext(executor.asCoroutineDispatcher()) {
+        event.filter { predicate(it) }
+    }
+}
+
+/**
+ * Returns a [PagingData] containing each original element, with an optional separator
+ * generated by [generator], given the elements before and after (or null, in boundary
+ * conditions).
+ *
+ * Note that this transform is applied asynchronously, as pages are loaded. Potential
+ * separators between pages are only computed once both pages are loaded.
+ *
+ * @sample androidx.paging.samples.insertSeparatorsSample
+ * @sample androidx.paging.samples.insertSeparatorsUiModelSample
+ */
+@CheckResult
+@JvmSynthetic
+fun <T : R, R : Any> PagingData<T>.insertSeparators(
+    generator: suspend (T?, T?) -> R?
+): PagingData<R> {
+    // This function must be an extension method, as it indirectly imposes a constraint on
+    // the type of T (because T extends R). Ideally it would be declared not be an
+    // extension, to make this method discoverable for Java callers, but we need to support
+    // the common UI model pattern for separators:
+    //     class UiModel
+    //     class ItemModel: UiModel
+    //     class SeparatorModel: UiModel
+    return PagingData(
+        flow = flow.insertEventSeparators(generator),
+        receiver = receiver
+    )
+}
+
+// NOTE: samples in the doc below are manually imported from Java code in the samples
+// project, since Java cannot be linked with @sample.
+// DO NOT CHANGE THE BELOW COMMENT WITHOUT MAKING THE CORRESPONDING CHANGE IN `samples/`
+/**
+ *
+ * Returns a [PagingData] containing each original element, with an optional separator
+ * generated by [generator], given the elements before and after (or null, in boundary
+ * conditions).
+ *
+ * Note that this transform is applied asynchronously, as pages are loaded. Potential
+ * separators between pages are only computed once both pages are loaded.
+ *
+ * **Kotlin callers should instead use the suspending extension function variant of
+ * insertSeparators**
+ *
+ * ```
+ * /*
+ *  * Create letter separators in an alphabetically sorted list.
+ *  *
+ *  * For example, if the input is:
+ *  *     "apple", "apricot", "banana", "carrot"
+ *  *
+ *  * The operator would output:
+ *  *     "A", "apple", "apricot", "B", "banana", "C", "carrot"
+ *  */
+ * pagingDataStream.map(pagingData ->
+ *         // map outer stream, so we can perform transformations on each paging generation
+ *         PagingDataTransforms.insertSeparators(pagingData, bgExecutor,
+ *                 (@Nullable String before, @Nullable String after) -> {
+ *                     if (after != null && (before == null
+ *                             || before.charAt(0) != after.charAt(0))) {
+ *                         // separator - after is first item that starts with its first
+ *                         // letter
+ *                         return Character.toString(
+ *                                 Character.toUpperCase(after.charAt(0)));
+ *                     } else {
+ *                         // no separator - either end of list, or first
+ *                         // letters of items are the same
+ *                         return null;
+ *                     }
+ *                 }));
+ *
+ * /*
+ *  * Create letter separators in an alphabetically sorted list of Items, with UiModel
+ *  * objects.
+ *  *
+ *  * For example, if the input is (each an `Item`):
+ *  *     "apple", "apricot", "banana", "carrot"
+ *  *
+ *  * The operator would output a list of UiModels corresponding to:
+ *  *     "A", "apple", "apricot", "B", "banana", "C", "carrot"
+ *  */
+ * pagingDataStream.map(itemPagingData -> {
+ *     // map outer stream, so we can perform transformations on each paging generation
+ *
+ *     // first convert items in stream to UiModel.Item
+ *     PagingData<UiModel.ItemModel> itemModelPagingData = PagingDataTransforms.map(
+ *             itemPagingData, bgExecutor, UiModel.ItemModel::new);
+ *
+ *     // Now insert UiModel.Separators, which makes the PagingData of generic type UiModel
+ *     return PagingDataTransforms.insertSeparators(
+ *             itemModelPagingData, bgExecutor,
+ *             (@Nullable UiModel.ItemModel before, @Nullable UiModel.ItemModel after) -> {
+ *                 if (after != null && (before == null
+ *                         || before.item.label.charAt(0) != after.item.label.charAt(0))) {
+ *                     // separator - after is first item that starts with its first letter
+ *                     return new UiModel.SeparatorModel(
+ *                             Character.toUpperCase(after.item.label.charAt(0)));
+ *                 } else {
+ *                     // no separator - either end of list, or first
+ *                     // letters of items are the same
+ *                     return null;
+ *                 }
+ *             });
+ * });
+ *
+ * public class UiModel {
+ *     static class ItemModel extends UiModel {
+ *         public Item item;
+ *         ItemModel(Item item) {
+ *             this.item = item;
+ *         }
+ *     }
+ *     static class SeparatorModel extends UiModel {
+ *         public char character;
+ *         SeparatorModel(char character) {
+ *             this.character = character;
+ *         }
+ *     }
+ * }
+ *
+ *
+ * ```
+ */
+@CheckResult
+fun <R : Any, T : R> PagingData<T>.insertSeparators(
+    executor: Executor,
+    generator: (T?, T?) -> R?
+): PagingData<R> {
+    return insertSeparators { before, after ->
+        withContext(executor.asCoroutineDispatcher()) {
+            generator(before, after)
+        }
+    }
+}
+
+/**
+ * Returns a [PagingData] containing each original element, with the passed header [item] added
+ * to the start of the list.
+ *
+ * The header [item] is added to a loaded page which marks the end of the data stream in the
+ * prepend direction by returning null in [PagingSource.LoadResult.Page.prevKey]. It will be
+ * removed if the first page in the list is dropped, which can happen in the case of loaded
+ * pages exceeding [PagedList.Config.maxSize].
+ *
+ * Note: This operation is not idempotent, calling it multiple times will continually add
+ * more headers to the start of the list, which can be useful if multiple header items are
+ * required.
+ *
+ * @see [insertFooterItem]
+ */
+@CheckResult
+fun <T : Any> PagingData<T>.insertHeaderItem(item: T) = insertSeparators { before, _ ->
+    if (before == null) item else null
+}
+
+/**
+ * Returns a [PagingData] containing each original element, with the passed footer [item] added
+ * to the end of the list.
+ *
+ * The footer [item] is added to a loaded page which marks the end of the data stream in the
+ * append direction, either by returning null in [PagingSource.LoadResult.Page.nextKey]. It
+ * will be removed if the first page in the list is dropped, which can happen in the case of
+ * loaded* pages exceeding [PagedList.Config.maxSize].
+ *
+ * Note: This operation is not idempotent, calling it multiple times will continually add
+ * more footers to the end of the list, which can be useful if multiple footer items are
+ * required.
+ *
+ * @see [insertHeaderItem]
+ */
+@CheckResult
+fun <T : Any> PagingData<T>.insertFooterItem(item: T) = insertSeparators { _, after ->
+    if (after == null) item else null
+}
diff --git a/paging/common/src/main/kotlin/androidx/paging/UiReceiver.kt b/paging/common/src/main/kotlin/androidx/paging/UiReceiver.kt
new file mode 100644
index 0000000..f7690ef
--- /dev/null
+++ b/paging/common/src/main/kotlin/androidx/paging/UiReceiver.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2020 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
+
+/**
+ * Fetcher-side callbacks for presenter-side events communicated through [PagingData].
+ */
+internal interface UiReceiver {
+    fun accessHint(viewportHint: ViewportHint)
+    fun retry()
+    fun refresh()
+}
diff --git a/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/v3/V3ViewModel.kt b/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/v3/V3ViewModel.kt
index ca57505..2cd36a9 100644
--- a/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/v3/V3ViewModel.kt
+++ b/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/v3/V3ViewModel.kt
@@ -22,6 +22,8 @@
 import androidx.paging.Pager
 import androidx.paging.PagingConfig
 import androidx.paging.cachedIn
+import androidx.paging.insertFooterItem
+import androidx.paging.insertHeaderItem
 import androidx.paging.insertSeparators
 import kotlinx.coroutines.flow.map
 
diff --git a/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/v3room/V3RoomViewModel.kt b/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/v3room/V3RoomViewModel.kt
index 8c84a91..c336fdb 100644
--- a/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/v3room/V3RoomViewModel.kt
+++ b/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/v3room/V3RoomViewModel.kt
@@ -25,6 +25,8 @@
 import androidx.paging.Pager
 import androidx.paging.PagingConfig
 import androidx.paging.cachedIn
+import androidx.paging.insertFooterItem
+import androidx.paging.insertHeaderItem
 import androidx.paging.insertSeparators
 import androidx.paging.integration.testapp.room.Customer
 import androidx.paging.integration.testapp.room.SampleDatabase
diff --git a/paging/samples/src/main/java/androidx/paging/samples/java/InsertSeparatorsJavaSample.java b/paging/samples/src/main/java/androidx/paging/samples/java/InsertSeparatorsJavaSample.java
index 47998ff..ba54d807 100644
--- a/paging/samples/src/main/java/androidx/paging/samples/java/InsertSeparatorsJavaSample.java
+++ b/paging/samples/src/main/java/androidx/paging/samples/java/InsertSeparatorsJavaSample.java
@@ -20,6 +20,9 @@
 
 import androidx.annotation.Nullable;
 import androidx.paging.PagingData;
+import androidx.paging.PagingDataTransforms;
+
+import java.util.concurrent.Executor;
 
 import io.reactivex.Flowable;
 import kotlin.NotImplementedError;
@@ -28,7 +31,7 @@
  * NOTE - MANUALLY COPIED SAMPLE
  *
  * Since @sample from kdoc doesn't support Java, this code must manually kept in sync with
- * the @JvmStatic `PagingData.insertSeparators` method
+ * the `PagingDataTransforms.insertSeparators` method
  */
 @SuppressWarnings({"unused", "WeakerAccess"})
 class InsertSeparatorsJavaSample {
@@ -39,6 +42,8 @@
         throw new NotImplementedError();
     }
 
+    public Executor bgExecutor;
+
     @SuppressLint("CheckResult")
     @SuppressWarnings({"unused", "ResultOfMethodCallIgnored", "RxReturnValueIgnored"})
     public void insertSeparatorsSample() {
@@ -51,14 +56,16 @@
          * The operator would output:
          *     "A", "apple", "apricot", "B", "banana", "C", "carrot"
          */
-        pagingDataStream.map((pagingData) ->
+        pagingDataStream.map(pagingData ->
                 // map outer stream, so we can perform transformations on each paging generation
-                PagingData.insertSeparators(pagingData,
+                PagingDataTransforms.insertSeparators(pagingData, bgExecutor,
                         (@Nullable String before, @Nullable String after) -> {
                             if (after != null && (before == null
                                     || before.charAt(0) != after.charAt(0))) {
-                                // separator - after is first item that starts with its first letter
-                                return Character.toString(Character.toUpperCase(after.charAt(0)));
+                                // separator - after is first item that starts with its first
+                                // letter
+                                return Character.toString(
+                                        Character.toUpperCase(after.charAt(0)));
                             } else {
                                 // no separator - either end of list, or first
                                 // letters of items are the same
diff --git a/paging/samples/src/main/java/androidx/paging/samples/java/InsertSeparatorsJavaUiModelSample.java b/paging/samples/src/main/java/androidx/paging/samples/java/InsertSeparatorsJavaUiModelSample.java
index 2aa1172..5bf43a5 100644
--- a/paging/samples/src/main/java/androidx/paging/samples/java/InsertSeparatorsJavaUiModelSample.java
+++ b/paging/samples/src/main/java/androidx/paging/samples/java/InsertSeparatorsJavaUiModelSample.java
@@ -20,6 +20,9 @@
 
 import androidx.annotation.Nullable;
 import androidx.paging.PagingData;
+import androidx.paging.PagingDataTransforms;
+
+import java.util.concurrent.Executor;
 
 import io.reactivex.Flowable;
 import kotlin.NotImplementedError;
@@ -28,7 +31,7 @@
  * NOTE - MANUALLY COPIED SAMPLE
  *
  * Since @sample from kdoc doesn't support Java, this code must manually kept in sync with
- * the @JvmStatic `PagingData.insertSeparators` method
+ * the `PagingDataTransforms.insertSeparators` method
  */
 @SuppressWarnings({"unused", "WeakerAccess"})
 class InsertSeparatorsJavaUiModelSample {
@@ -38,12 +41,15 @@
         throw new NotImplementedError();
     }
 
+    public Executor bgExecutor;
+
     @SuppressLint("CheckResult")
     @SuppressWarnings({"unused", "ResultOfMethodCallIgnored", "RxReturnValueIgnored"})
     public void insertSeparatorsSample() {
 
         /*
-         * Create letter separators in an alphabetically sorted list of Items, with UiModel objects.
+         * Create letter separators in an alphabetically sorted list of Items, with UiModel
+         * objects.
          *
          * For example, if the input is (each an `Item`):
          *     "apple", "apricot", "banana", "carrot"
@@ -55,12 +61,12 @@
             // map outer stream, so we can perform transformations on each paging generation
 
             // first convert items in stream to UiModel.Item
-            PagingData<UiModel.ItemModel> itemModelPagingData =
-                    itemPagingData.map(UiModel.ItemModel::new);
+            PagingData<UiModel.ItemModel> itemModelPagingData = PagingDataTransforms.map(
+                    itemPagingData, bgExecutor, UiModel.ItemModel::new);
 
             // Now insert UiModel.Separators, which makes the PagingData of generic type UiModel
-            return PagingData.insertSeparators(
-                    itemModelPagingData,
+            return PagingDataTransforms.insertSeparators(
+                    itemModelPagingData, bgExecutor,
                     (@Nullable UiModel.ItemModel before, @Nullable UiModel.ItemModel after) -> {
                         if (after != null && (before == null
                                 || before.item.label.charAt(0) != after.item.label.charAt(0))) {
diff --git a/room/compiler/src/main/kotlin/androidx/room/solver/types/EnumColumnTypeAdapter.kt b/room/compiler/src/main/kotlin/androidx/room/solver/types/EnumColumnTypeAdapter.kt
index 4527dd2..2f01519 100644
--- a/room/compiler/src/main/kotlin/androidx/room/solver/types/EnumColumnTypeAdapter.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/solver/types/EnumColumnTypeAdapter.kt
@@ -96,9 +96,10 @@
                             addStatement("case $L: return $S", enumConstant.name, enumConstant.name)
                         }
                         addStatement(
-                            "default: throw new $T($S)",
+                            "default: throw new $T($S + $N)",
                             ILLEGAL_ARG_EXCEPTION,
-                            "Can't convert ${param.name} to string, unknown enum value."
+                            "Can't convert enum to string, unknown enum value: ",
+                            param
                         )
                         endControlFlow()
                     }
@@ -136,9 +137,10 @@
                             )
                         }
                         addStatement(
-                            "default: throw new $T($S)",
+                            "default: throw new $T($S + $N)",
                             ILLEGAL_ARG_EXCEPTION,
-                            "Can't convert ${param.name} to enum, unknown value."
+                            "Can't convert value to enum, unknown value: ",
+                            param
                         )
                         endControlFlow()
                     }
diff --git a/samples/SupportLeanbackDemos/src/main/java/com/example/android/leanback/PhotoViewModel.java b/samples/SupportLeanbackDemos/src/main/java/com/example/android/leanback/PhotoViewModel.java
index 0f1b5c5..9fe6a2b 100644
--- a/samples/SupportLeanbackDemos/src/main/java/com/example/android/leanback/PhotoViewModel.java
+++ b/samples/SupportLeanbackDemos/src/main/java/com/example/android/leanback/PhotoViewModel.java
@@ -24,17 +24,22 @@
 import androidx.paging.Pager;
 import androidx.paging.PagingConfig;
 import androidx.paging.PagingData;
+import androidx.paging.PagingDataTransforms;
 import androidx.paging.PagingLiveData;
 
 import com.example.android.leanback.room.Photo;
 import com.example.android.leanback.room.PhotoDatabase;
 
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
 /**
  * ViewModel for showing sample usage of PagedListAdapter
  */
 public class PhotoViewModel extends AndroidViewModel {
 
     private LiveData<PagingData<PhotoItem>> mPagingDataLiveData;
+    private Executor mExecutor = Executors.newSingleThreadExecutor();
 
     public PhotoViewModel(Application application) {
         super(application);
@@ -47,9 +52,10 @@
 
         mPagingDataLiveData = Transformations.map(PagingLiveData.getLiveData(pager),
                 (Function<PagingData<Photo>, PagingData<PhotoItem>>) pagingData ->
-                    pagingData.map((photo) -> new PhotoItem(photo.getTitle(),
-                            photo.getImgResourceId(),
-                            photo.getId()))
+                        PagingDataTransforms.map(pagingData, mExecutor,
+                                (photo) -> new PhotoItem(photo.getTitle(),
+                                        photo.getImgResourceId(),
+                                        photo.getId()))
         );