Convert paging-common to .kt

DataSource.BaseResult is made public since Kotlin disallows changing
visibility of a derived class from its base class or different
visibility between a function and its parameters.

Test: ./gradlew paging:paging-common:check
Change-Id: Ia89cb55fdc2e74e681e299103aba1fba5b8b5abc
diff --git a/paging/common/api/2.2.0-alpha01.ignore b/paging/common/api/2.2.0-alpha01.ignore
index e81e503..6d27f48 100644
--- a/paging/common/api/2.2.0-alpha01.ignore
+++ b/paging/common/api/2.2.0-alpha01.ignore
@@ -1,7 +1,9 @@
 // Baseline format: 1.0
+AddedAbstractMethod: androidx.paging.PagedList#isContiguous():
+    Added method androidx.paging.PagedList.isContiguous()
+
+
 ChangedAbstract: androidx.paging.PagedList#detach():
     Method androidx.paging.PagedList.detach has changed 'abstract' qualifier
 ChangedAbstract: androidx.paging.PagedList#isDetached():
     Method androidx.paging.PagedList.isDetached has changed 'abstract' qualifier
-
-
diff --git a/paging/common/api/2.2.0-alpha01.txt b/paging/common/api/2.2.0-alpha01.txt
index cf51b44..f5eea3a 100644
--- a/paging/common/api/2.2.0-alpha01.txt
+++ b/paging/common/api/2.2.0-alpha01.txt
@@ -2,20 +2,34 @@
 package androidx.paging {
 
   public abstract class DataSource<Key, Value> {
-    method @AnyThread public void addInvalidatedCallback(androidx.paging.DataSource.InvalidatedCallback);
+    method @AnyThread public void addInvalidatedCallback(androidx.paging.DataSource.InvalidatedCallback onInvalidatedCallback);
+    method protected final java.util.concurrent.Executor getExecutor();
     method @AnyThread public void invalidate();
     method @WorkerThread public boolean isInvalid();
-    method public boolean isRetryableError(Throwable);
-    method public <ToValue> androidx.paging.DataSource<Key!,ToValue!> map(androidx.arch.core.util.Function<Value!,ToValue!>);
-    method public <ToValue> androidx.paging.DataSource<Key!,ToValue!> mapByPage(androidx.arch.core.util.Function<java.util.List<Value!>!,java.util.List<ToValue!>!>);
-    method @AnyThread public void removeInvalidatedCallback(androidx.paging.DataSource.InvalidatedCallback);
+    method public boolean isRetryableError(Throwable error);
+    method public <ToValue> androidx.paging.DataSource<Key,ToValue> map(androidx.arch.core.util.Function<Value,ToValue> function);
+    method public <ToValue> androidx.paging.DataSource<Key,ToValue> mapByPage(androidx.arch.core.util.Function<java.util.List<Value>,java.util.List<ToValue>> function);
+    method @AnyThread public void removeInvalidatedCallback(androidx.paging.DataSource.InvalidatedCallback onInvalidatedCallback);
+    property protected final java.util.concurrent.Executor executor;
+    property @WorkerThread public boolean isInvalid;
+  }
+
+  public static class DataSource.BaseResult<Value> {
+    ctor protected DataSource.BaseResult(java.util.List<? extends Value> data, Object? prevKey, Object? nextKey, int leadingNulls, int trailingNulls, int offset, boolean counted);
+    method public final boolean getCounted();
+    method public final int getLeadingNulls();
+    method public final Object? getNextKey();
+    method public final int getOffset();
+    method public final Object? getPrevKey();
+    method public final int getTrailingNulls();
+    field public final java.util.List<Value> data;
   }
 
   public abstract static class DataSource.Factory<Key, Value> {
     ctor public DataSource.Factory();
-    method public abstract androidx.paging.DataSource<Key!,Value!> create();
-    method public <ToValue> androidx.paging.DataSource.Factory<Key!,ToValue!> map(androidx.arch.core.util.Function<Value!,ToValue!>);
-    method public <ToValue> androidx.paging.DataSource.Factory<Key!,ToValue!> mapByPage(androidx.arch.core.util.Function<java.util.List<Value!>!,java.util.List<ToValue!>!>);
+    method public abstract androidx.paging.DataSource<Key,Value> create();
+    method public <ToValue> androidx.paging.DataSource.Factory<Key,ToValue> map(androidx.arch.core.util.Function<Value,ToValue> function);
+    method public <ToValue> androidx.paging.DataSource.Factory<Key,ToValue> mapByPage(androidx.arch.core.util.Function<java.util.List<Value>,java.util.List<ToValue>> function);
   }
 
   public static interface DataSource.InvalidatedCallback {
@@ -24,113 +38,108 @@
 
   public abstract class ItemKeyedDataSource<Key, Value> extends androidx.paging.ListenableItemKeyedDataSource<Key,Value> {
     ctor public ItemKeyedDataSource();
-    method public final com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenableItemKeyedDataSource.Result<Value!>!> loadAfter(androidx.paging.ListenableItemKeyedDataSource.LoadParams<Key!>);
-    method public abstract void loadAfter(androidx.paging.ItemKeyedDataSource.LoadParams<Key!>, androidx.paging.ItemKeyedDataSource.LoadCallback<Value!>);
-    method public final com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenableItemKeyedDataSource.Result<Value!>!> loadBefore(androidx.paging.ListenableItemKeyedDataSource.LoadParams<Key!>);
-    method public abstract void loadBefore(androidx.paging.ItemKeyedDataSource.LoadParams<Key!>, androidx.paging.ItemKeyedDataSource.LoadCallback<Value!>);
-    method public final com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenableItemKeyedDataSource.InitialResult<Value!>!> loadInitial(androidx.paging.ListenableItemKeyedDataSource.LoadInitialParams<Key!>);
-    method public abstract void loadInitial(androidx.paging.ItemKeyedDataSource.LoadInitialParams<Key!>, androidx.paging.ItemKeyedDataSource.LoadInitialCallback<Value!>);
-    method public final <ToValue> androidx.paging.ItemKeyedDataSource<Key!,ToValue!> map(androidx.arch.core.util.Function<Value!,ToValue!>);
-    method public final <ToValue> androidx.paging.ItemKeyedDataSource<Key!,ToValue!> mapByPage(androidx.arch.core.util.Function<java.util.List<Value!>!,java.util.List<ToValue!>!>);
+    method public final com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenableItemKeyedDataSource.Result<Value>> loadAfter(androidx.paging.ListenableItemKeyedDataSource.LoadParams<Key> params);
+    method public abstract void loadAfter(androidx.paging.ItemKeyedDataSource.LoadParams<Key> params, androidx.paging.ItemKeyedDataSource.LoadCallback<Value> callback);
+    method public final com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenableItemKeyedDataSource.Result<Value>> loadBefore(androidx.paging.ListenableItemKeyedDataSource.LoadParams<Key> params);
+    method public abstract void loadBefore(androidx.paging.ItemKeyedDataSource.LoadParams<Key> params, androidx.paging.ItemKeyedDataSource.LoadCallback<Value> callback);
+    method public final com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenableItemKeyedDataSource.InitialResult<Value>> loadInitial(androidx.paging.ListenableItemKeyedDataSource.LoadInitialParams<Key> params);
+    method public abstract void loadInitial(androidx.paging.ItemKeyedDataSource.LoadInitialParams<Key> params, androidx.paging.ItemKeyedDataSource.LoadInitialCallback<Value> callback);
+    method public final <ToValue> androidx.paging.ItemKeyedDataSource<Key,ToValue> map(androidx.arch.core.util.Function<Value,ToValue> function);
+    method public final <ToValue> androidx.paging.ItemKeyedDataSource<Key,ToValue> mapByPage(androidx.arch.core.util.Function<java.util.List<Value>,java.util.List<ToValue>> function);
   }
 
   public abstract static class ItemKeyedDataSource.LoadCallback<Value> {
     ctor public ItemKeyedDataSource.LoadCallback();
-    method public void onError(Throwable);
-    method public abstract void onResult(java.util.List<Value!>);
+    method public void onError(Throwable error);
+    method public abstract void onResult(java.util.List<? extends Value> data);
   }
 
   public abstract static class ItemKeyedDataSource.LoadInitialCallback<Value> extends androidx.paging.ItemKeyedDataSource.LoadCallback<Value> {
     ctor public ItemKeyedDataSource.LoadInitialCallback();
-    method public abstract void onResult(java.util.List<Value!>, int, int);
+    method public abstract void onResult(java.util.List<? extends Value> data, int position, int totalCount);
   }
 
   public static class ItemKeyedDataSource.LoadInitialParams<Key> extends androidx.paging.ListenableItemKeyedDataSource.LoadInitialParams<Key> {
-    ctor public ItemKeyedDataSource.LoadInitialParams(Key?, int, boolean);
+    ctor public ItemKeyedDataSource.LoadInitialParams(Key? requestedInitialKey, int requestedLoadSize, boolean placeholdersEnabled);
   }
 
   public static class ItemKeyedDataSource.LoadParams<Key> extends androidx.paging.ListenableItemKeyedDataSource.LoadParams<Key> {
-    ctor public ItemKeyedDataSource.LoadParams(Key, int);
+    ctor public ItemKeyedDataSource.LoadParams(Key key, int requestedLoadSize);
   }
 
   public abstract class ListenableItemKeyedDataSource<Key, Value> extends androidx.paging.DataSource<Key,Value> {
     ctor public ListenableItemKeyedDataSource();
-    method public abstract Key? getKey(Value);
-    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenableItemKeyedDataSource.Result<Value!>!> loadAfter(androidx.paging.ListenableItemKeyedDataSource.LoadParams<Key!>);
-    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenableItemKeyedDataSource.Result<Value!>!> loadBefore(androidx.paging.ListenableItemKeyedDataSource.LoadParams<Key!>);
-    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenableItemKeyedDataSource.InitialResult<Value!>!> loadInitial(androidx.paging.ListenableItemKeyedDataSource.LoadInitialParams<Key!>);
+    method public abstract Key getKey(Value item);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenableItemKeyedDataSource.Result<Value>> loadAfter(androidx.paging.ListenableItemKeyedDataSource.LoadParams<Key> params);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenableItemKeyedDataSource.Result<Value>> loadBefore(androidx.paging.ListenableItemKeyedDataSource.LoadParams<Key> params);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenableItemKeyedDataSource.InitialResult<Value>> loadInitial(androidx.paging.ListenableItemKeyedDataSource.LoadInitialParams<Key> params);
   }
 
-  public static class ListenableItemKeyedDataSource.InitialResult<V> {
-    ctor public ListenableItemKeyedDataSource.InitialResult(java.util.List<V!>, int, int);
-    ctor public ListenableItemKeyedDataSource.InitialResult(java.util.List<V!>);
-    method public boolean equals(Object!);
+  public static class ListenableItemKeyedDataSource.InitialResult<V> extends androidx.paging.DataSource.BaseResult<V> {
+    ctor public ListenableItemKeyedDataSource.InitialResult(java.util.List<? extends V> data, int position, int totalCount);
+    ctor public ListenableItemKeyedDataSource.InitialResult(java.util.List<? extends V> data);
   }
 
   public static class ListenableItemKeyedDataSource.LoadInitialParams<Key> {
-    ctor public ListenableItemKeyedDataSource.LoadInitialParams(Key?, int, boolean);
+    ctor public ListenableItemKeyedDataSource.LoadInitialParams(Key? requestedInitialKey, int requestedLoadSize, boolean placeholdersEnabled);
     field public final boolean placeholdersEnabled;
     field public final Key? requestedInitialKey;
     field public final int requestedLoadSize;
   }
 
   public static class ListenableItemKeyedDataSource.LoadParams<Key> {
-    ctor public ListenableItemKeyedDataSource.LoadParams(Key, int);
+    ctor public ListenableItemKeyedDataSource.LoadParams(Key key, int requestedLoadSize);
     field public final Key key;
     field public final int requestedLoadSize;
   }
 
-  public static class ListenableItemKeyedDataSource.Result<V> {
-    ctor public ListenableItemKeyedDataSource.Result(java.util.List<V!>);
-    method public boolean equals(Object!);
+  public static class ListenableItemKeyedDataSource.Result<V> extends androidx.paging.DataSource.BaseResult<V> {
+    ctor public ListenableItemKeyedDataSource.Result(java.util.List<? extends V> data);
   }
 
   public abstract class ListenablePageKeyedDataSource<Key, Value> extends androidx.paging.DataSource<Key,Value> {
     ctor public ListenablePageKeyedDataSource();
-    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenablePageKeyedDataSource.Result<Key!,Value!>!> loadAfter(androidx.paging.ListenablePageKeyedDataSource.LoadParams<Key!>);
-    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenablePageKeyedDataSource.Result<Key!,Value!>!> loadBefore(androidx.paging.ListenablePageKeyedDataSource.LoadParams<Key!>);
-    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenablePageKeyedDataSource.InitialResult<Key!,Value!>!> loadInitial(androidx.paging.ListenablePageKeyedDataSource.LoadInitialParams<Key!>);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenablePageKeyedDataSource.Result<Key,Value>> loadAfter(androidx.paging.ListenablePageKeyedDataSource.LoadParams<Key> params);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenablePageKeyedDataSource.Result<Key,Value>> loadBefore(androidx.paging.ListenablePageKeyedDataSource.LoadParams<Key> params);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenablePageKeyedDataSource.InitialResult<Key,Value>> loadInitial(androidx.paging.ListenablePageKeyedDataSource.LoadInitialParams<Key> params);
   }
 
-  public static class ListenablePageKeyedDataSource.InitialResult<Key, Value> {
-    ctor public ListenablePageKeyedDataSource.InitialResult(java.util.List<Value!>, int, int, Key?, Key?);
-    ctor public ListenablePageKeyedDataSource.InitialResult(java.util.List<Value!>, Key?, Key?);
-    method public boolean equals(Object!);
+  public static class ListenablePageKeyedDataSource.InitialResult<Key, Value> extends androidx.paging.DataSource.BaseResult<Value> {
+    ctor public ListenablePageKeyedDataSource.InitialResult(java.util.List<? extends Value> data, int position, int totalCount, Key? previousPageKey, Key? nextPageKey);
+    ctor public ListenablePageKeyedDataSource.InitialResult(java.util.List<? extends Value> data, Key? previousPageKey, Key? nextPageKey);
   }
 
   public static class ListenablePageKeyedDataSource.LoadInitialParams<Key> {
-    ctor public ListenablePageKeyedDataSource.LoadInitialParams(int, boolean);
+    ctor public ListenablePageKeyedDataSource.LoadInitialParams(int requestedLoadSize, boolean placeholdersEnabled);
     field public final boolean placeholdersEnabled;
     field public final int requestedLoadSize;
   }
 
   public static class ListenablePageKeyedDataSource.LoadParams<Key> {
-    ctor public ListenablePageKeyedDataSource.LoadParams(Key, int);
+    ctor public ListenablePageKeyedDataSource.LoadParams(Key key, int requestedLoadSize);
     field public final Key key;
     field public final int requestedLoadSize;
   }
 
-  public static class ListenablePageKeyedDataSource.Result<Key, Value> {
-    ctor public ListenablePageKeyedDataSource.Result(java.util.List<Value!>, Key?);
-    method public boolean equals(Object!);
+  public static class ListenablePageKeyedDataSource.Result<Key, Value> extends androidx.paging.DataSource.BaseResult<Value> {
+    ctor public ListenablePageKeyedDataSource.Result(java.util.List<? extends Value> data, Key? adjacentPageKey);
   }
 
   public abstract class ListenablePositionalDataSource<T> extends androidx.paging.DataSource<java.lang.Integer,T> {
     ctor public ListenablePositionalDataSource();
-    method public static int computeInitialLoadPosition(androidx.paging.ListenablePositionalDataSource.LoadInitialParams, int);
-    method public static int computeInitialLoadSize(androidx.paging.ListenablePositionalDataSource.LoadInitialParams, int, int);
-    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenablePositionalDataSource.InitialResult<T!>!> loadInitial(androidx.paging.ListenablePositionalDataSource.LoadInitialParams);
-    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenablePositionalDataSource.RangeResult<T!>!> loadRange(androidx.paging.ListenablePositionalDataSource.LoadRangeParams);
+    method public static final int computeInitialLoadPosition(androidx.paging.ListenablePositionalDataSource.LoadInitialParams params, int totalCount);
+    method public static final int computeInitialLoadSize(androidx.paging.ListenablePositionalDataSource.LoadInitialParams params, int initialLoadPosition, int totalCount);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenablePositionalDataSource.InitialResult<T>> loadInitial(androidx.paging.ListenablePositionalDataSource.LoadInitialParams params);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenablePositionalDataSource.RangeResult<T>> loadRange(androidx.paging.ListenablePositionalDataSource.LoadRangeParams params);
   }
 
-  public static class ListenablePositionalDataSource.InitialResult<V> {
-    ctor public ListenablePositionalDataSource.InitialResult(java.util.List<V!>, int, int);
-    ctor public ListenablePositionalDataSource.InitialResult(java.util.List<V!>, int);
-    method public boolean equals(Object!);
+  public static class ListenablePositionalDataSource.InitialResult<V> extends androidx.paging.DataSource.BaseResult<V> {
+    ctor public ListenablePositionalDataSource.InitialResult(java.util.List<? extends V> data, int position, int totalCount);
+    ctor public ListenablePositionalDataSource.InitialResult(java.util.List<? extends V> data, int position);
   }
 
   public static class ListenablePositionalDataSource.LoadInitialParams {
-    ctor public ListenablePositionalDataSource.LoadInitialParams(int, int, int, boolean);
+    ctor public ListenablePositionalDataSource.LoadInitialParams(int requestedStartPosition, int requestedLoadSize, int pageSize, boolean placeholdersEnabled);
     field public final int pageSize;
     field public final boolean placeholdersEnabled;
     field public final int requestedLoadSize;
@@ -138,92 +147,101 @@
   }
 
   public static class ListenablePositionalDataSource.LoadRangeParams {
-    ctor public ListenablePositionalDataSource.LoadRangeParams(int, int);
+    ctor public ListenablePositionalDataSource.LoadRangeParams(int startPosition, int loadSize);
     field public final int loadSize;
     field public final int startPosition;
   }
 
-  public static class ListenablePositionalDataSource.RangeResult<V> {
-    ctor public ListenablePositionalDataSource.RangeResult(java.util.List<V!>);
-    method public boolean equals(Object!);
+  public static class ListenablePositionalDataSource.RangeResult<V> extends androidx.paging.DataSource.BaseResult<V> {
+    ctor public ListenablePositionalDataSource.RangeResult(java.util.List<? extends V> data);
   }
 
   public abstract class PageKeyedDataSource<Key, Value> extends androidx.paging.ListenablePageKeyedDataSource<Key,Value> {
     ctor public PageKeyedDataSource();
-    method public final com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenablePageKeyedDataSource.Result<Key!,Value!>!> loadAfter(androidx.paging.ListenablePageKeyedDataSource.LoadParams<Key!>);
-    method public abstract void loadAfter(androidx.paging.PageKeyedDataSource.LoadParams<Key!>, androidx.paging.PageKeyedDataSource.LoadCallback<Key!,Value!>);
-    method public final com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenablePageKeyedDataSource.Result<Key!,Value!>!> loadBefore(androidx.paging.ListenablePageKeyedDataSource.LoadParams<Key!>);
-    method public abstract void loadBefore(androidx.paging.PageKeyedDataSource.LoadParams<Key!>, androidx.paging.PageKeyedDataSource.LoadCallback<Key!,Value!>);
-    method public final com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenablePageKeyedDataSource.InitialResult<Key!,Value!>!> loadInitial(androidx.paging.ListenablePageKeyedDataSource.LoadInitialParams<Key!>);
-    method public abstract void loadInitial(androidx.paging.PageKeyedDataSource.LoadInitialParams<Key!>, androidx.paging.PageKeyedDataSource.LoadInitialCallback<Key!,Value!>);
-    method public final <ToValue> androidx.paging.PageKeyedDataSource<Key!,ToValue!> map(androidx.arch.core.util.Function<Value!,ToValue!>);
-    method public final <ToValue> androidx.paging.PageKeyedDataSource<Key!,ToValue!> mapByPage(androidx.arch.core.util.Function<java.util.List<Value!>!,java.util.List<ToValue!>!>);
+    method public final com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenablePageKeyedDataSource.Result<Key,Value>> loadAfter(androidx.paging.ListenablePageKeyedDataSource.LoadParams<Key> params);
+    method public abstract void loadAfter(androidx.paging.PageKeyedDataSource.LoadParams<Key> params, androidx.paging.PageKeyedDataSource.LoadCallback<Key,Value> callback);
+    method public final com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenablePageKeyedDataSource.Result<Key,Value>> loadBefore(androidx.paging.ListenablePageKeyedDataSource.LoadParams<Key> params);
+    method public abstract void loadBefore(androidx.paging.PageKeyedDataSource.LoadParams<Key> params, androidx.paging.PageKeyedDataSource.LoadCallback<Key,Value> callback);
+    method public final com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenablePageKeyedDataSource.InitialResult<Key,Value>> loadInitial(androidx.paging.ListenablePageKeyedDataSource.LoadInitialParams<Key> params);
+    method public abstract void loadInitial(androidx.paging.PageKeyedDataSource.LoadInitialParams<Key> params, androidx.paging.PageKeyedDataSource.LoadInitialCallback<Key,Value> callback);
+    method public final <ToValue> androidx.paging.PageKeyedDataSource<Key,ToValue> map(androidx.arch.core.util.Function<Value,ToValue> function);
+    method public final <ToValue> androidx.paging.PageKeyedDataSource<Key,ToValue> mapByPage(androidx.arch.core.util.Function<java.util.List<Value>,java.util.List<ToValue>> function);
   }
 
   public abstract static class PageKeyedDataSource.LoadCallback<Key, Value> {
     ctor public PageKeyedDataSource.LoadCallback();
-    method public void onError(Throwable);
-    method public abstract void onResult(java.util.List<Value!>, Key?);
+    method public void onError(Throwable error);
+    method public abstract void onResult(java.util.List<? extends Value> data, Key? adjacentPageKey);
   }
 
   public abstract static class PageKeyedDataSource.LoadInitialCallback<Key, Value> {
     ctor public PageKeyedDataSource.LoadInitialCallback();
-    method public void onError(Throwable);
-    method public abstract void onResult(java.util.List<Value!>, int, int, Key?, Key?);
-    method public abstract void onResult(java.util.List<Value!>, Key?, Key?);
+    method public void onError(Throwable error);
+    method public abstract void onResult(java.util.List<? extends Value> data, int position, int totalCount, Key? previousPageKey, Key? nextPageKey);
+    method public abstract void onResult(java.util.List<? extends Value> data, Key? previousPageKey, Key? nextPageKey);
   }
 
   public static class PageKeyedDataSource.LoadInitialParams<Key> extends androidx.paging.ListenablePageKeyedDataSource.LoadInitialParams<Key> {
-    ctor public PageKeyedDataSource.LoadInitialParams(int, boolean);
+    ctor public PageKeyedDataSource.LoadInitialParams(int requestedLoadSize, boolean placeholdersEnabled);
   }
 
   public static class PageKeyedDataSource.LoadParams<Key> extends androidx.paging.ListenablePageKeyedDataSource.LoadParams<Key> {
-    ctor public PageKeyedDataSource.LoadParams(Key, int);
+    ctor public PageKeyedDataSource.LoadParams(Key key, int requestedLoadSize);
   }
 
   public abstract class PagedList<T> extends java.util.AbstractList<T> {
-    method public void addWeakCallback(java.util.List<T!>?, androidx.paging.PagedList.Callback);
-    method public void addWeakLoadStateListener(androidx.paging.PagedList.LoadStateListener);
+    method public void addWeakCallback(java.util.List<? extends T>? previousSnapshot, androidx.paging.PagedList.Callback callback);
+    method public void addWeakLoadStateListener(androidx.paging.PagedList.LoadStateListener listener);
     method public abstract void detach();
-    method public T? get(int);
+    method public T? get(int index);
     method public androidx.paging.PagedList.Config getConfig();
-    method public abstract androidx.paging.DataSource<?,T!> getDataSource();
+    method public abstract androidx.paging.DataSource<?,T> getDataSource();
     method public abstract Object? getLastKey();
     method public int getLoadedCount();
     method public int getPositionOffset();
+    method public int getSize();
+    method public abstract boolean isContiguous();
     method public abstract boolean isDetached();
     method public boolean isImmutable();
-    method public void loadAround(int);
-    method public void removeWeakCallback(androidx.paging.PagedList.Callback);
-    method public void removeWeakLoadStateListener(androidx.paging.PagedList.LoadStateListener);
+    method public void loadAround(int index);
+    method public void removeWeakCallback(androidx.paging.PagedList.Callback callback);
+    method public void removeWeakLoadStateListener(androidx.paging.PagedList.LoadStateListener listener);
     method public void retry();
-    method public int size();
-    method public java.util.List<T!> snapshot();
+    method public java.util.List<T> snapshot();
+    property public androidx.paging.PagedList.Config config;
+    property public abstract androidx.paging.DataSource<?,T> dataSource;
+    property public abstract boolean isContiguous;
+    property public abstract boolean isDetached;
+    property public boolean isImmutable;
+    property public abstract Object? lastKey;
+    property public int loadedCount;
+    property public int positionOffset;
+    property public int size;
   }
 
   @MainThread public abstract static class PagedList.BoundaryCallback<T> {
     ctor public PagedList.BoundaryCallback();
-    method public void onItemAtEndLoaded(T);
-    method public void onItemAtFrontLoaded(T);
+    method public void onItemAtEndLoaded(T? itemAtEnd);
+    method public void onItemAtFrontLoaded(T? itemAtFront);
     method public void onZeroItemsLoaded();
   }
 
   public static final class PagedList.Builder<Key, Value> {
-    ctor public PagedList.Builder(androidx.paging.DataSource<Key!,Value!>, androidx.paging.PagedList.Config);
-    ctor public PagedList.Builder(androidx.paging.DataSource<Key!,Value!>, int);
-    method @Deprecated @WorkerThread public androidx.paging.PagedList<Value!> build();
-    method public com.google.common.util.concurrent.ListenableFuture<androidx.paging.PagedList<Value!>!> buildAsync();
-    method public androidx.paging.PagedList.Builder<Key!,Value!> setBoundaryCallback(androidx.paging.PagedList.BoundaryCallback<Value!>?);
-    method public androidx.paging.PagedList.Builder<Key!,Value!> setFetchExecutor(java.util.concurrent.Executor);
-    method public androidx.paging.PagedList.Builder<Key!,Value!> setInitialKey(Key?);
-    method public androidx.paging.PagedList.Builder<Key!,Value!> setNotifyExecutor(java.util.concurrent.Executor);
+    ctor public PagedList.Builder(androidx.paging.DataSource<Key,Value> dataSource, androidx.paging.PagedList.Config config);
+    ctor public PagedList.Builder(androidx.paging.DataSource<Key,Value> dataSource, int pageSize);
+    method @Deprecated @WorkerThread public androidx.paging.PagedList<Value> build();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.paging.PagedList<Value>> buildAsync();
+    method public androidx.paging.PagedList.Builder<Key,Value> setBoundaryCallback(androidx.paging.PagedList.BoundaryCallback<Value>? boundaryCallback);
+    method public androidx.paging.PagedList.Builder<Key,Value> setFetchExecutor(java.util.concurrent.Executor fetchExecutor);
+    method public androidx.paging.PagedList.Builder<Key,Value> setInitialKey(Key? initialKey);
+    method public androidx.paging.PagedList.Builder<Key,Value> setNotifyExecutor(java.util.concurrent.Executor notifyExecutor);
   }
 
   public abstract static class PagedList.Callback {
     ctor public PagedList.Callback();
-    method public abstract void onChanged(int, int);
-    method public abstract void onInserted(int, int);
-    method public abstract void onRemoved(int, int);
+    method public abstract void onChanged(int position, int count);
+    method public abstract void onInserted(int position, int count);
+    method public abstract void onRemoved(int position, int count);
   }
 
   public static class PagedList.Config {
@@ -238,11 +256,11 @@
   public static final class PagedList.Config.Builder {
     ctor public PagedList.Config.Builder();
     method public androidx.paging.PagedList.Config build();
-    method public androidx.paging.PagedList.Config.Builder setEnablePlaceholders(boolean);
-    method public androidx.paging.PagedList.Config.Builder setInitialLoadSizeHint(@IntRange(from=1) int);
-    method public androidx.paging.PagedList.Config.Builder setMaxSize(@IntRange(from=2) int);
-    method public androidx.paging.PagedList.Config.Builder setPageSize(@IntRange(from=1) int);
-    method public androidx.paging.PagedList.Config.Builder setPrefetchDistance(@IntRange(from=0) int);
+    method public androidx.paging.PagedList.Config.Builder setEnablePlaceholders(boolean enablePlaceholders);
+    method public androidx.paging.PagedList.Config.Builder setInitialLoadSizeHint(@IntRange(from=1) int initialLoadSizeHint);
+    method public androidx.paging.PagedList.Config.Builder setMaxSize(@IntRange(from=2) int maxSize);
+    method public androidx.paging.PagedList.Config.Builder setPageSize(@IntRange(from=1) int pageSize);
+    method public androidx.paging.PagedList.Config.Builder setPrefetchDistance(@IntRange(from=0) int prefetchDistance);
   }
 
   public enum PagedList.LoadState {
@@ -254,7 +272,7 @@
   }
 
   public static interface PagedList.LoadStateListener {
-    method public void onLoadStateChanged(androidx.paging.PagedList.LoadType, androidx.paging.PagedList.LoadState, Throwable?);
+    method public void onLoadStateChanged(androidx.paging.PagedList.LoadType type, androidx.paging.PagedList.LoadState state, Throwable? error);
   }
 
   public enum PagedList.LoadType {
@@ -263,37 +281,50 @@
     enum_constant public static final androidx.paging.PagedList.LoadType START;
   }
 
+  public final class PagedListConfigKt {
+    ctor public PagedListConfigKt();
+    method public static androidx.paging.PagedList.Config Config(int pageSize, int prefetchDistance = pageSize, boolean enablePlaceholders = true, int initialLoadSizeHint = pageSize * androidx.paging.PagedList.Config.Builder.DEFAULT_INITIAL_PAGE_MULTIPLIER, int maxSize = 2147483647);
+  }
+
   public abstract class PositionalDataSource<T> extends androidx.paging.ListenablePositionalDataSource<T> {
     ctor public PositionalDataSource();
-    method public static int computeInitialLoadPosition(androidx.paging.PositionalDataSource.LoadInitialParams, int);
-    method public static int computeInitialLoadSize(androidx.paging.PositionalDataSource.LoadInitialParams, int, int);
-    method public final com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenablePositionalDataSource.InitialResult<T!>!> loadInitial(androidx.paging.ListenablePositionalDataSource.LoadInitialParams);
-    method @WorkerThread public abstract void loadInitial(androidx.paging.PositionalDataSource.LoadInitialParams, androidx.paging.PositionalDataSource.LoadInitialCallback<T!>);
-    method public final com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenablePositionalDataSource.RangeResult<T!>!> loadRange(androidx.paging.ListenablePositionalDataSource.LoadRangeParams);
-    method @WorkerThread public abstract void loadRange(androidx.paging.PositionalDataSource.LoadRangeParams, androidx.paging.PositionalDataSource.LoadRangeCallback<T!>);
-    method public final <V> androidx.paging.PositionalDataSource<V!> map(androidx.arch.core.util.Function<T!,V!>);
-    method public final <V> androidx.paging.PositionalDataSource<V!> mapByPage(androidx.arch.core.util.Function<java.util.List<T!>!,java.util.List<V!>!>);
+    method public static final int computeInitialLoadPosition(androidx.paging.PositionalDataSource.LoadInitialParams params, int totalCount);
+    method public static final int computeInitialLoadSize(androidx.paging.PositionalDataSource.LoadInitialParams params, int initialLoadPosition, int totalCount);
+    method public final com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenablePositionalDataSource.InitialResult<T>> loadInitial(androidx.paging.ListenablePositionalDataSource.LoadInitialParams params);
+    method @WorkerThread public abstract void loadInitial(androidx.paging.PositionalDataSource.LoadInitialParams params, androidx.paging.PositionalDataSource.LoadInitialCallback<T> callback);
+    method public final com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenablePositionalDataSource.RangeResult<T>> loadRange(androidx.paging.ListenablePositionalDataSource.LoadRangeParams params);
+    method @WorkerThread public abstract void loadRange(androidx.paging.PositionalDataSource.LoadRangeParams params, androidx.paging.PositionalDataSource.LoadRangeCallback<T> callback);
+    method public final <V> androidx.paging.PositionalDataSource<V> map(androidx.arch.core.util.Function<T,V> function);
+    method public final <V> androidx.paging.PositionalDataSource<V> mapByPage(androidx.arch.core.util.Function<java.util.List<T>,java.util.List<V>> function);
   }
 
   public abstract static class PositionalDataSource.LoadInitialCallback<T> {
     ctor public PositionalDataSource.LoadInitialCallback();
-    method public void onError(Throwable);
-    method public abstract void onResult(java.util.List<T!>, int, int);
-    method public abstract void onResult(java.util.List<T!>, int);
+    method public void onError(Throwable error);
+    method public abstract void onResult(java.util.List<? extends T> data, int position, int totalCount);
+    method public abstract void onResult(java.util.List<? extends T> data, int position);
   }
 
   public static class PositionalDataSource.LoadInitialParams extends androidx.paging.ListenablePositionalDataSource.LoadInitialParams {
-    ctor public PositionalDataSource.LoadInitialParams(int, int, int, boolean);
+    ctor public PositionalDataSource.LoadInitialParams(int requestedStartPosition, int requestedLoadSize, int pageSize, boolean placeholdersEnabled);
   }
 
   public abstract static class PositionalDataSource.LoadRangeCallback<T> {
     ctor public PositionalDataSource.LoadRangeCallback();
-    method public void onError(Throwable);
-    method public abstract void onResult(java.util.List<T!>);
+    method public void onError(Throwable error);
+    method public abstract void onResult(java.util.List<? extends T> data);
   }
 
   public static class PositionalDataSource.LoadRangeParams extends androidx.paging.ListenablePositionalDataSource.LoadRangeParams {
-    ctor public PositionalDataSource.LoadRangeParams(int, int);
+    ctor public PositionalDataSource.LoadRangeParams(int startPosition, int loadSize);
+  }
+
+}
+
+package androidx.paging.futures {
+
+  public final class Futures {
+    ctor public Futures();
   }
 
 }
diff --git a/paging/common/api/current.txt b/paging/common/api/current.txt
index cf51b44..f5eea3a 100644
--- a/paging/common/api/current.txt
+++ b/paging/common/api/current.txt
@@ -2,20 +2,34 @@
 package androidx.paging {
 
   public abstract class DataSource<Key, Value> {
-    method @AnyThread public void addInvalidatedCallback(androidx.paging.DataSource.InvalidatedCallback);
+    method @AnyThread public void addInvalidatedCallback(androidx.paging.DataSource.InvalidatedCallback onInvalidatedCallback);
+    method protected final java.util.concurrent.Executor getExecutor();
     method @AnyThread public void invalidate();
     method @WorkerThread public boolean isInvalid();
-    method public boolean isRetryableError(Throwable);
-    method public <ToValue> androidx.paging.DataSource<Key!,ToValue!> map(androidx.arch.core.util.Function<Value!,ToValue!>);
-    method public <ToValue> androidx.paging.DataSource<Key!,ToValue!> mapByPage(androidx.arch.core.util.Function<java.util.List<Value!>!,java.util.List<ToValue!>!>);
-    method @AnyThread public void removeInvalidatedCallback(androidx.paging.DataSource.InvalidatedCallback);
+    method public boolean isRetryableError(Throwable error);
+    method public <ToValue> androidx.paging.DataSource<Key,ToValue> map(androidx.arch.core.util.Function<Value,ToValue> function);
+    method public <ToValue> androidx.paging.DataSource<Key,ToValue> mapByPage(androidx.arch.core.util.Function<java.util.List<Value>,java.util.List<ToValue>> function);
+    method @AnyThread public void removeInvalidatedCallback(androidx.paging.DataSource.InvalidatedCallback onInvalidatedCallback);
+    property protected final java.util.concurrent.Executor executor;
+    property @WorkerThread public boolean isInvalid;
+  }
+
+  public static class DataSource.BaseResult<Value> {
+    ctor protected DataSource.BaseResult(java.util.List<? extends Value> data, Object? prevKey, Object? nextKey, int leadingNulls, int trailingNulls, int offset, boolean counted);
+    method public final boolean getCounted();
+    method public final int getLeadingNulls();
+    method public final Object? getNextKey();
+    method public final int getOffset();
+    method public final Object? getPrevKey();
+    method public final int getTrailingNulls();
+    field public final java.util.List<Value> data;
   }
 
   public abstract static class DataSource.Factory<Key, Value> {
     ctor public DataSource.Factory();
-    method public abstract androidx.paging.DataSource<Key!,Value!> create();
-    method public <ToValue> androidx.paging.DataSource.Factory<Key!,ToValue!> map(androidx.arch.core.util.Function<Value!,ToValue!>);
-    method public <ToValue> androidx.paging.DataSource.Factory<Key!,ToValue!> mapByPage(androidx.arch.core.util.Function<java.util.List<Value!>!,java.util.List<ToValue!>!>);
+    method public abstract androidx.paging.DataSource<Key,Value> create();
+    method public <ToValue> androidx.paging.DataSource.Factory<Key,ToValue> map(androidx.arch.core.util.Function<Value,ToValue> function);
+    method public <ToValue> androidx.paging.DataSource.Factory<Key,ToValue> mapByPage(androidx.arch.core.util.Function<java.util.List<Value>,java.util.List<ToValue>> function);
   }
 
   public static interface DataSource.InvalidatedCallback {
@@ -24,113 +38,108 @@
 
   public abstract class ItemKeyedDataSource<Key, Value> extends androidx.paging.ListenableItemKeyedDataSource<Key,Value> {
     ctor public ItemKeyedDataSource();
-    method public final com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenableItemKeyedDataSource.Result<Value!>!> loadAfter(androidx.paging.ListenableItemKeyedDataSource.LoadParams<Key!>);
-    method public abstract void loadAfter(androidx.paging.ItemKeyedDataSource.LoadParams<Key!>, androidx.paging.ItemKeyedDataSource.LoadCallback<Value!>);
-    method public final com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenableItemKeyedDataSource.Result<Value!>!> loadBefore(androidx.paging.ListenableItemKeyedDataSource.LoadParams<Key!>);
-    method public abstract void loadBefore(androidx.paging.ItemKeyedDataSource.LoadParams<Key!>, androidx.paging.ItemKeyedDataSource.LoadCallback<Value!>);
-    method public final com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenableItemKeyedDataSource.InitialResult<Value!>!> loadInitial(androidx.paging.ListenableItemKeyedDataSource.LoadInitialParams<Key!>);
-    method public abstract void loadInitial(androidx.paging.ItemKeyedDataSource.LoadInitialParams<Key!>, androidx.paging.ItemKeyedDataSource.LoadInitialCallback<Value!>);
-    method public final <ToValue> androidx.paging.ItemKeyedDataSource<Key!,ToValue!> map(androidx.arch.core.util.Function<Value!,ToValue!>);
-    method public final <ToValue> androidx.paging.ItemKeyedDataSource<Key!,ToValue!> mapByPage(androidx.arch.core.util.Function<java.util.List<Value!>!,java.util.List<ToValue!>!>);
+    method public final com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenableItemKeyedDataSource.Result<Value>> loadAfter(androidx.paging.ListenableItemKeyedDataSource.LoadParams<Key> params);
+    method public abstract void loadAfter(androidx.paging.ItemKeyedDataSource.LoadParams<Key> params, androidx.paging.ItemKeyedDataSource.LoadCallback<Value> callback);
+    method public final com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenableItemKeyedDataSource.Result<Value>> loadBefore(androidx.paging.ListenableItemKeyedDataSource.LoadParams<Key> params);
+    method public abstract void loadBefore(androidx.paging.ItemKeyedDataSource.LoadParams<Key> params, androidx.paging.ItemKeyedDataSource.LoadCallback<Value> callback);
+    method public final com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenableItemKeyedDataSource.InitialResult<Value>> loadInitial(androidx.paging.ListenableItemKeyedDataSource.LoadInitialParams<Key> params);
+    method public abstract void loadInitial(androidx.paging.ItemKeyedDataSource.LoadInitialParams<Key> params, androidx.paging.ItemKeyedDataSource.LoadInitialCallback<Value> callback);
+    method public final <ToValue> androidx.paging.ItemKeyedDataSource<Key,ToValue> map(androidx.arch.core.util.Function<Value,ToValue> function);
+    method public final <ToValue> androidx.paging.ItemKeyedDataSource<Key,ToValue> mapByPage(androidx.arch.core.util.Function<java.util.List<Value>,java.util.List<ToValue>> function);
   }
 
   public abstract static class ItemKeyedDataSource.LoadCallback<Value> {
     ctor public ItemKeyedDataSource.LoadCallback();
-    method public void onError(Throwable);
-    method public abstract void onResult(java.util.List<Value!>);
+    method public void onError(Throwable error);
+    method public abstract void onResult(java.util.List<? extends Value> data);
   }
 
   public abstract static class ItemKeyedDataSource.LoadInitialCallback<Value> extends androidx.paging.ItemKeyedDataSource.LoadCallback<Value> {
     ctor public ItemKeyedDataSource.LoadInitialCallback();
-    method public abstract void onResult(java.util.List<Value!>, int, int);
+    method public abstract void onResult(java.util.List<? extends Value> data, int position, int totalCount);
   }
 
   public static class ItemKeyedDataSource.LoadInitialParams<Key> extends androidx.paging.ListenableItemKeyedDataSource.LoadInitialParams<Key> {
-    ctor public ItemKeyedDataSource.LoadInitialParams(Key?, int, boolean);
+    ctor public ItemKeyedDataSource.LoadInitialParams(Key? requestedInitialKey, int requestedLoadSize, boolean placeholdersEnabled);
   }
 
   public static class ItemKeyedDataSource.LoadParams<Key> extends androidx.paging.ListenableItemKeyedDataSource.LoadParams<Key> {
-    ctor public ItemKeyedDataSource.LoadParams(Key, int);
+    ctor public ItemKeyedDataSource.LoadParams(Key key, int requestedLoadSize);
   }
 
   public abstract class ListenableItemKeyedDataSource<Key, Value> extends androidx.paging.DataSource<Key,Value> {
     ctor public ListenableItemKeyedDataSource();
-    method public abstract Key? getKey(Value);
-    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenableItemKeyedDataSource.Result<Value!>!> loadAfter(androidx.paging.ListenableItemKeyedDataSource.LoadParams<Key!>);
-    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenableItemKeyedDataSource.Result<Value!>!> loadBefore(androidx.paging.ListenableItemKeyedDataSource.LoadParams<Key!>);
-    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenableItemKeyedDataSource.InitialResult<Value!>!> loadInitial(androidx.paging.ListenableItemKeyedDataSource.LoadInitialParams<Key!>);
+    method public abstract Key getKey(Value item);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenableItemKeyedDataSource.Result<Value>> loadAfter(androidx.paging.ListenableItemKeyedDataSource.LoadParams<Key> params);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenableItemKeyedDataSource.Result<Value>> loadBefore(androidx.paging.ListenableItemKeyedDataSource.LoadParams<Key> params);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenableItemKeyedDataSource.InitialResult<Value>> loadInitial(androidx.paging.ListenableItemKeyedDataSource.LoadInitialParams<Key> params);
   }
 
-  public static class ListenableItemKeyedDataSource.InitialResult<V> {
-    ctor public ListenableItemKeyedDataSource.InitialResult(java.util.List<V!>, int, int);
-    ctor public ListenableItemKeyedDataSource.InitialResult(java.util.List<V!>);
-    method public boolean equals(Object!);
+  public static class ListenableItemKeyedDataSource.InitialResult<V> extends androidx.paging.DataSource.BaseResult<V> {
+    ctor public ListenableItemKeyedDataSource.InitialResult(java.util.List<? extends V> data, int position, int totalCount);
+    ctor public ListenableItemKeyedDataSource.InitialResult(java.util.List<? extends V> data);
   }
 
   public static class ListenableItemKeyedDataSource.LoadInitialParams<Key> {
-    ctor public ListenableItemKeyedDataSource.LoadInitialParams(Key?, int, boolean);
+    ctor public ListenableItemKeyedDataSource.LoadInitialParams(Key? requestedInitialKey, int requestedLoadSize, boolean placeholdersEnabled);
     field public final boolean placeholdersEnabled;
     field public final Key? requestedInitialKey;
     field public final int requestedLoadSize;
   }
 
   public static class ListenableItemKeyedDataSource.LoadParams<Key> {
-    ctor public ListenableItemKeyedDataSource.LoadParams(Key, int);
+    ctor public ListenableItemKeyedDataSource.LoadParams(Key key, int requestedLoadSize);
     field public final Key key;
     field public final int requestedLoadSize;
   }
 
-  public static class ListenableItemKeyedDataSource.Result<V> {
-    ctor public ListenableItemKeyedDataSource.Result(java.util.List<V!>);
-    method public boolean equals(Object!);
+  public static class ListenableItemKeyedDataSource.Result<V> extends androidx.paging.DataSource.BaseResult<V> {
+    ctor public ListenableItemKeyedDataSource.Result(java.util.List<? extends V> data);
   }
 
   public abstract class ListenablePageKeyedDataSource<Key, Value> extends androidx.paging.DataSource<Key,Value> {
     ctor public ListenablePageKeyedDataSource();
-    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenablePageKeyedDataSource.Result<Key!,Value!>!> loadAfter(androidx.paging.ListenablePageKeyedDataSource.LoadParams<Key!>);
-    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenablePageKeyedDataSource.Result<Key!,Value!>!> loadBefore(androidx.paging.ListenablePageKeyedDataSource.LoadParams<Key!>);
-    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenablePageKeyedDataSource.InitialResult<Key!,Value!>!> loadInitial(androidx.paging.ListenablePageKeyedDataSource.LoadInitialParams<Key!>);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenablePageKeyedDataSource.Result<Key,Value>> loadAfter(androidx.paging.ListenablePageKeyedDataSource.LoadParams<Key> params);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenablePageKeyedDataSource.Result<Key,Value>> loadBefore(androidx.paging.ListenablePageKeyedDataSource.LoadParams<Key> params);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenablePageKeyedDataSource.InitialResult<Key,Value>> loadInitial(androidx.paging.ListenablePageKeyedDataSource.LoadInitialParams<Key> params);
   }
 
-  public static class ListenablePageKeyedDataSource.InitialResult<Key, Value> {
-    ctor public ListenablePageKeyedDataSource.InitialResult(java.util.List<Value!>, int, int, Key?, Key?);
-    ctor public ListenablePageKeyedDataSource.InitialResult(java.util.List<Value!>, Key?, Key?);
-    method public boolean equals(Object!);
+  public static class ListenablePageKeyedDataSource.InitialResult<Key, Value> extends androidx.paging.DataSource.BaseResult<Value> {
+    ctor public ListenablePageKeyedDataSource.InitialResult(java.util.List<? extends Value> data, int position, int totalCount, Key? previousPageKey, Key? nextPageKey);
+    ctor public ListenablePageKeyedDataSource.InitialResult(java.util.List<? extends Value> data, Key? previousPageKey, Key? nextPageKey);
   }
 
   public static class ListenablePageKeyedDataSource.LoadInitialParams<Key> {
-    ctor public ListenablePageKeyedDataSource.LoadInitialParams(int, boolean);
+    ctor public ListenablePageKeyedDataSource.LoadInitialParams(int requestedLoadSize, boolean placeholdersEnabled);
     field public final boolean placeholdersEnabled;
     field public final int requestedLoadSize;
   }
 
   public static class ListenablePageKeyedDataSource.LoadParams<Key> {
-    ctor public ListenablePageKeyedDataSource.LoadParams(Key, int);
+    ctor public ListenablePageKeyedDataSource.LoadParams(Key key, int requestedLoadSize);
     field public final Key key;
     field public final int requestedLoadSize;
   }
 
-  public static class ListenablePageKeyedDataSource.Result<Key, Value> {
-    ctor public ListenablePageKeyedDataSource.Result(java.util.List<Value!>, Key?);
-    method public boolean equals(Object!);
+  public static class ListenablePageKeyedDataSource.Result<Key, Value> extends androidx.paging.DataSource.BaseResult<Value> {
+    ctor public ListenablePageKeyedDataSource.Result(java.util.List<? extends Value> data, Key? adjacentPageKey);
   }
 
   public abstract class ListenablePositionalDataSource<T> extends androidx.paging.DataSource<java.lang.Integer,T> {
     ctor public ListenablePositionalDataSource();
-    method public static int computeInitialLoadPosition(androidx.paging.ListenablePositionalDataSource.LoadInitialParams, int);
-    method public static int computeInitialLoadSize(androidx.paging.ListenablePositionalDataSource.LoadInitialParams, int, int);
-    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenablePositionalDataSource.InitialResult<T!>!> loadInitial(androidx.paging.ListenablePositionalDataSource.LoadInitialParams);
-    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenablePositionalDataSource.RangeResult<T!>!> loadRange(androidx.paging.ListenablePositionalDataSource.LoadRangeParams);
+    method public static final int computeInitialLoadPosition(androidx.paging.ListenablePositionalDataSource.LoadInitialParams params, int totalCount);
+    method public static final int computeInitialLoadSize(androidx.paging.ListenablePositionalDataSource.LoadInitialParams params, int initialLoadPosition, int totalCount);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenablePositionalDataSource.InitialResult<T>> loadInitial(androidx.paging.ListenablePositionalDataSource.LoadInitialParams params);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenablePositionalDataSource.RangeResult<T>> loadRange(androidx.paging.ListenablePositionalDataSource.LoadRangeParams params);
   }
 
-  public static class ListenablePositionalDataSource.InitialResult<V> {
-    ctor public ListenablePositionalDataSource.InitialResult(java.util.List<V!>, int, int);
-    ctor public ListenablePositionalDataSource.InitialResult(java.util.List<V!>, int);
-    method public boolean equals(Object!);
+  public static class ListenablePositionalDataSource.InitialResult<V> extends androidx.paging.DataSource.BaseResult<V> {
+    ctor public ListenablePositionalDataSource.InitialResult(java.util.List<? extends V> data, int position, int totalCount);
+    ctor public ListenablePositionalDataSource.InitialResult(java.util.List<? extends V> data, int position);
   }
 
   public static class ListenablePositionalDataSource.LoadInitialParams {
-    ctor public ListenablePositionalDataSource.LoadInitialParams(int, int, int, boolean);
+    ctor public ListenablePositionalDataSource.LoadInitialParams(int requestedStartPosition, int requestedLoadSize, int pageSize, boolean placeholdersEnabled);
     field public final int pageSize;
     field public final boolean placeholdersEnabled;
     field public final int requestedLoadSize;
@@ -138,92 +147,101 @@
   }
 
   public static class ListenablePositionalDataSource.LoadRangeParams {
-    ctor public ListenablePositionalDataSource.LoadRangeParams(int, int);
+    ctor public ListenablePositionalDataSource.LoadRangeParams(int startPosition, int loadSize);
     field public final int loadSize;
     field public final int startPosition;
   }
 
-  public static class ListenablePositionalDataSource.RangeResult<V> {
-    ctor public ListenablePositionalDataSource.RangeResult(java.util.List<V!>);
-    method public boolean equals(Object!);
+  public static class ListenablePositionalDataSource.RangeResult<V> extends androidx.paging.DataSource.BaseResult<V> {
+    ctor public ListenablePositionalDataSource.RangeResult(java.util.List<? extends V> data);
   }
 
   public abstract class PageKeyedDataSource<Key, Value> extends androidx.paging.ListenablePageKeyedDataSource<Key,Value> {
     ctor public PageKeyedDataSource();
-    method public final com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenablePageKeyedDataSource.Result<Key!,Value!>!> loadAfter(androidx.paging.ListenablePageKeyedDataSource.LoadParams<Key!>);
-    method public abstract void loadAfter(androidx.paging.PageKeyedDataSource.LoadParams<Key!>, androidx.paging.PageKeyedDataSource.LoadCallback<Key!,Value!>);
-    method public final com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenablePageKeyedDataSource.Result<Key!,Value!>!> loadBefore(androidx.paging.ListenablePageKeyedDataSource.LoadParams<Key!>);
-    method public abstract void loadBefore(androidx.paging.PageKeyedDataSource.LoadParams<Key!>, androidx.paging.PageKeyedDataSource.LoadCallback<Key!,Value!>);
-    method public final com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenablePageKeyedDataSource.InitialResult<Key!,Value!>!> loadInitial(androidx.paging.ListenablePageKeyedDataSource.LoadInitialParams<Key!>);
-    method public abstract void loadInitial(androidx.paging.PageKeyedDataSource.LoadInitialParams<Key!>, androidx.paging.PageKeyedDataSource.LoadInitialCallback<Key!,Value!>);
-    method public final <ToValue> androidx.paging.PageKeyedDataSource<Key!,ToValue!> map(androidx.arch.core.util.Function<Value!,ToValue!>);
-    method public final <ToValue> androidx.paging.PageKeyedDataSource<Key!,ToValue!> mapByPage(androidx.arch.core.util.Function<java.util.List<Value!>!,java.util.List<ToValue!>!>);
+    method public final com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenablePageKeyedDataSource.Result<Key,Value>> loadAfter(androidx.paging.ListenablePageKeyedDataSource.LoadParams<Key> params);
+    method public abstract void loadAfter(androidx.paging.PageKeyedDataSource.LoadParams<Key> params, androidx.paging.PageKeyedDataSource.LoadCallback<Key,Value> callback);
+    method public final com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenablePageKeyedDataSource.Result<Key,Value>> loadBefore(androidx.paging.ListenablePageKeyedDataSource.LoadParams<Key> params);
+    method public abstract void loadBefore(androidx.paging.PageKeyedDataSource.LoadParams<Key> params, androidx.paging.PageKeyedDataSource.LoadCallback<Key,Value> callback);
+    method public final com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenablePageKeyedDataSource.InitialResult<Key,Value>> loadInitial(androidx.paging.ListenablePageKeyedDataSource.LoadInitialParams<Key> params);
+    method public abstract void loadInitial(androidx.paging.PageKeyedDataSource.LoadInitialParams<Key> params, androidx.paging.PageKeyedDataSource.LoadInitialCallback<Key,Value> callback);
+    method public final <ToValue> androidx.paging.PageKeyedDataSource<Key,ToValue> map(androidx.arch.core.util.Function<Value,ToValue> function);
+    method public final <ToValue> androidx.paging.PageKeyedDataSource<Key,ToValue> mapByPage(androidx.arch.core.util.Function<java.util.List<Value>,java.util.List<ToValue>> function);
   }
 
   public abstract static class PageKeyedDataSource.LoadCallback<Key, Value> {
     ctor public PageKeyedDataSource.LoadCallback();
-    method public void onError(Throwable);
-    method public abstract void onResult(java.util.List<Value!>, Key?);
+    method public void onError(Throwable error);
+    method public abstract void onResult(java.util.List<? extends Value> data, Key? adjacentPageKey);
   }
 
   public abstract static class PageKeyedDataSource.LoadInitialCallback<Key, Value> {
     ctor public PageKeyedDataSource.LoadInitialCallback();
-    method public void onError(Throwable);
-    method public abstract void onResult(java.util.List<Value!>, int, int, Key?, Key?);
-    method public abstract void onResult(java.util.List<Value!>, Key?, Key?);
+    method public void onError(Throwable error);
+    method public abstract void onResult(java.util.List<? extends Value> data, int position, int totalCount, Key? previousPageKey, Key? nextPageKey);
+    method public abstract void onResult(java.util.List<? extends Value> data, Key? previousPageKey, Key? nextPageKey);
   }
 
   public static class PageKeyedDataSource.LoadInitialParams<Key> extends androidx.paging.ListenablePageKeyedDataSource.LoadInitialParams<Key> {
-    ctor public PageKeyedDataSource.LoadInitialParams(int, boolean);
+    ctor public PageKeyedDataSource.LoadInitialParams(int requestedLoadSize, boolean placeholdersEnabled);
   }
 
   public static class PageKeyedDataSource.LoadParams<Key> extends androidx.paging.ListenablePageKeyedDataSource.LoadParams<Key> {
-    ctor public PageKeyedDataSource.LoadParams(Key, int);
+    ctor public PageKeyedDataSource.LoadParams(Key key, int requestedLoadSize);
   }
 
   public abstract class PagedList<T> extends java.util.AbstractList<T> {
-    method public void addWeakCallback(java.util.List<T!>?, androidx.paging.PagedList.Callback);
-    method public void addWeakLoadStateListener(androidx.paging.PagedList.LoadStateListener);
+    method public void addWeakCallback(java.util.List<? extends T>? previousSnapshot, androidx.paging.PagedList.Callback callback);
+    method public void addWeakLoadStateListener(androidx.paging.PagedList.LoadStateListener listener);
     method public abstract void detach();
-    method public T? get(int);
+    method public T? get(int index);
     method public androidx.paging.PagedList.Config getConfig();
-    method public abstract androidx.paging.DataSource<?,T!> getDataSource();
+    method public abstract androidx.paging.DataSource<?,T> getDataSource();
     method public abstract Object? getLastKey();
     method public int getLoadedCount();
     method public int getPositionOffset();
+    method public int getSize();
+    method public abstract boolean isContiguous();
     method public abstract boolean isDetached();
     method public boolean isImmutable();
-    method public void loadAround(int);
-    method public void removeWeakCallback(androidx.paging.PagedList.Callback);
-    method public void removeWeakLoadStateListener(androidx.paging.PagedList.LoadStateListener);
+    method public void loadAround(int index);
+    method public void removeWeakCallback(androidx.paging.PagedList.Callback callback);
+    method public void removeWeakLoadStateListener(androidx.paging.PagedList.LoadStateListener listener);
     method public void retry();
-    method public int size();
-    method public java.util.List<T!> snapshot();
+    method public java.util.List<T> snapshot();
+    property public androidx.paging.PagedList.Config config;
+    property public abstract androidx.paging.DataSource<?,T> dataSource;
+    property public abstract boolean isContiguous;
+    property public abstract boolean isDetached;
+    property public boolean isImmutable;
+    property public abstract Object? lastKey;
+    property public int loadedCount;
+    property public int positionOffset;
+    property public int size;
   }
 
   @MainThread public abstract static class PagedList.BoundaryCallback<T> {
     ctor public PagedList.BoundaryCallback();
-    method public void onItemAtEndLoaded(T);
-    method public void onItemAtFrontLoaded(T);
+    method public void onItemAtEndLoaded(T? itemAtEnd);
+    method public void onItemAtFrontLoaded(T? itemAtFront);
     method public void onZeroItemsLoaded();
   }
 
   public static final class PagedList.Builder<Key, Value> {
-    ctor public PagedList.Builder(androidx.paging.DataSource<Key!,Value!>, androidx.paging.PagedList.Config);
-    ctor public PagedList.Builder(androidx.paging.DataSource<Key!,Value!>, int);
-    method @Deprecated @WorkerThread public androidx.paging.PagedList<Value!> build();
-    method public com.google.common.util.concurrent.ListenableFuture<androidx.paging.PagedList<Value!>!> buildAsync();
-    method public androidx.paging.PagedList.Builder<Key!,Value!> setBoundaryCallback(androidx.paging.PagedList.BoundaryCallback<Value!>?);
-    method public androidx.paging.PagedList.Builder<Key!,Value!> setFetchExecutor(java.util.concurrent.Executor);
-    method public androidx.paging.PagedList.Builder<Key!,Value!> setInitialKey(Key?);
-    method public androidx.paging.PagedList.Builder<Key!,Value!> setNotifyExecutor(java.util.concurrent.Executor);
+    ctor public PagedList.Builder(androidx.paging.DataSource<Key,Value> dataSource, androidx.paging.PagedList.Config config);
+    ctor public PagedList.Builder(androidx.paging.DataSource<Key,Value> dataSource, int pageSize);
+    method @Deprecated @WorkerThread public androidx.paging.PagedList<Value> build();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.paging.PagedList<Value>> buildAsync();
+    method public androidx.paging.PagedList.Builder<Key,Value> setBoundaryCallback(androidx.paging.PagedList.BoundaryCallback<Value>? boundaryCallback);
+    method public androidx.paging.PagedList.Builder<Key,Value> setFetchExecutor(java.util.concurrent.Executor fetchExecutor);
+    method public androidx.paging.PagedList.Builder<Key,Value> setInitialKey(Key? initialKey);
+    method public androidx.paging.PagedList.Builder<Key,Value> setNotifyExecutor(java.util.concurrent.Executor notifyExecutor);
   }
 
   public abstract static class PagedList.Callback {
     ctor public PagedList.Callback();
-    method public abstract void onChanged(int, int);
-    method public abstract void onInserted(int, int);
-    method public abstract void onRemoved(int, int);
+    method public abstract void onChanged(int position, int count);
+    method public abstract void onInserted(int position, int count);
+    method public abstract void onRemoved(int position, int count);
   }
 
   public static class PagedList.Config {
@@ -238,11 +256,11 @@
   public static final class PagedList.Config.Builder {
     ctor public PagedList.Config.Builder();
     method public androidx.paging.PagedList.Config build();
-    method public androidx.paging.PagedList.Config.Builder setEnablePlaceholders(boolean);
-    method public androidx.paging.PagedList.Config.Builder setInitialLoadSizeHint(@IntRange(from=1) int);
-    method public androidx.paging.PagedList.Config.Builder setMaxSize(@IntRange(from=2) int);
-    method public androidx.paging.PagedList.Config.Builder setPageSize(@IntRange(from=1) int);
-    method public androidx.paging.PagedList.Config.Builder setPrefetchDistance(@IntRange(from=0) int);
+    method public androidx.paging.PagedList.Config.Builder setEnablePlaceholders(boolean enablePlaceholders);
+    method public androidx.paging.PagedList.Config.Builder setInitialLoadSizeHint(@IntRange(from=1) int initialLoadSizeHint);
+    method public androidx.paging.PagedList.Config.Builder setMaxSize(@IntRange(from=2) int maxSize);
+    method public androidx.paging.PagedList.Config.Builder setPageSize(@IntRange(from=1) int pageSize);
+    method public androidx.paging.PagedList.Config.Builder setPrefetchDistance(@IntRange(from=0) int prefetchDistance);
   }
 
   public enum PagedList.LoadState {
@@ -254,7 +272,7 @@
   }
 
   public static interface PagedList.LoadStateListener {
-    method public void onLoadStateChanged(androidx.paging.PagedList.LoadType, androidx.paging.PagedList.LoadState, Throwable?);
+    method public void onLoadStateChanged(androidx.paging.PagedList.LoadType type, androidx.paging.PagedList.LoadState state, Throwable? error);
   }
 
   public enum PagedList.LoadType {
@@ -263,37 +281,50 @@
     enum_constant public static final androidx.paging.PagedList.LoadType START;
   }
 
+  public final class PagedListConfigKt {
+    ctor public PagedListConfigKt();
+    method public static androidx.paging.PagedList.Config Config(int pageSize, int prefetchDistance = pageSize, boolean enablePlaceholders = true, int initialLoadSizeHint = pageSize * androidx.paging.PagedList.Config.Builder.DEFAULT_INITIAL_PAGE_MULTIPLIER, int maxSize = 2147483647);
+  }
+
   public abstract class PositionalDataSource<T> extends androidx.paging.ListenablePositionalDataSource<T> {
     ctor public PositionalDataSource();
-    method public static int computeInitialLoadPosition(androidx.paging.PositionalDataSource.LoadInitialParams, int);
-    method public static int computeInitialLoadSize(androidx.paging.PositionalDataSource.LoadInitialParams, int, int);
-    method public final com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenablePositionalDataSource.InitialResult<T!>!> loadInitial(androidx.paging.ListenablePositionalDataSource.LoadInitialParams);
-    method @WorkerThread public abstract void loadInitial(androidx.paging.PositionalDataSource.LoadInitialParams, androidx.paging.PositionalDataSource.LoadInitialCallback<T!>);
-    method public final com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenablePositionalDataSource.RangeResult<T!>!> loadRange(androidx.paging.ListenablePositionalDataSource.LoadRangeParams);
-    method @WorkerThread public abstract void loadRange(androidx.paging.PositionalDataSource.LoadRangeParams, androidx.paging.PositionalDataSource.LoadRangeCallback<T!>);
-    method public final <V> androidx.paging.PositionalDataSource<V!> map(androidx.arch.core.util.Function<T!,V!>);
-    method public final <V> androidx.paging.PositionalDataSource<V!> mapByPage(androidx.arch.core.util.Function<java.util.List<T!>!,java.util.List<V!>!>);
+    method public static final int computeInitialLoadPosition(androidx.paging.PositionalDataSource.LoadInitialParams params, int totalCount);
+    method public static final int computeInitialLoadSize(androidx.paging.PositionalDataSource.LoadInitialParams params, int initialLoadPosition, int totalCount);
+    method public final com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenablePositionalDataSource.InitialResult<T>> loadInitial(androidx.paging.ListenablePositionalDataSource.LoadInitialParams params);
+    method @WorkerThread public abstract void loadInitial(androidx.paging.PositionalDataSource.LoadInitialParams params, androidx.paging.PositionalDataSource.LoadInitialCallback<T> callback);
+    method public final com.google.common.util.concurrent.ListenableFuture<androidx.paging.ListenablePositionalDataSource.RangeResult<T>> loadRange(androidx.paging.ListenablePositionalDataSource.LoadRangeParams params);
+    method @WorkerThread public abstract void loadRange(androidx.paging.PositionalDataSource.LoadRangeParams params, androidx.paging.PositionalDataSource.LoadRangeCallback<T> callback);
+    method public final <V> androidx.paging.PositionalDataSource<V> map(androidx.arch.core.util.Function<T,V> function);
+    method public final <V> androidx.paging.PositionalDataSource<V> mapByPage(androidx.arch.core.util.Function<java.util.List<T>,java.util.List<V>> function);
   }
 
   public abstract static class PositionalDataSource.LoadInitialCallback<T> {
     ctor public PositionalDataSource.LoadInitialCallback();
-    method public void onError(Throwable);
-    method public abstract void onResult(java.util.List<T!>, int, int);
-    method public abstract void onResult(java.util.List<T!>, int);
+    method public void onError(Throwable error);
+    method public abstract void onResult(java.util.List<? extends T> data, int position, int totalCount);
+    method public abstract void onResult(java.util.List<? extends T> data, int position);
   }
 
   public static class PositionalDataSource.LoadInitialParams extends androidx.paging.ListenablePositionalDataSource.LoadInitialParams {
-    ctor public PositionalDataSource.LoadInitialParams(int, int, int, boolean);
+    ctor public PositionalDataSource.LoadInitialParams(int requestedStartPosition, int requestedLoadSize, int pageSize, boolean placeholdersEnabled);
   }
 
   public abstract static class PositionalDataSource.LoadRangeCallback<T> {
     ctor public PositionalDataSource.LoadRangeCallback();
-    method public void onError(Throwable);
-    method public abstract void onResult(java.util.List<T!>);
+    method public void onError(Throwable error);
+    method public abstract void onResult(java.util.List<? extends T> data);
   }
 
   public static class PositionalDataSource.LoadRangeParams extends androidx.paging.ListenablePositionalDataSource.LoadRangeParams {
-    ctor public PositionalDataSource.LoadRangeParams(int, int);
+    ctor public PositionalDataSource.LoadRangeParams(int startPosition, int loadSize);
+  }
+
+}
+
+package androidx.paging.futures {
+
+  public final class Futures {
+    ctor public Futures();
   }
 
 }
diff --git a/paging/common/api/restricted_2.2.0-alpha01.ignore b/paging/common/api/restricted_2.2.0-alpha01.ignore
new file mode 100644
index 0000000..e7785e82
--- /dev/null
+++ b/paging/common/api/restricted_2.2.0-alpha01.ignore
@@ -0,0 +1,3 @@
+// Baseline format: 1.0
+RemovedDeprecatedClass: androidx.paging.TiledDataSource:
+    Removed deprecated class androidx.paging.TiledDataSource
diff --git a/paging/common/api/restricted_2.2.0-alpha01.txt b/paging/common/api/restricted_2.2.0-alpha01.txt
index c7a8fc2..6161bbb 100644
--- a/paging/common/api/restricted_2.2.0-alpha01.txt
+++ b/paging/common/api/restricted_2.2.0-alpha01.txt
@@ -1,31 +1,49 @@
 // Signature format: 3.0
 package androidx.paging {
 
-  @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public abstract class TiledDataSource<T> extends androidx.paging.PositionalDataSource<T> {
-    ctor @Deprecated public TiledDataSource();
-    method @Deprecated @WorkerThread public abstract int countItems();
-    method @Deprecated public void loadInitial(androidx.paging.PositionalDataSource.LoadInitialParams, androidx.paging.PositionalDataSource.LoadInitialCallback<T!>);
-    method @Deprecated @WorkerThread public abstract java.util.List<T!>? loadRange(int, int);
-    method @Deprecated public void loadRange(androidx.paging.PositionalDataSource.LoadRangeParams, androidx.paging.PositionalDataSource.LoadRangeCallback<T!>);
+
+  public abstract class DataSource<Key, Value> {
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public final void initExecutor(java.util.concurrent.Executor executor);
   }
 
+
+
+
+  public abstract class ListenableItemKeyedDataSource<Key, Value> extends androidx.paging.DataSource<Key,Value> {
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public final Key getKeyInternal$lintWithKotlin(Value item);
+  }
+
+  public abstract class ListenablePageKeyedDataSource<Key, Value> extends androidx.paging.DataSource<Key,Value> {
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public Key getKeyInternal$lintWithKotlin(Value item);
+  }
+
+  public abstract class ListenablePositionalDataSource<T> extends androidx.paging.DataSource<java.lang.Integer,T> {
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public final Integer! getKeyInternal$lintWithKotlin(T item);
+  }
+
+  public abstract class PagedList<T> extends java.util.AbstractList<T> {
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) protected final androidx.paging.PagedStorage<T> getStorage();
+  }
+
+
+
+
 }
 
 package androidx.paging.futures {
 
-  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public class DirectExecutor implements java.util.concurrent.Executor {
-    method public void execute(Runnable);
-    field public static androidx.paging.futures.DirectExecutor INSTANCE;
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public final class DirectExecutor implements java.util.concurrent.Executor {
+    method public void execute(Runnable runnable);
   }
 
   @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public interface FutureCallback<V> {
-    method public void onError(Throwable);
-    method public void onSuccess(V!);
+    method public void onError(Throwable throwable);
+    method public void onSuccess(V? value);
   }
 
-  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public class Futures {
-    method public static <V> void addCallback(com.google.common.util.concurrent.ListenableFuture<V!>, androidx.paging.futures.FutureCallback<? super V>, java.util.concurrent.Executor);
-    method public static <I, O> com.google.common.util.concurrent.ListenableFuture<O!> transform(com.google.common.util.concurrent.ListenableFuture<I!>, androidx.arch.core.util.Function<? super I,? extends O>, java.util.concurrent.Executor);
+  public final class Futures {
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static <V> void addCallback(com.google.common.util.concurrent.ListenableFuture<? extends V>, androidx.paging.futures.FutureCallback<? super V> callback, java.util.concurrent.Executor executor);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static <I, O> com.google.common.util.concurrent.ListenableFuture<O> transform(com.google.common.util.concurrent.ListenableFuture<? extends I>, androidx.arch.core.util.Function<? super I,? extends O> function, java.util.concurrent.Executor executor);
   }
 
 }
diff --git a/paging/common/api/restricted_current.txt b/paging/common/api/restricted_current.txt
index c7a8fc2..6161bbb 100644
--- a/paging/common/api/restricted_current.txt
+++ b/paging/common/api/restricted_current.txt
@@ -1,31 +1,49 @@
 // Signature format: 3.0
 package androidx.paging {
 
-  @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public abstract class TiledDataSource<T> extends androidx.paging.PositionalDataSource<T> {
-    ctor @Deprecated public TiledDataSource();
-    method @Deprecated @WorkerThread public abstract int countItems();
-    method @Deprecated public void loadInitial(androidx.paging.PositionalDataSource.LoadInitialParams, androidx.paging.PositionalDataSource.LoadInitialCallback<T!>);
-    method @Deprecated @WorkerThread public abstract java.util.List<T!>? loadRange(int, int);
-    method @Deprecated public void loadRange(androidx.paging.PositionalDataSource.LoadRangeParams, androidx.paging.PositionalDataSource.LoadRangeCallback<T!>);
+
+  public abstract class DataSource<Key, Value> {
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public final void initExecutor(java.util.concurrent.Executor executor);
   }
 
+
+
+
+  public abstract class ListenableItemKeyedDataSource<Key, Value> extends androidx.paging.DataSource<Key,Value> {
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public final Key getKeyInternal$lintWithKotlin(Value item);
+  }
+
+  public abstract class ListenablePageKeyedDataSource<Key, Value> extends androidx.paging.DataSource<Key,Value> {
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public Key getKeyInternal$lintWithKotlin(Value item);
+  }
+
+  public abstract class ListenablePositionalDataSource<T> extends androidx.paging.DataSource<java.lang.Integer,T> {
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public final Integer! getKeyInternal$lintWithKotlin(T item);
+  }
+
+  public abstract class PagedList<T> extends java.util.AbstractList<T> {
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) protected final androidx.paging.PagedStorage<T> getStorage();
+  }
+
+
+
+
 }
 
 package androidx.paging.futures {
 
-  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public class DirectExecutor implements java.util.concurrent.Executor {
-    method public void execute(Runnable);
-    field public static androidx.paging.futures.DirectExecutor INSTANCE;
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public final class DirectExecutor implements java.util.concurrent.Executor {
+    method public void execute(Runnable runnable);
   }
 
   @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public interface FutureCallback<V> {
-    method public void onError(Throwable);
-    method public void onSuccess(V!);
+    method public void onError(Throwable throwable);
+    method public void onSuccess(V? value);
   }
 
-  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public class Futures {
-    method public static <V> void addCallback(com.google.common.util.concurrent.ListenableFuture<V!>, androidx.paging.futures.FutureCallback<? super V>, java.util.concurrent.Executor);
-    method public static <I, O> com.google.common.util.concurrent.ListenableFuture<O!> transform(com.google.common.util.concurrent.ListenableFuture<I!>, androidx.arch.core.util.Function<? super I,? extends O>, java.util.concurrent.Executor);
+  public final class Futures {
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static <V> void addCallback(com.google.common.util.concurrent.ListenableFuture<? extends V>, androidx.paging.futures.FutureCallback<? super V> callback, java.util.concurrent.Executor executor);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static <I, O> com.google.common.util.concurrent.ListenableFuture<O> transform(com.google.common.util.concurrent.ListenableFuture<? extends I>, androidx.arch.core.util.Function<? super I,? extends O> function, java.util.concurrent.Executor executor);
   }
 
 }
diff --git a/paging/common/build.gradle b/paging/common/build.gradle
index c643f56..2156399 100644
--- a/paging/common/build.gradle
+++ b/paging/common/build.gradle
@@ -34,10 +34,13 @@
     compile("androidx.annotation:annotation:1.1.0")
     compile(ARCH_CORE_COMMON)
     compile("androidx.concurrent:concurrent-futures:1.0.0-alpha02")
+    implementation(KOTLIN_STDLIB)
 
     testCompile(JUNIT)
     testCompile(MOCKITO_CORE)
-    testCompile(KOTLIN_STDLIB)
+    testImplementation MOCKITO_KOTLIN, {
+        exclude group: 'org.mockito' // to keep control on the mockito version
+    }
     testCompile(GUAVA)
     testImplementation project(':internal-testutils-common')
 }
diff --git a/paging/common/ktx/api/2.2.0-alpha01.ignore b/paging/common/ktx/api/2.2.0-alpha01.ignore
new file mode 100644
index 0000000..063ab48
--- /dev/null
+++ b/paging/common/ktx/api/2.2.0-alpha01.ignore
@@ -0,0 +1,2 @@
+RemovedClass: androidx.paging.PagedListConfigKt:
+    Removed class androidx.paging.PagedListConfigKt
diff --git a/paging/common/ktx/api/2.2.0-alpha01.txt b/paging/common/ktx/api/2.2.0-alpha01.txt
index 6d9cc829..66e463b 100644
--- a/paging/common/ktx/api/2.2.0-alpha01.txt
+++ b/paging/common/ktx/api/2.2.0-alpha01.txt
@@ -1,11 +1,6 @@
 // Signature format: 3.0
 package androidx.paging {
 
-  public final class PagedListConfigKt {
-    ctor public PagedListConfigKt();
-    method public static androidx.paging.PagedList.Config Config(int pageSize, int prefetchDistance = pageSize, boolean enablePlaceholders = true, int initialLoadSizeHint = pageSize * androidx.paging.PagedList.Config.Builder.DEFAULT_INITIAL_PAGE_MULTIPLIER, int maxSize = 2147483647);
-  }
-
   public final class PagedListKt {
     ctor public PagedListKt();
     method public static <Key, Value> androidx.paging.PagedList<Value> PagedList(androidx.paging.DataSource<Key,Value> dataSource, androidx.paging.PagedList.Config config, java.util.concurrent.Executor notifyExecutor, java.util.concurrent.Executor fetchExecutor, androidx.paging.PagedList.BoundaryCallback<Value>? boundaryCallback = null, Key? initialKey = null);
diff --git a/paging/common/ktx/api/current.txt b/paging/common/ktx/api/current.txt
index 6d9cc829..66e463b 100644
--- a/paging/common/ktx/api/current.txt
+++ b/paging/common/ktx/api/current.txt
@@ -1,11 +1,6 @@
 // Signature format: 3.0
 package androidx.paging {
 
-  public final class PagedListConfigKt {
-    ctor public PagedListConfigKt();
-    method public static androidx.paging.PagedList.Config Config(int pageSize, int prefetchDistance = pageSize, boolean enablePlaceholders = true, int initialLoadSizeHint = pageSize * androidx.paging.PagedList.Config.Builder.DEFAULT_INITIAL_PAGE_MULTIPLIER, int maxSize = 2147483647);
-  }
-
   public final class PagedListKt {
     ctor public PagedListKt();
     method public static <Key, Value> androidx.paging.PagedList<Value> PagedList(androidx.paging.DataSource<Key,Value> dataSource, androidx.paging.PagedList.Config config, java.util.concurrent.Executor notifyExecutor, java.util.concurrent.Executor fetchExecutor, androidx.paging.PagedList.BoundaryCallback<Value>? boundaryCallback = null, Key? initialKey = null);
diff --git a/paging/common/ktx/src/main/java/androidx/paging/PagedList.kt b/paging/common/ktx/src/main/java/androidx/paging/PagedList.kt
index 383a10f..d957dc7 100644
--- a/paging/common/ktx/src/main/java/androidx/paging/PagedList.kt
+++ b/paging/common/ktx/src/main/java/androidx/paging/PagedList.kt
@@ -34,7 +34,7 @@
  * @param initialKey Key the DataSource should load around as part of initialization.
  */
 @Suppress("FunctionName")
-fun <Key, Value> PagedList(
+fun <Key : Any, Value : Any> PagedList(
     dataSource: DataSource<Key, Value>,
     config: PagedList.Config,
     notifyExecutor: Executor,
diff --git a/paging/common/ktx/src/test/java/PagedListTest.kt b/paging/common/ktx/src/test/java/PagedListTest.kt
index ac5e511..ef5d93d 100644
--- a/paging/common/ktx/src/test/java/PagedListTest.kt
+++ b/paging/common/ktx/src/test/java/PagedListTest.kt
@@ -30,8 +30,8 @@
         val pagedList = PagedList(
                 dataSource = dataSource,
                 config = config,
-                fetchExecutor = DirectExecutor.INSTANCE,
-                notifyExecutor = DirectExecutor.INSTANCE
+                fetchExecutor = DirectExecutor,
+                notifyExecutor = DirectExecutor
         )
 
         assertEquals(dataSource, pagedList.dataSource)
diff --git a/paging/common/src/main/java/androidx/paging/ContiguousPagedList.java b/paging/common/src/main/java/androidx/paging/ContiguousPagedList.java
deleted file mode 100644
index d5ad40d..0000000
--- a/paging/common/src/main/java/androidx/paging/ContiguousPagedList.java
+++ /dev/null
@@ -1,357 +0,0 @@
-/*
- * 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.MainThread;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import java.util.List;
-import java.util.concurrent.Executor;
-
-class ContiguousPagedList<K, V> extends PagedList<V> implements PagedStorage.Callback,
-        Pager.PageConsumer<V> {
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
-    final DataSource<K, V> mDataSource;
-
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
-    int mPrependItemsRequested = 0;
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
-    int mAppendItemsRequested = 0;
-
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
-    boolean mReplacePagesWithNulls = false;
-
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
-    final boolean mShouldTrim;
-
-    /**
-     * Given a page result, apply or drop it, and return whether more loading is needed.
-     */
-    @Override
-    public boolean onPageResult(@NonNull LoadType type,
-            @NonNull DataSource.BaseResult<V> pageResult) {
-        boolean continueLoading = false;
-        @NonNull List<V> page = pageResult.data;
-
-
-        // if we end up trimming, we trim from side that's furthest from most recent access
-        boolean trimFromFront = mLastLoad > mStorage.getMiddleOfLoadedRange();
-
-        // is the new page big enough to warrant pre-trimming (i.e. dropping) it?
-        boolean skipNewPage = mShouldTrim
-                && mStorage.shouldPreTrimNewPage(
-                mConfig.maxSize, mRequiredRemainder, page.size());
-
-        if (type == LoadType.END) {
-            if (skipNewPage && !trimFromFront) {
-                // don't append this data, drop it
-                mAppendItemsRequested = 0;
-            } else {
-                mStorage.appendPage(page, ContiguousPagedList.this);
-                mAppendItemsRequested -= page.size();
-                if (mAppendItemsRequested > 0 && page.size() != 0) {
-                    continueLoading = true;
-                }
-            }
-        } else if (type == LoadType.START) {
-            if (skipNewPage && trimFromFront) {
-                // don't append this data, drop it
-                mPrependItemsRequested = 0;
-            } else {
-                mStorage.prependPage(page, ContiguousPagedList.this);
-                mPrependItemsRequested -= page.size();
-                if (mPrependItemsRequested > 0 && page.size() != 0) {
-                    continueLoading = true;
-                }
-            }
-        } else {
-            throw new IllegalArgumentException("unexpected result type " + type);
-        }
-
-        if (mShouldTrim) {
-            // Try and trim, but only if the side being trimmed isn't actually fetching.
-            // For simplicity (both of impl here, and contract w/ DataSource) we don't
-            // allow fetches in same direction - this means reading the load state is safe.
-            if (trimFromFront) {
-                if (mPager.mLoadStateManager.getStart() != LoadState.LOADING) {
-                    if (mStorage.trimFromFront(
-                            mReplacePagesWithNulls,
-                            mConfig.maxSize,
-                            mRequiredRemainder,
-                            ContiguousPagedList.this)) {
-                        // trimmed from front, ensure we can fetch in that dir
-                        mPager.mLoadStateManager.setState(LoadType.START, LoadState.IDLE, null);
-                    }
-                }
-            } else {
-                if (mPager.mLoadStateManager.getEnd() != LoadState.LOADING) {
-                    if (mStorage.trimFromEnd(
-                            mReplacePagesWithNulls,
-                            mConfig.maxSize,
-                            mRequiredRemainder,
-                            ContiguousPagedList.this)) {
-                        mPager.mLoadStateManager.setState(LoadType.END, LoadState.IDLE, null);
-                    }
-                }
-            }
-        }
-
-        triggerBoundaryCallback(type, page);
-        return continueLoading;
-    }
-
-    @Override
-    public void onStateChanged(@NonNull LoadType type, @NonNull LoadState state,
-            @Nullable Throwable error) {
-        dispatchStateChange(type, state, error);
-    }
-
-    private void triggerBoundaryCallback(@NonNull LoadType type, @NonNull List<V> page) {
-        if (mBoundaryCallback != null) {
-            boolean deferEmpty = mStorage.size() == 0;
-            boolean deferBegin = !deferEmpty
-                    && type == LoadType.START
-                    && page.size() == 0;
-            boolean deferEnd = !deferEmpty
-                    && type == LoadType.END
-                    && page.size() == 0;
-            deferBoundaryCallbacks(deferEmpty, deferBegin, deferEnd);
-        }
-    }
-
-    @NonNull
-    private final Pager mPager;
-
-    @Override
-    public void retry() {
-        super.retry();
-        mPager.retry();
-
-        if (mRefreshRetryCallback != null
-                && mPager.mLoadStateManager.getRefresh() == LoadState.RETRYABLE_ERROR) {
-            // Loading the next PagedList failed, signal the retry callback.
-            mRefreshRetryCallback.run();
-        }
-    }
-
-    static final int LAST_LOAD_UNSPECIFIED = -1;
-
-    ContiguousPagedList(
-            @NonNull DataSource<K, V> dataSource,
-            @NonNull Executor mainThreadExecutor,
-            @NonNull Executor backgroundThreadExecutor,
-            @Nullable BoundaryCallback<V> boundaryCallback,
-            @NonNull Config config,
-            @NonNull DataSource.BaseResult<V> initialResult,
-            int lastLoad) {
-        super(new PagedStorage<V>(), mainThreadExecutor, backgroundThreadExecutor,
-                boundaryCallback, config);
-        mDataSource = dataSource;
-        mLastLoad = lastLoad;
-        mPager = new Pager<>(config, dataSource, mainThreadExecutor, backgroundThreadExecutor,
-                this, mStorage, initialResult);
-
-        if (config.enablePlaceholders) {
-            // Placeholders enabled, pass raw data to storage init
-            mStorage.init(initialResult.leadingNulls, initialResult.data,
-                    initialResult.trailingNulls, initialResult.offset, this);
-        } else {
-            // If placeholder are disabled, avoid passing leading/trailing nulls,
-            // since DataSource may have passed them anyway
-            mStorage.init(0, initialResult.data,
-                    0, initialResult.offset + initialResult.leadingNulls, this);
-        }
-
-        mShouldTrim = mDataSource.supportsPageDropping()
-                && mConfig.maxSize != Config.MAX_SIZE_UNBOUNDED;
-
-        if (mLastLoad == LAST_LOAD_UNSPECIFIED) {
-            // Because the ContiguousPagedList wasn't initialized with a last load position,
-            // initialize it to the middle of the initial load
-            mLastLoad = initialResult.leadingNulls + initialResult.offset
-                    + initialResult.data.size() / 2;
-        }
-        triggerBoundaryCallback(LoadType.REFRESH, initialResult.data);
-    }
-
-    @Override
-    void dispatchCurrentLoadState(LoadStateListener listener) {
-        mPager.mLoadStateManager.dispatchCurrentLoadState(listener);
-    }
-
-    @Override
-    void setInitialLoadState(@NonNull LoadState loadState, @Nullable Throwable error) {
-        mPager.mLoadStateManager.setState(LoadType.REFRESH, loadState, error);
-    }
-
-    @MainThread
-    @Override
-    void dispatchUpdatesSinceSnapshot(
-            @NonNull PagedList<V> pagedListSnapshot, @NonNull Callback callback) {
-        final PagedStorage<V> snapshot = pagedListSnapshot.mStorage;
-
-        final int newlyAppended = mStorage.getNumberAppended() - snapshot.getNumberAppended();
-        final int newlyPrepended = mStorage.getNumberPrepended() - snapshot.getNumberPrepended();
-
-        final int previousTrailing = snapshot.getTrailingNullCount();
-        final int previousLeading = snapshot.getLeadingNullCount();
-
-        // Validate that the snapshot looks like a previous version of this list - if it's not,
-        // we can't be sure we'll dispatch callbacks safely
-        if (snapshot.isEmpty()
-                || newlyAppended < 0
-                || newlyPrepended < 0
-                || mStorage.getTrailingNullCount() != Math.max(previousTrailing - newlyAppended, 0)
-                || mStorage.getLeadingNullCount() != Math.max(previousLeading - newlyPrepended, 0)
-                || (mStorage.getStorageCount()
-                        != snapshot.getStorageCount() + newlyAppended + newlyPrepended)) {
-            throw new IllegalArgumentException("Invalid snapshot provided - doesn't appear"
-                    + " to be a snapshot of this PagedList");
-        }
-
-        if (newlyAppended != 0) {
-            final int changedCount = Math.min(previousTrailing, newlyAppended);
-            final int addedCount = newlyAppended - changedCount;
-
-            final int endPosition = snapshot.getLeadingNullCount() + snapshot.getStorageCount();
-            if (changedCount != 0) {
-                callback.onChanged(endPosition, changedCount);
-            }
-            if (addedCount != 0) {
-                callback.onInserted(endPosition + changedCount, addedCount);
-            }
-        }
-        if (newlyPrepended != 0) {
-            final int changedCount = Math.min(previousLeading, newlyPrepended);
-            final int addedCount = newlyPrepended - changedCount;
-
-            if (changedCount != 0) {
-                callback.onChanged(previousLeading, changedCount);
-            }
-            if (addedCount != 0) {
-                callback.onInserted(0, addedCount);
-            }
-        }
-    }
-
-    static int getPrependItemsRequested(int prefetchDistance, int index, int leadingNulls) {
-        return prefetchDistance - (index - leadingNulls);
-    }
-
-    static int getAppendItemsRequested(
-            int prefetchDistance, int index, int itemsBeforeTrailingNulls) {
-        return index + prefetchDistance + 1 - itemsBeforeTrailingNulls;
-    }
-
-    @MainThread
-    @Override
-    protected void loadAroundInternal(int index) {
-        int prependItems = getPrependItemsRequested(mConfig.prefetchDistance, index,
-                mStorage.getLeadingNullCount());
-        int appendItems = getAppendItemsRequested(mConfig.prefetchDistance, index,
-                mStorage.getLeadingNullCount() + mStorage.getStorageCount());
-
-        mPrependItemsRequested = Math.max(prependItems, mPrependItemsRequested);
-        if (mPrependItemsRequested > 0) {
-            mPager.trySchedulePrepend();
-        }
-
-        mAppendItemsRequested = Math.max(appendItems, mAppendItemsRequested);
-        if (mAppendItemsRequested > 0) {
-            mPager.tryScheduleAppend();
-        }
-    }
-
-    @Override
-    public boolean isDetached() {
-        return mPager.isDetached();
-    }
-
-    @Override
-    public void detach() {
-        mPager.detach();
-    }
-
-    @Override
-    boolean isContiguous() {
-        return true;
-    }
-
-    @NonNull
-    @Override
-    public DataSource<?, V> getDataSource() {
-        return mDataSource;
-    }
-
-    @Nullable
-    @Override
-    public Object getLastKey() {
-        return mDataSource.getKey(mLastLoad, mLastItem);
-    }
-
-    @MainThread
-    @Override
-    public void onInitialized(int count) {
-        notifyInserted(0, count);
-        // simple heuristic to decide if, when dropping pages, we should replace with placeholders
-        mReplacePagesWithNulls =
-                mStorage.getLeadingNullCount() > 0 || mStorage.getTrailingNullCount() > 0;
-    }
-
-    @MainThread
-    @Override
-    public void onPagePrepended(int leadingNulls, int changedCount, int addedCount) {
-
-        // finally dispatch callbacks, after prepend may have already been scheduled
-        notifyChanged(leadingNulls, changedCount);
-        notifyInserted(0, addedCount);
-
-        offsetAccessIndices(addedCount);
-    }
-
-    @MainThread
-    @Override
-    public void onPageAppended(int endPosition, int changedCount, int addedCount) {
-        // finally dispatch callbacks, after append may have already been scheduled
-        notifyChanged(endPosition, changedCount);
-        notifyInserted(endPosition + changedCount, addedCount);
-    }
-
-
-    @MainThread
-    @Override
-    public void onPagePlaceholderInserted(int pageIndex) {
-        throw new IllegalStateException("Tiled callback on ContiguousPagedList");
-    }
-
-    @MainThread
-    @Override
-    public void onPageInserted(int start, int count) {
-        throw new IllegalStateException("Tiled callback on ContiguousPagedList");
-    }
-
-    @Override
-    public void onPagesRemoved(int startOfDrops, int count) {
-        notifyRemoved(startOfDrops, count);
-    }
-
-    @Override
-    public void onPagesSwappedToPlaceholder(int startOfDrops, int count) {
-        notifyChanged(startOfDrops, count);
-    }
-}
diff --git a/paging/common/src/main/java/androidx/paging/DataSource.java b/paging/common/src/main/java/androidx/paging/DataSource.java
deleted file mode 100644
index 2313653..0000000
--- a/paging/common/src/main/java/androidx/paging/DataSource.java
+++ /dev/null
@@ -1,545 +0,0 @@
-/*
- * 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.AnyThread;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.WorkerThread;
-import androidx.arch.core.util.Function;
-
-import com.google.common.util.concurrent.ListenableFuture;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.Executor;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-/**
- * Base class for loading pages of snapshot data into a {@link PagedList}.
- * <p>
- * DataSource is queried to load pages of content into a {@link PagedList}. A PagedList can grow as
- * it loads more data, but the data loaded cannot be updated. If the underlying data set is
- * modified, a new PagedList / DataSource pair must be created to represent the new data.
- * <h4>Loading Pages</h4>
- * PagedList queries data from its DataSource in response to loading hints. PagedListAdapter
- * calls {@link PagedList#loadAround(int)} to load content as the user scrolls in a RecyclerView.
- * <p>
- * To control how and when a PagedList queries data from its DataSource, see
- * {@link PagedList.Config}. The Config object defines things like load sizes and prefetch distance.
- * <h4>Updating Paged Data</h4>
- * A PagedList / DataSource pair are a snapshot of the data set. A new pair of
- * PagedList / DataSource must be created if an update occurs, such as a reorder, insert, delete, or
- * content update occurs. A DataSource must detect that it cannot continue loading its
- * snapshot (for instance, when Database query notices a table being invalidated), and call
- * {@link #invalidate()}. Then a new PagedList / DataSource pair would be created to load data from
- * the new state of the Database query.
- * <p>
- * To page in data that doesn't update, you can create a single DataSource, and pass it to a single
- * PagedList. For example, loading from network when the network's paging API doesn't provide
- * updates.
- * <p>
- * To page in data from a source that does provide updates, you can create a
- * {@link DataSource.Factory}, where each DataSource created is invalidated when an update to the
- * data set occurs that makes the current snapshot invalid. For example, when paging a query from
- * the Database, and the table being queried inserts or removes items. You can also use a
- * DataSource.Factory to provide multiple versions of network-paged lists. If reloading all content
- * (e.g. in response to an action like swipe-to-refresh) is required to get a new version of data,
- * you can connect an explicit refresh signal to call {@link #invalidate()} on the current
- * DataSource.
- * <p>
- * If you have more granular update signals, such as a network API signaling an update to a single
- * item in the list, it's recommended to load data from network into memory. Then present that
- * data to the PagedList via a DataSource that wraps an in-memory snapshot. Each time the in-memory
- * copy changes, invalidate the previous DataSource, and a new one wrapping the new state of the
- * snapshot can be created.
- * <h4>Implementing a DataSource</h4>
- * To implement, extend one of the subclasses: {@link PageKeyedDataSource},
- * {@link ItemKeyedDataSource}, or {@link PositionalDataSource}.
- * <p>
- * Use {@link PageKeyedDataSource} if pages you load embed keys for loading adjacent pages. For
- * example a network response that returns some items, and a next/previous page links.
- * <p>
- * Use {@link ItemKeyedDataSource} if you need to use data from item {@code N-1} to load item
- * {@code N}. For example, if requesting the backend for the next comments in the list
- * requires the ID or timestamp of the most recent loaded comment, or if querying the next users
- * from a name-sorted database query requires the name and unique ID of the previous.
- * <p>
- * Use {@link PositionalDataSource} if you can load pages of a requested size at arbitrary
- * positions, and provide a fixed item count. PositionalDataSource supports querying pages at
- * arbitrary positions, so can provide data to PagedLists in arbitrary order. Note that
- * PositionalDataSource is required to respect page size for efficient tiling. If you want to
- * override page size (e.g. when network page size constraints are only known at runtime), use one
- * of the other DataSource classes.
- * <p>
- * Because a {@code null} item indicates a placeholder in {@link PagedList}, DataSource may not
- * return {@code null} items in lists that it loads. This is so that users of the PagedList
- * can differentiate unloaded placeholder items from content that has been paged in.
- *
- * @param <Key> Unique identifier for item loaded from DataSource. Often an integer to represent
- *             position in data set. Note - this is distinct from e.g. Room's {@code @PrimaryKey}.
- * @param <Value> Value type loaded by the DataSource.
- */
-@SuppressWarnings("unused") // suppress warning to remove Key/Value, needed for subclass type safety
-public abstract class DataSource<Key, Value> {
-    /**
-     * Factory for DataSources.
-     * <p>
-     * Data-loading systems of an application or library can implement this interface to allow
-     * {@code LiveData<PagedList>}s to be created. For example, Room can provide a
-     * DataSource.Factory for a given SQL query:
-     *
-     * <pre>
-     * {@literal @}Dao
-     * interface UserDao {
-     *    {@literal @}Query("SELECT * FROM user ORDER BY lastName ASC")
-     *    public abstract DataSource.Factory&lt;Integer, User> usersByLastName();
-     * }
-     * </pre>
-     * In the above sample, {@code Integer} is used because it is the {@code Key} type of
-     * PositionalDataSource. Currently, Room uses the {@code LIMIT}/{@code OFFSET} SQL keywords to
-     * page a large query with a PositionalDataSource.
-     *
-     * @param <Key> Key identifying items in DataSource.
-     * @param <Value> Type of items in the list loaded by the DataSources.
-     */
-    public abstract static class Factory<Key, Value> {
-        /**
-         * Create a DataSource.
-         * <p>
-         * The DataSource should invalidate itself if the snapshot is no longer valid. If a
-         * DataSource becomes invalid, the only way to query more data is to create a new DataSource
-         * from the Factory.
-         * <p>
-         * {@link androidx.paging.LivePagedListBuilder} for example will construct a new PagedList and DataSource
-         * when the current DataSource is invalidated, and pass the new PagedList through the
-         * {@code LiveData<PagedList>} to observers.
-         *
-         * @return the new DataSource.
-         */
-        @NonNull
-        public abstract DataSource<Key, Value> create();
-
-        /**
-         * Applies the given function to each value emitted by DataSources produced by this Factory.
-         * <p>
-         * Same as {@link #mapByPage(Function)}, but operates on individual items.
-         *
-         * @param function Function that runs on each loaded item, returning items of a potentially
-         *                  new type.
-         * @param <ToValue> Type of items produced by the new DataSource, from the passed function.
-         *
-         * @return A new DataSource.Factory, which transforms items using the given function.
-         *
-         * @see #mapByPage(Function)
-         * @see DataSource#map(Function)
-         * @see DataSource#mapByPage(Function)
-         */
-        @NonNull
-        public <ToValue> DataSource.Factory<Key, ToValue> map(
-                @NonNull Function<Value, ToValue> function) {
-            return mapByPage(createListFunction(function));
-        }
-
-        /**
-         * Applies the given function to each value emitted by DataSources produced by this Factory.
-         * <p>
-         * Same as {@link #map(Function)}, but allows for batch conversions.
-         *
-         * @param function Function that runs on each loaded page, returning items of a potentially
-         *                  new type.
-         * @param <ToValue> Type of items produced by the new DataSource, from the passed function.
-         *
-         * @return A new DataSource.Factory, which transforms items using the given function.
-         *
-         * @see #map(Function)
-         * @see DataSource#map(Function)
-         * @see DataSource#mapByPage(Function)
-         */
-        @NonNull
-        public <ToValue> DataSource.Factory<Key, ToValue> mapByPage(
-                @NonNull final Function<List<Value>, List<ToValue>> function) {
-            return new Factory<Key, ToValue>() {
-                @Override
-                public DataSource<Key, ToValue> create() {
-                    return Factory.this.create().mapByPage(function);
-                }
-            };
-        }
-    }
-
-    @NonNull
-    static <X, Y> Function<List<X>, List<Y>> createListFunction(
-            final @NonNull Function<X, Y> innerFunc) {
-        return new Function<List<X>, List<Y>>() {
-            @Override
-            public List<Y> apply(@NonNull List<X> source) {
-                List<Y> out = new ArrayList<>(source.size());
-                for (int i = 0; i < source.size(); i++) {
-                    out.add(innerFunc.apply(source.get(i)));
-                }
-                return out;
-            }
-        };
-    }
-
-    static <A, B> List<B> convert(Function<List<A>, List<B>> function, List<A> source) {
-        List<B> dest = function.apply(source);
-        if (dest.size() != source.size()) {
-            throw new IllegalStateException("Invalid Function " + function
-                    + " changed return size. This is not supported.");
-        }
-        return dest;
-    }
-
-
-    /**
-     * Applies the given function to each value emitted by the DataSource.
-     * <p>
-     * Same as {@link #map(Function)}, but allows for batch conversions.
-     *
-     * @param function Function that runs on each loaded page, returning items of a potentially
-     *                  new type.
-     * @param <ToValue> Type of items produced by the new DataSource, from the passed function.
-     *
-     * @return A new DataSource, which transforms items using the given function.
-     *
-     * @see #map(Function)
-     * @see DataSource.Factory#map(Function)
-     * @see DataSource.Factory#mapByPage(Function)
-     */
-    @NonNull
-    public <ToValue> DataSource<Key, ToValue> mapByPage(
-            @NonNull Function<List<Value>, List<ToValue>> function) {
-        return new WrapperDataSource<>(this, function);
-    }
-
-    /**
-     * Applies the given function to each value emitted by the DataSource.
-     * <p>
-     * Same as {@link #mapByPage(Function)}, but operates on individual items.
-     *
-     * @param function Function that runs on each loaded item, returning items of a potentially
-     *                  new type.
-     * @param <ToValue> Type of items produced by the new DataSource, from the passed function.
-     *
-     * @return A new DataSource, which transforms items using the given function.
-     *
-     * @see #mapByPage(Function)
-     * @see DataSource.Factory#map(Function)
-     * @see DataSource.Factory#mapByPage(Function)
-     */
-    @NonNull
-    public <ToValue> DataSource<Key, ToValue> map(
-            @NonNull Function<Value, ToValue> function) {
-        return mapByPage(createListFunction(function));
-    }
-
-    /**
-     * Returns true if the data source guaranteed to produce a contiguous set of items,
-     * never producing gaps.
-     */
-    boolean isContiguous() {
-        return true;
-    }
-
-    boolean supportsPageDropping() {
-        return true;
-    }
-
-    /**
-     * Invalidation callback for DataSource.
-     * <p>
-     * Used to signal when a DataSource a data source has become invalid, and that a new data source
-     * is needed to continue loading data.
-     */
-    public interface InvalidatedCallback {
-        /**
-         * Called when the data backing the list has become invalid. This callback is typically used
-         * to signal that a new data source is needed.
-         * <p>
-         * This callback will be invoked on the thread that calls {@link #invalidate()}. It is valid
-         * for the data source to invalidate itself during its load methods, or for an outside
-         * source to invalidate it.
-         */
-        @AnyThread
-        void onInvalidated();
-    }
-
-    private AtomicBoolean mInvalid = new AtomicBoolean(false);
-
-    private CopyOnWriteArrayList<InvalidatedCallback> mOnInvalidatedCallbacks =
-            new CopyOnWriteArrayList<>();
-
-    /**
-     * Add a callback to invoke when the DataSource is first invalidated.
-     * <p>
-     * Once invalidated, a data source will not become valid again.
-     * <p>
-     * A data source will only invoke its callbacks once - the first time {@link #invalidate()}
-     * is called, on that thread.
-     *
-     * @param onInvalidatedCallback The callback, will be invoked on thread that invalidates the
-     *                              DataSource.
-     */
-    @AnyThread
-    public void addInvalidatedCallback(@NonNull InvalidatedCallback onInvalidatedCallback) {
-        //noinspection ConstantConditions
-        if (onInvalidatedCallback == null) {
-            throw new IllegalArgumentException("onInvalidatedCallback must be non-null");
-        }
-        mOnInvalidatedCallbacks.add(onInvalidatedCallback);
-    }
-
-    /**
-     * Remove a previously added invalidate callback.
-     *
-     * @param onInvalidatedCallback The previously added callback.
-     */
-    @AnyThread
-    public void removeInvalidatedCallback(@NonNull InvalidatedCallback onInvalidatedCallback) {
-        mOnInvalidatedCallbacks.remove(onInvalidatedCallback);
-    }
-
-    /**
-     * Signal the data source to stop loading, and notify its callback.
-     * <p>
-     * If invalidate has already been called, this method does nothing.
-     */
-    @AnyThread
-    public void invalidate() {
-        if (mInvalid.compareAndSet(false, true)) {
-            for (InvalidatedCallback callback : mOnInvalidatedCallbacks) {
-                callback.onInvalidated();
-            }
-        }
-    }
-
-    /**
-     * Returns true if the data source is invalid, and can no longer be queried for data.
-     *
-     * @return True if the data source is invalid, and can no longer return data.
-     */
-    @WorkerThread
-    public boolean isInvalid() {
-        return mInvalid.get();
-    }
-
-    enum LoadType {
-        INITIAL,
-        START,
-        END
-    }
-
-    @SuppressWarnings("WeakerAccess")
-    static class Params<K> {
-        @NonNull
-        public final LoadType type;
-        /* can be NULL for init, otherwise non-null */
-        @Nullable
-        public final K key;
-        public final int initialLoadSize;
-        public final boolean placeholdersEnabled;
-        public final int pageSize;
-
-        Params(@NonNull LoadType type, @Nullable K key, int initialLoadSize,
-                boolean placeholdersEnabled, int pageSize) {
-            this.type = type;
-            this.key = key;
-            this.initialLoadSize = initialLoadSize;
-            this.placeholdersEnabled = placeholdersEnabled;
-            this.pageSize = pageSize;
-        }
-    }
-
-    @SuppressWarnings("WeakerAccess")
-    static class BaseResult<Value> {
-        @SuppressWarnings("unchecked")
-        static <T> BaseResult<T> empty() {
-            return (BaseResult<T>) EMPTY;
-        }
-
-        private static final BaseResult<Object> EMPTY =
-                new BaseResult<>(Collections.emptyList(), null, null, 0, 0, 0, true);
-
-        public final List<Value> data;
-        public final Object prevKey;
-        public final Object nextKey;
-        public final int leadingNulls;
-        public final int trailingNulls;
-        public final int offset;
-        /**
-         * Set to true if the result is an initial load that is passed totalCount
-         */
-        public final boolean counted;
-
-        protected BaseResult(List<Value> data, Object prevKey, Object nextKey, int leadingNulls,
-                int trailingNulls, int offset, boolean counted) {
-            this.data = data;
-            this.prevKey = prevKey;
-            this.nextKey = nextKey;
-            this.leadingNulls = leadingNulls;
-            this.trailingNulls = trailingNulls;
-            this.offset = offset;
-            this.counted = counted;
-            validate();
-        }
-
-        <ToValue> BaseResult(@NonNull BaseResult<ToValue> result,
-                @NonNull Function<List<ToValue>, List<Value>> function) {
-            data = convert(function, result.data);
-            prevKey = result.prevKey;
-            nextKey = result.nextKey;
-            leadingNulls = result.leadingNulls;
-            trailingNulls = result.trailingNulls;
-            offset = result.offset;
-            counted = result.counted;
-            validate();
-        }
-
-        private int position() {
-            // only one of leadingNulls / offset may be used
-            return leadingNulls + offset;
-        }
-
-        static final int TOTAL_COUNT_UNKNOWN = -1;
-
-        int totalCount() {
-            // only one of leadingNulls / offset may be used
-            if (counted) {
-                return position() + data.size() + trailingNulls;
-            } else {
-                return TOTAL_COUNT_UNKNOWN;
-            }
-
-        }
-
-        void validate() {
-            if (leadingNulls < 0 || offset < 0) {
-                throw new IllegalArgumentException("Position must be non-negative");
-            }
-            if (data.isEmpty() && (leadingNulls != 0 || trailingNulls != 0)) {
-                throw new IllegalArgumentException("Initial result cannot be empty if items are"
-                        + " present in data set.");
-            }
-            if (trailingNulls < 0) {
-                throw new IllegalArgumentException(
-                        "List size + position too large, last item in list beyond totalCount.");
-            }
-        }
-
-        void validateForInitialTiling(int pageSize) {
-            if (!counted) {
-                throw new IllegalStateException("Placeholders requested, but totalCount not"
-                        + " provided. Please call the three-parameter onResult method, or"
-                        + " disable placeholders in the PagedList.Config");
-            }
-            if (trailingNulls != 0
-                    && data.size() % pageSize != 0) {
-                int totalCount = leadingNulls + data.size() + trailingNulls;
-                throw new IllegalArgumentException("PositionalDataSource requires initial load size"
-                        + " to be a multiple of page size to support internal tiling. loadSize "
-                        + data.size() + ", position " + leadingNulls + ", totalCount " + totalCount
-                        + ", pageSize " + pageSize);
-            }
-            if (position() % pageSize != 0) {
-                throw new IllegalArgumentException("Initial load must be pageSize aligned."
-                        + "Position = " + position() + ", pageSize = " + pageSize);
-            }
-        }
-
-        @SuppressWarnings("EqualsHashCode")
-        @Override
-        public boolean equals(Object o) {
-            if (!(o instanceof BaseResult)) {
-                return false;
-            }
-            BaseResult other = (BaseResult) o;
-            return data.equals(other.data)
-                    && PagedList.equalsHelper(prevKey, other.prevKey)
-                    && PagedList.equalsHelper(nextKey, other.nextKey)
-                    && leadingNulls == other.leadingNulls
-                    && trailingNulls == other.trailingNulls
-                    && offset == other.offset
-                    && counted == other.counted;
-        }
-    }
-
-    enum KeyType {
-        POSITIONAL,
-        PAGE_KEYED,
-        ITEM_KEYED,
-    }
-
-    @NonNull
-    final KeyType mType;
-
-    // Since we currently rely on implementation details of two implementations,
-    // prevent external subclassing, except through exposed subclasses
-    DataSource(@NonNull KeyType type) {
-        mType = type;
-    }
-
-    abstract ListenableFuture<? extends BaseResult<Value>> load(
-            @NonNull Params<Key> params);
-
-    @Nullable
-    abstract Key getKey(@NonNull Value item);
-
-    @Nullable
-    final Key getKey(int lastLoad, @Nullable Value item) {
-        if (mType == KeyType.POSITIONAL) {
-            //noinspection unchecked
-            return (Key) ((Integer) lastLoad);
-        }
-        if (item == null) {
-            return null;
-        }
-        return getKey(item);
-    }
-
-    /**
-     * Determine whether an error passed to a loading method is retryable.
-     *
-     * @param error Throwable returned from an attempted load from this DataSource.
-     * @return true if the error is retryable, otherwise false.
-     */
-    public boolean isRetryableError(@NonNull Throwable error) {
-        return false;
-    }
-
-    final void initExecutor(@NonNull Executor executor) {
-        mExecutor = executor;
-    }
-
-    /**
-     * Null until loadInitial is called by PagedList construction
-     */
-    @Nullable
-    private Executor mExecutor;
-
-    @NonNull
-    Executor getExecutor() {
-        if (mExecutor == null) {
-            throw new IllegalStateException(
-                    "This DataSource has not been passed to a PagedList, has no executor yet.");
-        }
-        return mExecutor;
-    }
-}
diff --git a/paging/common/src/main/java/androidx/paging/InitialPagedList.java b/paging/common/src/main/java/androidx/paging/InitialPagedList.java
deleted file mode 100644
index 2589bdf..0000000
--- a/paging/common/src/main/java/androidx/paging/InitialPagedList.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * 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.
- */
-
-package androidx.paging;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.paging.futures.DirectExecutor;
-
-/**
- * InitialPagedList is an empty placeholder that's sent at the front of a stream of PagedLists.
- *
- * It's used solely for listening to {@link PagedList.LoadType#REFRESH} loading events, and retrying
- * any errors that occur during initial load.
- */
-class InitialPagedList<K, V> extends ContiguousPagedList<K, V> {
-    @Nullable
-    private K mInitialKey;
-
-    InitialPagedList(
-            @NonNull DataSource<K, V> dataSource,
-            @NonNull Config config,
-            @Nullable K initialKey) {
-        super(dataSource,
-                DirectExecutor.INSTANCE,
-                DirectExecutor.INSTANCE,
-                null,
-                config,
-                DataSource.BaseResult.<V>empty(),
-                /* no previous load, so pass 0 */ 0);
-        mInitialKey = initialKey;
-    }
-
-    @Nullable
-    @Override
-    public Object getLastKey() {
-        return mInitialKey;
-    }
-}
diff --git a/paging/common/src/main/java/androidx/paging/ItemKeyedDataSource.java b/paging/common/src/main/java/androidx/paging/ItemKeyedDataSource.java
deleted file mode 100644
index bba7477d..0000000
--- a/paging/common/src/main/java/androidx/paging/ItemKeyedDataSource.java
+++ /dev/null
@@ -1,348 +0,0 @@
-/*
- * 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.util.Function;
-import androidx.concurrent.futures.ResolvableFuture;
-
-import com.google.common.util.concurrent.ListenableFuture;
-
-import java.util.List;
-
-/**
- * Incremental data loader for paging keyed content, where loaded content uses previously loaded
- * items as input to future loads.
- * <p>
- * Implement a DataSource using ItemKeyedDataSource if you need to use data from item {@code N - 1}
- * to load item {@code N}. This is common, for example, in uniquely sorted database queries where
- * attributes of the item such just before the next query define how to execute it.
- * <p>
- * The {@code InMemoryByItemRepository} in the
- * <a href="https://github.com/googlesamples/android-architecture-components/blob/master/PagingWithNetworkSample/README.md">PagingWithNetworkSample</a>
- * shows how to implement a network ItemKeyedDataSource using
- * <a href="https://square.github.io/retrofit/">Retrofit</a>, while
- * handling swipe-to-refresh, network errors, and retry.
- *
- * @see ListenableItemKeyedDataSource
- *
- * @param <Key> Type of data used to query Value types out of the DataSource.
- * @param <Value> Type of items being loaded by the DataSource.
- */
-public abstract class ItemKeyedDataSource<Key, Value> extends
-        ListenableItemKeyedDataSource<Key, Value> {
-
-    /**
-     * Holder object for inputs to {@link #loadInitial(LoadInitialParams, LoadInitialCallback)}.
-     *
-     * @param <Key> Type of data used to query Value types out of the DataSource.
-     */
-    @SuppressWarnings("WeakerAccess")
-    public static class LoadInitialParams<Key> extends
-            ListenableItemKeyedDataSource.LoadInitialParams<Key> {
-        public LoadInitialParams(@Nullable Key requestedInitialKey, int requestedLoadSize,
-                boolean placeholdersEnabled) {
-            super(requestedInitialKey, requestedLoadSize, placeholdersEnabled);
-        }
-    }
-
-    /**
-     * Holder object for inputs to {@link #loadBefore(LoadParams, LoadCallback)}
-     * and {@link #loadAfter(LoadParams, LoadCallback)}.
-     *
-     * @param <Key> Type of data used to query Value types out of the DataSource.
-     */
-    @SuppressWarnings("WeakerAccess")
-    public static class LoadParams<Key> extends ListenableItemKeyedDataSource.LoadParams<Key> {
-        public LoadParams(@NonNull Key key, int requestedLoadSize) {
-            super(key, requestedLoadSize);
-        }
-    }
-
-    /**
-     * Callback for {@link #loadInitial(LoadInitialParams, LoadInitialCallback)}
-     * to return data and, optionally, position/count information.
-     * <p>
-     * A callback can be called only once, and will throw if called again.
-     * <p>
-     * If you can compute the number of items in the data set before and after the loaded range,
-     * call the three parameter {@link #onResult(List, int, int)} to pass that information. You
-     * can skip passing this information by calling the single parameter {@link #onResult(List)},
-     * either if it's difficult to compute, or if {@link LoadInitialParams#placeholdersEnabled} is
-     * {@code false}, so the positioning information will be ignored.
-     * <p>
-     * It is always valid for a DataSource loading method that takes a callback to stash the
-     * callback and call it later. This enables DataSources to be fully asynchronous, and to handle
-     * temporary, recoverable error states (such as a network error that can be retried).
-     *
-     * @param <Value> Type of items being loaded.
-     */
-    public abstract static class LoadInitialCallback<Value> extends LoadCallback<Value> {
-        /**
-         * Called to pass initial load state from a DataSource.
-         * <p>
-         * Call this method from your DataSource's {@code loadInitial} function to return data,
-         * and inform how many placeholders should be shown before and after. If counting is cheap
-         * to compute (for example, if a network load returns the information regardless), it's
-         * recommended to pass data back through this method.
-         * <p>
-         * It is always valid to pass a different amount of data than what is requested. Pass an
-         * empty list if there is no more data to load.
-         *
-         * @param data List of items loaded from the DataSource. If this is empty, the DataSource
-         *             is treated as empty, and no further loads will occur.
-         * @param position Position of the item at the front of the list. If there are {@code N}
-         *                 items before the items in data that can be loaded from this DataSource,
-         *                 pass {@code N}.
-         * @param totalCount Total number of items that may be returned from this DataSource.
-         *                   Includes the number in the initial {@code data} parameter
-         *                   as well as any items that can be loaded in front or behind of
-         *                   {@code data}.
-         */
-        public abstract void onResult(@NonNull List<Value> data, int position, int totalCount);
-    }
-
-    /**
-     * Callback for ItemKeyedDataSource {@link #loadBefore(LoadParams, LoadCallback)}
-     * and {@link #loadAfter(LoadParams, LoadCallback)} to return data.
-     * <p>
-     * A callback can be called only once, and will throw if called again.
-     * <p>
-     * It is always valid for a DataSource loading method that takes a callback to stash the
-     * callback and call it later. This enables DataSources to be fully asynchronous, and to handle
-     * temporary, recoverable error states (such as a network error that can be retried).
-     *
-     * @param <Value> Type of items being loaded.
-     */
-    public abstract static class LoadCallback<Value> {
-        /**
-         * Called to pass loaded data from a DataSource.
-         * <p>
-         * Call this method from your ItemKeyedDataSource's
-         * {@link #loadBefore(LoadParams, LoadCallback)} and
-         * {@link #loadAfter(LoadParams, LoadCallback)} methods to return data.
-         * <p>
-         * Call this from {@link #loadInitial(LoadInitialParams, LoadInitialCallback)} to
-         * initialize without counting available data, or supporting placeholders.
-         * <p>
-         * It is always valid to pass a different amount of data than what is requested. Pass an
-         * empty list if there is no more data to load.
-         *
-         * @param data List of items loaded from the ItemKeyedDataSource.
-         */
-        public abstract void onResult(@NonNull List<Value> data);
-
-        /**
-         * Called to report an error from a DataSource.
-         * <p>
-         * Call this method to report an error from
-         * {@link #loadInitial(LoadInitialParams, LoadInitialCallback)},
-         * {@link #loadBefore(LoadParams, LoadCallback)}, or
-         * {@link #loadAfter(LoadParams, LoadCallback)} methods.
-         *
-         * @param error The error that occurred during loading.
-         */
-        public void onError(@NonNull Throwable error) {
-            // TODO: remove default implementation in 3.0
-            throw new IllegalStateException(
-                    "You must implement onError if implementing your own load callback");
-        }
-    }
-
-    @NonNull
-    @Override
-    public final ListenableFuture<InitialResult<Value>> loadInitial(
-            final @NonNull ListenableItemKeyedDataSource.LoadInitialParams<Key> params) {
-        final ResolvableFuture<InitialResult<Value>> future = ResolvableFuture.create();
-        getExecutor().execute(new Runnable() {
-            @Override
-            public void run() {
-                LoadInitialCallback<Value> callback = new LoadInitialCallback<Value>() {
-                    @Override
-                    public void onResult(@NonNull List<Value> data, int position, int totalCount) {
-                        future.set(new InitialResult<>(data, position, totalCount));
-                    }
-
-                    @Override
-                    public void onResult(@NonNull List<Value> data) {
-                        future.set(new InitialResult<>(data));
-                    }
-
-                    @Override
-                    public void onError(@NonNull Throwable error) {
-                        future.setException(error);
-                    }
-                };
-                loadInitial(new LoadInitialParams<>(
-                                params.requestedInitialKey,
-                                params.requestedLoadSize,
-                                params.placeholdersEnabled),
-                        callback);
-            }
-        });
-        return future;
-    }
-
-    @SuppressWarnings("WeakerAccess")
-    LoadCallback<Value> getFutureAsCallback(
-            final @NonNull ResolvableFuture<Result<Value>> future) {
-        return new LoadCallback<Value>() {
-            @Override
-            public void onResult(@NonNull List<Value> data) {
-                future.set(new Result<>(data));
-            }
-
-            @Override
-            public void onError(@NonNull Throwable error) {
-                future.setException(error);
-            }
-        };
-    }
-
-    @NonNull
-    @Override
-    public final ListenableFuture<Result<Value>> loadBefore(
-            final @NonNull ListenableItemKeyedDataSource.LoadParams<Key> params) {
-        final ResolvableFuture<Result<Value>> future = ResolvableFuture.create();
-        getExecutor().execute(new Runnable() {
-            @Override
-            public void run() {
-                loadBefore(new LoadParams<>(params.key, params.requestedLoadSize),
-                        getFutureAsCallback(future));
-            }
-        });
-        return future;
-    }
-
-    @NonNull
-    @Override
-    public final ListenableFuture<Result<Value>> loadAfter(
-            final @NonNull ListenableItemKeyedDataSource.LoadParams<Key> params) {
-        final ResolvableFuture<Result<Value>> future = ResolvableFuture.create();
-        getExecutor().execute(new Runnable() {
-            @Override
-            public void run() {
-                loadAfter(new LoadParams<>(params.key, params.requestedLoadSize),
-                        getFutureAsCallback(future));
-            }
-        });
-        return future;
-    }
-
-    /**
-     * Load initial data.
-     * <p>
-     * This method is called first to initialize a PagedList with data. If it's possible to count
-     * the items that can be loaded by the DataSource, it's recommended to pass the loaded data to
-     * the callback via the three-parameter
-     * {@link LoadInitialCallback#onResult(List, int, int)}. This enables PagedLists
-     * presenting data from this source to display placeholders to represent unloaded items.
-     * <p>
-     * {@link LoadInitialParams#requestedInitialKey} and {@link LoadInitialParams#requestedLoadSize}
-     * are hints, not requirements, so they may be altered or ignored. Note that ignoring the
-     * {@code requestedInitialKey} can prevent subsequent PagedList/DataSource pairs from
-     * initializing at the same location. If your DataSource never invalidates (for example,
-     * loading from the network without the network ever signalling that old data must be reloaded),
-     * it's fine to ignore the {@code initialLoadKey} and always start from the beginning of the
-     * data set.
-     *
-     * @param params Parameters for initial load, including initial key and requested size.
-     * @param callback Callback that receives initial load data.
-     */
-    public abstract void loadInitial(
-            @NonNull LoadInitialParams<Key> params,
-            @NonNull LoadInitialCallback<Value> callback);
-
-    /**
-     * Load list data after the key specified in {@link LoadParams#key LoadParams.key}.
-     * <p>
-     * It's valid to return a different list size than the page size if it's easier, e.g. if your
-     * backend defines page sizes. It is generally preferred to increase the number loaded than
-     * reduce.
-     * <p>
-     * Data may be passed synchronously during the loadAfter method, or deferred and called at a
-     * later time. Further loads going down will be blocked until the callback is called.
-     * <p>
-     * If data cannot be loaded (for example, if the request is invalid, or the data would be stale
-     * and inconsistent), it is valid to call {@link #invalidate()} to invalidate the data source,
-     * and prevent further loading.
-     *
-     * @param params Parameters for the load, including the key to load after, and requested size.
-     * @param callback Callback that receives loaded data.
-     */
-    public abstract void loadAfter(@NonNull LoadParams<Key> params,
-            @NonNull LoadCallback<Value> callback);
-
-    /**
-     * Load list data before the key specified in {@link LoadParams#key LoadParams.key}.
-     * <p>
-     * It's valid to return a different list size than the page size if it's easier, e.g. if your
-     * backend defines page sizes. It is generally preferred to increase the number loaded than
-     * reduce.
-     * <p>
-     * <p class="note"><strong>Note:</strong> Data returned will be prepended just before the key
-     * passed, so if you vary size, ensure that the last item is adjacent to the passed key.
-     * <p>
-     * Data may be passed synchronously during the loadBefore method, or deferred and called at a
-     * later time. Further loads going up will be blocked until the callback is called.
-     * <p>
-     * If data cannot be loaded (for example, if the request is invalid, or the data would be stale
-     * and inconsistent), it is valid to call {@link #invalidate()} to invalidate the data source,
-     * and prevent further loading.
-     *
-     * @param params Parameters for the load, including the key to load before, and requested size.
-     * @param callback Callback that receives loaded data.
-     */
-    public abstract void loadBefore(@NonNull LoadParams<Key> params,
-            @NonNull LoadCallback<Value> callback);
-
-    /**
-     * Return a key associated with the given item.
-     * <p>
-     * If your ItemKeyedDataSource is loading from a source that is sorted and loaded by a unique
-     * integer ID, you would return {@code item.getID()} here. This key can then be passed to
-     * {@link #loadBefore(LoadParams, LoadCallback)} or
-     * {@link #loadAfter(LoadParams, LoadCallback)} to load additional items adjacent to the item
-     * passed to this function.
-     * <p>
-     * If your key is more complex, such as when you're sorting by name, then resolving collisions
-     * with integer ID, you'll need to return both. In such a case you would use a wrapper class,
-     * such as {@code Pair<String, Integer>} or, in Kotlin,
-     * {@code data class Key(val name: String, val id: Int)}
-     *
-     * @param item Item to get the key from.
-     * @return Key associated with given item.
-     */
-    @NonNull
-    @Override
-    public abstract Key getKey(@NonNull Value item);
-
-    @NonNull
-    @Override
-    public final <ToValue> ItemKeyedDataSource<Key, ToValue> mapByPage(
-            @NonNull Function<List<Value>, List<ToValue>> function) {
-        return new WrapperItemKeyedDataSource<>(this, function);
-    }
-
-    @NonNull
-    @Override
-    public final <ToValue> ItemKeyedDataSource<Key, ToValue> map(
-            @NonNull Function<Value, ToValue> function) {
-        return mapByPage(createListFunction(function));
-    }
-}
diff --git a/paging/common/src/main/java/androidx/paging/ListDataSource.java b/paging/common/src/main/java/androidx/paging/ListDataSource.java
deleted file mode 100644
index 3f0971f..0000000
--- a/paging/common/src/main/java/androidx/paging/ListDataSource.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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 java.util.ArrayList;
-import java.util.List;
-
-class ListDataSource<T> extends PositionalDataSource<T> {
-    private final List<T> mList;
-
-    public ListDataSource(List<T> list) {
-        mList = new ArrayList<>(list);
-    }
-
-    @Override
-    public void loadInitial(@NonNull LoadInitialParams params,
-            @NonNull LoadInitialCallback<T> callback) {
-        final int totalCount = mList.size();
-        final int position = computeInitialLoadPosition(params, totalCount);
-        final int loadSize = computeInitialLoadSize(params, position, totalCount);
-
-        // for simplicity, we could return everything immediately,
-        // but we tile here since it's expected behavior
-        List<T> sublist = mList.subList(position, position + loadSize);
-        callback.onResult(sublist, position, totalCount);
-    }
-
-    @Override
-    public void loadRange(@NonNull LoadRangeParams params,
-            @NonNull LoadRangeCallback<T> callback) {
-        callback.onResult(mList.subList(params.startPosition,
-                Math.min(mList.size(), params.startPosition + params.loadSize)));
-    }
-}
diff --git a/paging/common/src/main/java/androidx/paging/ListenableItemKeyedDataSource.java b/paging/common/src/main/java/androidx/paging/ListenableItemKeyedDataSource.java
deleted file mode 100644
index e81193d..0000000
--- a/paging/common/src/main/java/androidx/paging/ListenableItemKeyedDataSource.java
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
- * 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 com.google.common.util.concurrent.ListenableFuture;
-
-import java.util.List;
-
-/**
- * Incremental data loader for paging keyed content, where loaded content uses previously loaded
- * items as input to future loads.
- * <p>
- * Implement a DataSource using ListenableItemKeyedDataSource if you need to use data from item
- * {@code N - 1} to load item {@code N}. This is common, for example, in uniquely sorted database
- * queries where attributes of the item such just before the next query define how to execute it.
- *
- * @see ItemKeyedDataSource
- *
- * @param <Key> Type of data used to query Value types out of the DataSource.
- * @param <Value> Type of items being loaded by the DataSource.
- */
-public abstract class ListenableItemKeyedDataSource<Key, Value> extends DataSource<Key, Value> {
-    public ListenableItemKeyedDataSource() {
-        super(KeyType.ITEM_KEYED);
-    }
-
-    @Override
-    final ListenableFuture<? extends BaseResult<Value>> load(@NonNull Params<Key> params) {
-        if (params.type == LoadType.INITIAL) {
-            ItemKeyedDataSource.LoadInitialParams<Key> initParams =
-                    new ItemKeyedDataSource.LoadInitialParams<>(params.key,
-                            params.initialLoadSize, params.placeholdersEnabled);
-            return loadInitial(initParams);
-        } else {
-            //noinspection ConstantConditions (key is known to be non-null for non-initial queries)
-            ItemKeyedDataSource.LoadParams<Key> loadParams =
-                    new ItemKeyedDataSource.LoadParams<>(params.key, params.pageSize);
-
-            if (params.type == LoadType.START) {
-                return loadBefore(loadParams);
-            } else if (params.type == LoadType.END) {
-                return loadAfter(loadParams);
-            }
-        }
-        throw new IllegalArgumentException("Unsupported type " + params.type.toString());
-    }
-
-
-    /**
-     * Holder object for inputs to {@code loadInitial()}.
-     *
-     * @param <Key> Type of data used to query Value types out of the DataSource.
-     */
-    public static class LoadInitialParams<Key> {
-        /**
-         * Load items around this key, or at the beginning of the data set if {@code null} is
-         * passed.
-         * <p>
-         * Note that this key is generally a hint, and may be ignored if you want to always load
-         * from the beginning.
-         */
-        @Nullable
-        public final Key requestedInitialKey;
-
-        /**
-         * Requested number of items to load.
-         * <p>
-         * Note that this may be larger than available data.
-         */
-        public final int requestedLoadSize;
-
-        /**
-         * Defines whether placeholders are enabled, and whether the loaded total count will be
-         * ignored.
-         */
-        public final boolean placeholdersEnabled;
-
-        public LoadInitialParams(@Nullable Key requestedInitialKey, int requestedLoadSize,
-                boolean placeholdersEnabled) {
-            this.requestedInitialKey = requestedInitialKey;
-            this.requestedLoadSize = requestedLoadSize;
-            this.placeholdersEnabled = placeholdersEnabled;
-        }
-    }
-
-    /**
-     * Holder object for inputs to {@code loadBefore()} and {@code loadAfter()}.
-     *
-     * @param <Key> Type of data used to query Value types out of the DataSource.
-     */
-    public static class LoadParams<Key> {
-        /**
-         * Load items before/after this key.
-         * <p>
-         * Returned data must begin directly adjacent to this position.
-         */
-        @NonNull
-        public final Key key;
-        /**
-         * Requested number of items to load.
-         * <p>
-         * Returned page can be of this size, but it may be altered if that is easier, e.g. a
-         * network data source where the backend defines page size.
-         */
-        public final int requestedLoadSize;
-
-        public LoadParams(@NonNull Key key, int requestedLoadSize) {
-            this.key = key;
-            this.requestedLoadSize = requestedLoadSize;
-        }
-    }
-
-    /**
-     * Load initial data.
-     * <p>
-     * This method is called first to initialize a PagedList with data. If it's possible to count
-     * the items that can be loaded by the DataSource, it's recommended to pass {@code totalCount}
-     * to the {@link InitialResult} constructor. This enables PagedLists presenting data from this
-     * source to display placeholders to represent unloaded items.
-     * <p>
-     * {@link ItemKeyedDataSource.LoadInitialParams#requestedInitialKey} and
-     * {@link ItemKeyedDataSource.LoadInitialParams#requestedLoadSize} are hints, not requirements,
-     * so they may be altered or ignored. Note that ignoring the {@code requestedInitialKey} can
-     * prevent subsequent PagedList/DataSource pairs from initializing at the same location. If your
-     * DataSource never invalidates (for example, loading from the network without the network ever
-     * signalling that old data must be reloaded), it's fine to ignore the {@code initialLoadKey}
-     * and always start from the beginning of the data set.
-     *
-     * @param params Parameters for initial load, including initial key and requested size.
-     * @return ListenableFuture of the loaded data.
-     */
-    @NonNull
-    public abstract ListenableFuture<InitialResult<Value>> loadInitial(
-            @NonNull LoadInitialParams<Key> params);
-
-    /**
-     * Load list data after the key specified in
-     * {@link ItemKeyedDataSource.LoadParams#key LoadParams.key}.
-     * <p>
-     * It's valid to return a different list size than the page size if it's easier, e.g. if your
-     * backend defines page sizes. It is generally preferred to increase the number loaded than
-     * reduce.
-     * <p>
-     * If data cannot be loaded (for example, if the request is invalid, or the data would be stale
-     * and inconsistent), it is valid to call {@link #invalidate()} to invalidate the data source,
-     * and prevent further loading.
-     *
-     * @param params Parameters for the load, including the key to load after, and requested size.
-     * @return ListenableFuture of the loaded data.
-     */
-    @NonNull
-    public abstract ListenableFuture<Result<Value>> loadAfter(@NonNull LoadParams<Key> params);
-
-
-    /**
-     * Load list data after the key specified in
-     * {@link ItemKeyedDataSource.LoadParams#key LoadParams.key}.
-     * <p>
-     * It's valid to return a different list size than the page size if it's easier, e.g. if your
-     * backend defines page sizes. It is generally preferred to increase the number loaded than
-     * reduce.
-     * <p>
-     * <p class="note"><strong>Note:</strong> Data returned will be prepended just before the key
-     * passed, so if you don't return a page of the requested size, ensure that the last item is
-     * adjacent to the passed key.
-     * It's valid to return a different list size than the page size if it's easier, e.g. if your
-     * backend defines page sizes. It is generally preferred to increase the number loaded than
-     * reduce.
-     * <p>
-     * If data cannot be loaded (for example, if the request is invalid, or the data would be stale
-     * and inconsistent), it is valid to call {@link #invalidate()} to invalidate the data source,
-     * and prevent further loading.
-     *
-     * @param params Parameters for the load, including the key to load before, and requested size.
-     * @return ListenableFuture of the loaded data.
-     */
-    @NonNull
-    public abstract ListenableFuture<Result<Value>> loadBefore(@NonNull LoadParams<Key> params);
-
-
-    @Nullable
-    @Override
-    public abstract Key getKey(@NonNull Value item);
-
-    /**
-     * Type produced by {@link #loadInitial(LoadInitialParams)} to represent
-     * initially loaded data.
-     *
-     * @param <V> The type of the data loaded.
-     */
-    public static class InitialResult<V> extends BaseResult<V> {
-        public InitialResult(@NonNull List<V> data, int position, int totalCount) {
-            super(data, null, null, position, totalCount - data.size() - position, position, true);
-        }
-
-        public InitialResult(@NonNull List<V> data) {
-            super(data, null, null, 0, 0, 0, false);
-        }
-    }
-
-    /**
-     * Type produced by {@link #loadBefore(LoadParams)} and
-     * {@link #loadAfter(LoadParams)} to represent a page of loaded data.
-     *
-     * @param <V> The type of the data loaded.
-     */
-    public static class Result<V> extends BaseResult<V> {
-        public Result(@NonNull List<V> data) {
-            super(data, null, null, 0, 0, 0, false);
-        }
-    }
-}
diff --git a/paging/common/src/main/java/androidx/paging/ListenablePageKeyedDataSource.java b/paging/common/src/main/java/androidx/paging/ListenablePageKeyedDataSource.java
deleted file mode 100644
index f15075c..0000000
--- a/paging/common/src/main/java/androidx/paging/ListenablePageKeyedDataSource.java
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
- * 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.
- */
-
-package androidx.paging;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.concurrent.futures.ResolvableFuture;
-
-import com.google.common.util.concurrent.ListenableFuture;
-
-import java.util.List;
-
-/**
- * Incremental data loader for page-keyed content, where requests return keys for next/previous
- * pages.
- * <p>
- * Implement a DataSource using PageKeyedDataSource if you need to use data from page {@code N - 1}
- * to load page {@code N}. This is common, for example, in network APIs that include a next/previous
- * link or key with each page load.
- *
- * @param <Key> Type of data used to query Value types out of the DataSource.
- * @param <Value> Type of items being loaded by the DataSource.
- */
-public abstract class ListenablePageKeyedDataSource<Key, Value> extends DataSource<Key, Value> {
-    public ListenablePageKeyedDataSource() {
-        super(KeyType.PAGE_KEYED);
-    }
-
-    @Override
-    final ListenableFuture<? extends BaseResult<Value>> load(
-            @NonNull Params<Key> params) {
-        if (params.type == LoadType.INITIAL) {
-            PageKeyedDataSource.LoadInitialParams<Key> initParams =
-                    new PageKeyedDataSource.LoadInitialParams<>(
-                            params.initialLoadSize, params.placeholdersEnabled);
-            return loadInitial(initParams);
-        } else {
-            if (params.key == null) {
-                // null key, immediately return empty data
-                ResolvableFuture<BaseResult<Value>> future = ResolvableFuture.create();
-                future.set(BaseResult.<Value>empty());
-                return future;
-            }
-
-            PageKeyedDataSource.LoadParams<Key> loadParams =
-                    new PageKeyedDataSource.LoadParams<>(params.key, params.pageSize);
-
-            if (params.type == LoadType.START) {
-                return loadBefore(loadParams);
-            } else if (params.type == LoadType.END) {
-                return loadAfter(loadParams);
-            }
-        }
-        throw new IllegalArgumentException("Unsupported type " + params.type.toString());
-    }
-
-    /**
-     * Holder object for inputs to {@code loadInitial()}.
-     *
-     * @param <Key> Type of data used to query pages.
-     */
-    @SuppressWarnings("unused")
-    public static class LoadInitialParams<Key> {
-        /**
-         * Requested number of items to load.
-         * <p>
-         * Note that this may be larger than available data.
-         */
-        public final int requestedLoadSize;
-
-        /**
-         * Defines whether placeholders are enabled, and whether the loaded total count will be
-         * ignored.
-         */
-        public final boolean placeholdersEnabled;
-
-
-        public LoadInitialParams(int requestedLoadSize, boolean placeholdersEnabled) {
-            this.requestedLoadSize = requestedLoadSize;
-            this.placeholdersEnabled = placeholdersEnabled;
-        }
-    }
-
-    /**
-     * Holder object for inputs to {@code loadBefore()} and {@code loadAfter()}.
-     *
-     * @param <Key> Type of data used to query pages.
-     */
-    public static class LoadParams<Key> {
-        /**
-         * Load items before/after this key.
-         * <p>
-         * Returned data must begin directly adjacent to this position.
-         */
-        @NonNull
-        public final Key key;
-
-        /**
-         * Requested number of items to load.
-         * <p>
-         * Returned page can be of this size, but it may be altered if that is easier, e.g. a
-         * network data source where the backend defines page size.
-         */
-        public final int requestedLoadSize;
-
-        public LoadParams(@NonNull Key key, int requestedLoadSize) {
-            this.key = key;
-            this.requestedLoadSize = requestedLoadSize;
-        }
-    }
-
-    /**
-     * Load initial data.
-     * <p>
-     * This method is called first to initialize a PagedList with data. If it's possible to count
-     * the items that can be loaded by the DataSource, it's recommended to pass the position and
-     * count to the
-     * {@link InitialResult InitialResult constructor}. This
-     * enables PagedLists presenting data from this source to display placeholders to represent
-     * unloaded items.
-     * <p>
-     * {@link LoadInitialParams#requestedLoadSize} is a hint, not a requirement,
-     * so it may be may be altered or ignored.
-     *
-     * @param params Parameters for initial load, including requested load size.
-     * @return ListenableFuture of the loaded data.
-     */
-    @NonNull
-    public abstract ListenableFuture<InitialResult<Key, Value>> loadInitial(
-            @NonNull LoadInitialParams<Key> params);
-
-    /**
-     * Prepend page with the key specified by
-     * {@link PageKeyedDataSource.LoadParams#key LoadParams.key}.
-     * <p>
-     * It's valid to return a different list size than the page size if it's easier, e.g. if your
-     * backend defines page sizes. It is generally preferred to increase the number loaded than
-     * reduce.
-     * <p>
-     * If data cannot be loaded (for example, if the request is invalid, or the data would be stale
-     * and inconsistent), it is valid to call {@link #invalidate()} to invalidate the data source,
-     * and prevent further loading.
-     *
-     * @param params Parameters for the load, including the key for the new page, and requested load
-     *               size.
-     * @return ListenableFuture of the loaded data.
-     */
-    @NonNull
-    public abstract ListenableFuture<Result<Key, Value>> loadBefore(
-            @NonNull LoadParams<Key> params);
-    /**
-     * Append page with the key specified by
-     * {@link PageKeyedDataSource.LoadParams#key LoadParams.key}.
-     * <p>
-     * It's valid to return a different list size than the page size if it's easier, e.g. if your
-     * backend defines page sizes. It is generally preferred to increase the number loaded than
-     * reduce.
-     * <p>
-     * If data cannot be loaded (for example, if the request is invalid, or the data would be stale
-     * and inconsistent), it is valid to call {@link #invalidate()} to invalidate the data source,
-     * and prevent further loading.
-     *
-     * @param params Parameters for the load, including the key for the new page, and requested load
-     *               size.
-     * @return ListenableFuture of the loaded data.
-     */
-    @NonNull
-    public abstract ListenableFuture<Result<Key, Value>> loadAfter(
-            @NonNull LoadParams<Key> params);
-
-    @Nullable
-    @Override
-    Key getKey(@NonNull Value item) {
-        return null;
-    }
-
-    @Override
-    boolean supportsPageDropping() {
-        /* To support page dropping when PageKeyed, we'll need to:
-         *    - Stash keys for every page we have loaded (can id by index relative to loadInitial)
-         *    - Drop keys for any page not adjacent to loaded content
-         *    - And either:
-         *        - Allow impl to signal previous page key: onResult(data, nextPageKey, prevPageKey)
-         *        - Re-trigger loadInitial, and break assumption it will only occur once.
-         */
-        return false;
-    }
-
-    /**
-     * Type produced by {@link #loadInitial(LoadInitialParams)} to represent
-     * initially loaded data.
-     *
-     * @param <Key> Type of key used to identify pages.
-     * @param <Value> Type of items being loaded by the DataSource.
-     */
-    public static class InitialResult<Key, Value> extends BaseResult<Value> {
-        public InitialResult(@NonNull List<Value> data, int position, int totalCount,
-                @Nullable Key previousPageKey, @Nullable Key nextPageKey) {
-            super(data, previousPageKey, nextPageKey,
-                    position, totalCount - data.size() - position, position, true);
-        }
-
-        public InitialResult(@NonNull List<Value> data, @Nullable Key previousPageKey,
-                @Nullable Key nextPageKey) {
-            super(data, previousPageKey, nextPageKey, 0, 0, 0, false);
-        }
-    }
-
-    /**
-     * Type produced by {@link #loadBefore(LoadParams)} and {@link #loadAfter(LoadParams)} to
-     * represent a page of loaded data.
-     *
-     * @param <Key> Type of key used to identify pages.
-     * @param <Value> Type of items being loaded by the DataSource.
-     */
-    public static class Result<Key, Value> extends BaseResult<Value> {
-        public Result(@NonNull List<Value> data, @Nullable Key adjacentPageKey) {
-            super(data, adjacentPageKey, adjacentPageKey, 0, 0, 0, false);
-        }
-    }
-}
diff --git a/paging/common/src/main/java/androidx/paging/ListenablePositionalDataSource.java b/paging/common/src/main/java/androidx/paging/ListenablePositionalDataSource.java
deleted file mode 100644
index a28e156..0000000
--- a/paging/common/src/main/java/androidx/paging/ListenablePositionalDataSource.java
+++ /dev/null
@@ -1,336 +0,0 @@
-/*
- * 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 com.google.common.util.concurrent.ListenableFuture;
-
-import java.util.List;
-
-/**
- * Position-based data loader for a fixed-size, countable data set, supporting fixed-size loads at
- * arbitrary page positions.
- * <p>
- * Extend ListenablePositionalDataSource if you can load pages of a requested size at arbitrary
- * positions, and provide a fixed item count. If your data source can't support loading arbitrary
- * requested page sizes (e.g. when network page size constraints are only known at runtime), either
- * use {@link PageKeyedDataSource} or {@link ItemKeyedDataSource}, or pass the initial result with
- *  the two parameter {@link InitialResult InitialResult constructor}.
- *
- * @see PositionalDataSource
- *
- * @param <T> Type of items being loaded by the PositionalDataSource.
- */
-public abstract class ListenablePositionalDataSource<T> extends DataSource<Integer, T> {
-    public ListenablePositionalDataSource() {
-        super(KeyType.POSITIONAL);
-    }
-
-    @Override
-    final ListenableFuture<? extends BaseResult<T>> load(@NonNull Params<Integer> params) {
-        if (params.type == LoadType.INITIAL) {
-            int initialPosition = 0;
-            int initialLoadSize = params.initialLoadSize;
-            if (params.key != null) {
-                initialPosition = params.key;
-
-                if (params.placeholdersEnabled) {
-                    // snap load size to page multiple (minimum two)
-                    initialLoadSize = Math.max(initialLoadSize / params.pageSize, 2)
-                            * params.pageSize;
-
-                    // move start so the load is centered around the key, not starting at it
-                    final int idealStart = initialPosition - initialLoadSize / 2;
-                    initialPosition = Math.max(0, idealStart / params.pageSize * params.pageSize);
-                } else {
-                    // not tiled, so don't try to snap or force multiple of a page size
-                    initialPosition = initialPosition - initialLoadSize / 2;
-                }
-
-            }
-            PositionalDataSource.LoadInitialParams initParams =
-                    new PositionalDataSource.LoadInitialParams(
-                            initialPosition,
-                            initialLoadSize,
-                            params.pageSize,
-                            params.placeholdersEnabled);
-            return loadInitial(initParams);
-        } else {
-            int startIndex = params.key;
-            int loadSize = params.pageSize;
-            if (params.type == LoadType.START) {
-                loadSize = Math.min(loadSize, startIndex + 1);
-                startIndex = startIndex - loadSize + 1;
-            }
-            return loadRange(new PositionalDataSource.LoadRangeParams(startIndex, loadSize));
-        }
-    }
-
-    /**
-     * Holder object for inputs to {@code loadInitial()}.
-     */
-    public static class LoadInitialParams {
-        /**
-         * Initial load position requested.
-         * <p>
-         * Note that this may not be within the bounds of your data set, it may need to be adjusted
-         * before you execute your load.
-         */
-        public final int requestedStartPosition;
-
-        /**
-         * Requested number of items to load.
-         * <p>
-         * Note that this may be larger than available data.
-         */
-        public final int requestedLoadSize;
-
-        /**
-         * Defines page size acceptable for return values.
-         * <p>
-         * List of items passed to the callback must be an integer multiple of page size.
-         */
-        public final int pageSize;
-
-        /**
-         * Defines whether placeholders are enabled, and whether the loaded total count will be
-         * ignored.
-         */
-        public final boolean placeholdersEnabled;
-
-        public LoadInitialParams(
-                int requestedStartPosition,
-                int requestedLoadSize,
-                int pageSize,
-                boolean placeholdersEnabled) {
-            this.requestedStartPosition = requestedStartPosition;
-            this.requestedLoadSize = requestedLoadSize;
-            this.pageSize = pageSize;
-            this.placeholdersEnabled = placeholdersEnabled;
-        }
-    }
-
-    /**
-     * Holder object for inputs to {@code loadRange()}.
-     */
-    public static class LoadRangeParams {
-        /**
-         * START position of data to load.
-         * <p>
-         * Returned data must start at this position.
-         */
-        public final int startPosition;
-        /**
-         * Number of items to load.
-         * <p>
-         * Returned data must be of this size, unless at end of the list.
-         */
-        public final int loadSize;
-
-        public LoadRangeParams(int startPosition, int loadSize) {
-            this.startPosition = startPosition;
-            this.loadSize = loadSize;
-        }
-    }
-
-    /**
-     * Load initial list data.
-     * <p>
-     * This method is called to load the initial page(s) from the DataSource.
-     * <p>
-     * Result list must be a multiple of pageSize to enable efficient tiling.
-     *
-     * @param params Parameters for initial load, including requested start position, load size, and
-     *               page size.
-     * @return ListenableFuture of the loaded data.
-     */
-    @NonNull
-    public abstract ListenableFuture<InitialResult<T>> loadInitial(
-            @NonNull LoadInitialParams params);
-
-    /**
-     * Called to load a range of data from the DataSource.
-     * <p>
-     * This method is called to load additional pages from the DataSource after the
-     * LoadInitialCallback passed to dispatchLoadInitial has initialized a PagedList.
-     * <p>
-     * Unlike {@link #loadInitial(LoadInitialParams)}, this method must return
-     * the number of items requested, at the position requested.
-     *
-     * @param params Parameters for load, including start position and load size.
-     * @return ListenableFuture of the loaded data.
-     */
-    @NonNull
-    public abstract ListenableFuture<RangeResult<T>> loadRange(@NonNull LoadRangeParams params);
-
-    @Nullable
-    @Override
-    final Integer getKey(@NonNull T item) {
-        return null;
-    }
-
-    /**
-     * Type produced by {@link #loadInitial(LoadInitialParams)} to represent
-     * initially loaded data.
-     *
-     * @param <V> The type of the data loaded.
-     */
-    public static class InitialResult<V> extends BaseResult<V> {
-        public InitialResult(@NonNull List<V> data, int position, int totalCount) {
-            super(data, null, null, position, totalCount - data.size() - position, 0, true);
-            if (data.isEmpty() && position != 0) {
-                throw new IllegalArgumentException(
-                        "Initial result cannot be empty if items are present in data set.");
-            }
-        }
-
-        public InitialResult(@NonNull List<V> data, int position) {
-            super(data, null, null, 0, 0, position, false);
-            if (data.isEmpty() && position != 0) {
-                throw new IllegalArgumentException(
-                        "Initial result cannot be empty if items are present in data set.");
-            }
-        }
-    }
-
-    /**
-     * Type produced by {@link #loadRange(LoadRangeParams)} to represent a page
-     * of loaded data.
-     *
-     * @param <V> The type of the data loaded.
-     */
-    public static class RangeResult<V> extends BaseResult<V> {
-        public RangeResult(@NonNull List<V> data) {
-            super(data, null, null, 0, 0, 0, false);
-        }
-    }
-
-    /**
-     * Helper for computing an initial position in
-     * {@link #loadInitial(LoadInitialParams)} when total data set size can be
-     * computed ahead of loading.
-     * <p>
-     * The value computed by this function will do bounds checking, page alignment, and positioning
-     * based on initial load size requested.
-     * <p>
-     * Example usage in a PositionalDataSource subclass:
-     * <pre>
-     * class ItemDataSource extends PositionalDataSource&lt;Item> {
-     *     private int computeCount() {
-     *         // actual count code here
-     *     }
-     *
-     *     private List&lt;Item> loadRangeInternal(int startPosition, int loadCount) {
-     *         // actual load code here
-     *     }
-     *
-     *     {@literal @}Override
-     *     public void loadInitial({@literal @}NonNull LoadInitialParams params,
-     *             {@literal @}NonNull LoadInitialCallback&lt;Item> callback) {
-     *         int totalCount = computeCount();
-     *         int position = computeInitialLoadPosition(params, totalCount);
-     *         int loadSize = computeInitialLoadSize(params, position, totalCount);
-     *         callback.onResult(loadRangeInternal(position, loadSize), position, totalCount);
-     *     }
-     *
-     *     {@literal @}Override
-     *     public void loadRange({@literal @}NonNull LoadRangeParams params,
-     *             {@literal @}NonNull LoadRangeCallback&lt;Item> callback) {
-     *         callback.onResult(loadRangeInternal(params.startPosition, params.loadSize));
-     *     }
-     * }</pre>
-     *
-     * @param params Params passed to {@link #loadInitial(LoadInitialParams)},
-     *               including page size, and requested start/loadSize.
-     * @param totalCount Total size of the data set.
-     * @return Position to start loading at.
-     *
-     *
-     * @see #computeInitialLoadSize(ListenablePositionalDataSource.LoadInitialParams, int, int)
-     */
-    public static int computeInitialLoadPosition(
-            @NonNull ListenablePositionalDataSource.LoadInitialParams params,
-            int totalCount) {
-        int position = params.requestedStartPosition;
-        int initialLoadSize = params.requestedLoadSize;
-        int pageSize = params.pageSize;
-
-        int pageStart = position / pageSize * pageSize;
-
-        // maximum start pos is that which will encompass end of list
-        int maximumLoadPage = ((totalCount - initialLoadSize + pageSize - 1) / pageSize) * pageSize;
-        pageStart = Math.min(maximumLoadPage, pageStart);
-
-        // minimum start position is 0
-        pageStart = Math.max(0, pageStart);
-
-        return pageStart;
-    }
-
-    /**
-     * Helper for computing an initial load size in
-     * {@link #loadInitial(LoadInitialParams)} when total data set size can be
-     * computed ahead of loading.
-     * <p>
-     * This function takes the requested load size, and bounds checks it against the value returned
-     * by
-     * {@link #computeInitialLoadPosition(ListenablePositionalDataSource.LoadInitialParams, int)}.
-     * <p>
-     * Example usage in a PositionalDataSource subclass:
-     * <pre>
-     * class ItemDataSource extends PositionalDataSource&lt;Item> {
-     *     private int computeCount() {
-     *         // actual count code here
-     *     }
-     *
-     *     private List&lt;Item> loadRangeInternal(int startPosition, int loadCount) {
-     *         // actual load code here
-     *     }
-     *
-     *     {@literal @}Override
-     *     public void loadInitial({@literal @}NonNull LoadInitialParams params,
-     *             {@literal @}NonNull LoadInitialCallback&lt;Item> callback) {
-     *         int totalCount = computeCount();
-     *         int position = computeInitialLoadPosition(params, totalCount);
-     *         int loadSize = computeInitialLoadSize(params, position, totalCount);
-     *         callback.onResult(loadRangeInternal(position, loadSize), position, totalCount);
-     *     }
-     *
-     *     {@literal @}Override
-     *     public void loadRange({@literal @}NonNull LoadRangeParams params,
-     *             {@literal @}NonNull LoadRangeCallback&lt;Item> callback) {
-     *         callback.onResult(loadRangeInternal(params.startPosition, params.loadSize));
-     *     }
-     * }</pre>
-     *
-     * @param params Params passed to {@link #loadInitial(LoadInitialParams)},
-     *               including page size, and requested start/loadSize.
-     * @param initialLoadPosition Value returned by
-     *   {@link #computeInitialLoadPosition(ListenablePositionalDataSource.LoadInitialParams, int)}
-     * @param totalCount Total size of the data set.
-     * @return Number of items to load.
-     *
-     * @see #computeInitialLoadPosition(ListenablePositionalDataSource.LoadInitialParams, int)
-     */
-    public static int computeInitialLoadSize(@NonNull
-            ListenablePositionalDataSource.LoadInitialParams params,
-            int initialLoadPosition, int totalCount) {
-        return Math.min(totalCount - initialLoadPosition, params.requestedLoadSize);
-    }
-}
diff --git a/paging/common/src/main/java/androidx/paging/PageKeyedDataSource.java b/paging/common/src/main/java/androidx/paging/PageKeyedDataSource.java
deleted file mode 100644
index 65f9c0d..0000000
--- a/paging/common/src/main/java/androidx/paging/PageKeyedDataSource.java
+++ /dev/null
@@ -1,360 +0,0 @@
-/*
- * 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.util.Function;
-import androidx.concurrent.futures.ResolvableFuture;
-
-import com.google.common.util.concurrent.ListenableFuture;
-
-import java.util.List;
-
-/**
- * Incremental data loader for page-keyed content, where requests return keys for next/previous
- * pages.
- * <p>
- * Implement a DataSource using PageKeyedDataSource if you need to use data from page {@code N - 1}
- * to load page {@code N}. This is common, for example, in network APIs that include a next/previous
- * link or key with each page load.
- * <p>
- * The {@code InMemoryByPageRepository} in the
- * <a href="https://github.com/googlesamples/android-architecture-components/blob/master/PagingWithNetworkSample/README.md">PagingWithNetworkSample</a>
- * shows how to implement a network PageKeyedDataSource using
- * <a href="https://square.github.io/retrofit/">Retrofit</a>, while
- * handling swipe-to-refresh, network errors, and retry.
- *
- * @param <Key> Type of data used to query Value types out of the DataSource.
- * @param <Value> Type of items being loaded by the DataSource.
- */
-public abstract class PageKeyedDataSource<Key, Value>
-        extends ListenablePageKeyedDataSource<Key, Value> {
-    /**
-     * Holder object for inputs to {@link #loadInitial(LoadInitialParams, LoadInitialCallback)}.
-     *
-     * @param <Key> Type of data used to query pages.
-     */
-    public static class LoadInitialParams<Key> extends
-            ListenablePageKeyedDataSource.LoadInitialParams<Key> {
-        public LoadInitialParams(int requestedLoadSize, boolean placeholdersEnabled) {
-            super(requestedLoadSize, placeholdersEnabled);
-        }
-    }
-
-    /**
-     * Holder object for inputs to {@link #loadBefore(LoadParams, LoadCallback)} and
-     * {@link #loadAfter(LoadParams, LoadCallback)}.
-     *
-     * @param <Key> Type of data used to query pages.
-     */
-    public static class LoadParams<Key> extends ListenablePageKeyedDataSource.LoadParams<Key> {
-        public LoadParams(@NonNull Key key, int requestedLoadSize) {
-            super(key, requestedLoadSize);
-        }
-    }
-
-    /**
-     * Callback for {@link #loadInitial(LoadInitialParams, LoadInitialCallback)}
-     * to return data and, optionally, position/count information.
-     * <p>
-     * A callback can be called only once, and will throw if called again.
-     * <p>
-     * If you can compute the number of items in the data set before and after the loaded range,
-     * call the five parameter {@link #onResult(List, int, int, Key, Key)} to pass that
-     * information. You can skip passing this information by calling the three parameter
-     * {@link #onResult(List, Key, Key)}, either if it's difficult to compute, or if
-     * {@link LoadInitialParams#placeholdersEnabled} is {@code false}, so the positioning
-     * information will be ignored.
-     * <p>
-     * It is always valid for a DataSource loading method that takes a callback to stash the
-     * callback and call it later. This enables DataSources to be fully asynchronous, and to handle
-     * temporary, recoverable error states (such as a network error that can be retried).
-     *
-     * @param <Key> Type of data used to query pages.
-     * @param <Value> Type of items being loaded.
-     */
-    public abstract static class LoadInitialCallback<Key, Value> {
-        /**
-         * Called to pass initial load state from a DataSource.
-         * <p>
-         * Call this method from your DataSource's {@code loadInitial} function to return data,
-         * and inform how many placeholders should be shown before and after. If counting is cheap
-         * to compute (for example, if a network load returns the information regardless), it's
-         * recommended to pass data back through this method.
-         * <p>
-         * It is always valid to pass a different amount of data than what is requested. Pass an
-         * empty list if there is no more data to load.
-         *
-         * @param data List of items loaded from the DataSource. If this is empty, the DataSource
-         *             is treated as empty, and no further loads will occur.
-         * @param position Position of the item at the front of the list. If there are {@code N}
-         *                 items before the items in data that can be loaded from this DataSource,
-         *                 pass {@code N}.
-         * @param totalCount Total number of items that may be returned from this DataSource.
-         *                   Includes the number in the initial {@code data} parameter
-         *                   as well as any items that can be loaded in front or behind of
-         *                   {@code data}.
-         */
-        public abstract void onResult(@NonNull List<Value> data, int position, int totalCount,
-                @Nullable Key previousPageKey, @Nullable Key nextPageKey);
-
-        /**
-         * Called to pass loaded data from a DataSource.
-         * <p>
-         * Call this from {@link #loadInitial(LoadInitialParams, LoadInitialCallback)} to
-         * initialize without counting available data, or supporting placeholders.
-         * <p>
-         * It is always valid to pass a different amount of data than what is requested. Pass an
-         * empty list if there is no more data to load.
-         *
-         * @param data List of items loaded from the PageKeyedDataSource.
-         * @param previousPageKey Key for page before the initial load result, or {@code null} if no
-         *                        more data can be loaded before.
-         * @param nextPageKey Key for page after the initial load result, or {@code null} if no
-         *                        more data can be loaded after.
-         */
-        public abstract void onResult(@NonNull List<Value> data, @Nullable Key previousPageKey,
-                @Nullable Key nextPageKey);
-
-        /**
-         * Called to report an error from a DataSource.
-         * <p>
-         * Call this method to report an error from
-         * {@link #loadInitial(LoadInitialParams, LoadInitialCallback)}.
-         *
-         * @param error The error that occurred during loading.
-         */
-        public void onError(@NonNull Throwable error) {
-            // TODO: remove default implementation in 3.0
-            throw new IllegalStateException(
-                    "You must implement onError if implementing your own load callback");
-        }
-    }
-
-    /**
-     * Callback for PageKeyedDataSource {@link #loadBefore(LoadParams, LoadCallback)} and
-     * {@link #loadAfter(LoadParams, LoadCallback)} to return data.
-     * <p>
-     * A callback can be called only once, and will throw if called again.
-     * <p>
-     * It is always valid for a DataSource loading method that takes a callback to stash the
-     * callback and call it later. This enables DataSources to be fully asynchronous, and to handle
-     * temporary, recoverable error states (such as a network error that can be retried).
-     *
-     * @param <Key> Type of data used to query pages.
-     * @param <Value> Type of items being loaded.
-     */
-    public abstract static class LoadCallback<Key, Value> {
-        /**
-         * Called to pass loaded data from a DataSource.
-         * <p>
-         * Call this method from your PageKeyedDataSource's
-         * {@link #loadBefore(LoadParams, LoadCallback)} and
-         * {@link #loadAfter(LoadParams, LoadCallback)} methods to return data.
-         * <p>
-         * It is always valid to pass a different amount of data than what is requested. Pass an
-         * empty list if there is no more data to load.
-         * <p>
-         * Pass the key for the subsequent page to load to adjacentPageKey. For example, if you've
-         * loaded a page in {@link #loadBefore(LoadParams, LoadCallback)}, pass the key for the
-         * previous page, or {@code null} if the loaded page is the first. If in
-         * {@link #loadAfter(LoadParams, LoadCallback)}, pass the key for the next page, or
-         * {@code null} if the loaded page is the last.
-         *
-         * @param data List of items loaded from the PageKeyedDataSource.
-         * @param adjacentPageKey Key for subsequent page load (previous page in {@link #loadBefore}
-         *                        / next page in {@link #loadAfter}), or {@code null} if there are
-         *                        no more pages to load in the current load direction.
-         */
-        public abstract void onResult(@NonNull List<Value> data, @Nullable Key adjacentPageKey);
-
-        /**
-         * Called to report an error from a DataSource.
-         * <p>
-         * Call this method to report an error from your PageKeyedDataSource's
-         * {@link #loadBefore(LoadParams, LoadCallback)} and
-         * {@link #loadAfter(LoadParams, LoadCallback)} methods.
-         *
-         * @param error The error that occurred during loading.
-         */
-        public void onError(@NonNull Throwable error) {
-            // TODO: remove default implementation in 3.0
-            throw new IllegalStateException(
-                    "You must implement onError if implementing your own load callback");
-        }
-    }
-
-    @NonNull
-    @Override
-    public final ListenableFuture<InitialResult<Key, Value>> loadInitial(
-            final @NonNull ListenablePageKeyedDataSource.LoadInitialParams<Key> params) {
-        final ResolvableFuture<InitialResult<Key, Value>> future = ResolvableFuture.create();
-        getExecutor().execute(new Runnable() {
-            @Override
-            public void run() {
-                LoadInitialCallback<Key, Value> callback = new LoadInitialCallback<Key, Value>() {
-                    @Override
-                    public void onResult(@NonNull List<Value> data, int position, int totalCount,
-                            @Nullable Key previousPageKey, @Nullable Key nextPageKey) {
-                        future.set(new InitialResult<>(data, position, totalCount, previousPageKey,
-                                nextPageKey));
-                    }
-
-                    @Override
-                    public void onResult(@NonNull List<Value> data, @Nullable Key previousPageKey,
-                            @Nullable Key nextPageKey) {
-                        future.set(new InitialResult<>(data, previousPageKey, nextPageKey));
-                    }
-
-                    @Override
-                    public void onError(@NonNull Throwable error) {
-                        future.setException(error);
-                    }
-                };
-                loadInitial(new LoadInitialParams<Key>(
-                                params.requestedLoadSize,
-                                params.placeholdersEnabled),
-                        callback);
-            }
-        });
-        return future;
-    }
-
-    @SuppressWarnings("WeakerAccess")
-    LoadCallback<Key, Value> getFutureAsCallback(
-            final @NonNull ResolvableFuture<Result<Key, Value>> future) {
-        return new LoadCallback<Key, Value>() {
-            @Override
-            public void onResult(@NonNull List<Value> data, @Nullable Key adjacentPageKey) {
-                future.set(new Result<>(data, adjacentPageKey));
-            }
-
-            @Override
-            public void onError(@NonNull Throwable error) {
-                future.setException(error);
-            }
-        };
-    }
-
-    @NonNull
-    @Override
-    public final ListenableFuture<Result<Key, Value>> loadBefore(
-            final @NonNull ListenablePageKeyedDataSource.LoadParams<Key> params) {
-        final ResolvableFuture<Result<Key, Value>> future = ResolvableFuture.create();
-        getExecutor().execute(new Runnable() {
-            @Override
-            public void run() {
-                loadBefore(new LoadParams<>(
-                                params.key,
-                                params.requestedLoadSize),
-                        getFutureAsCallback(future));
-            }
-        });
-        return future;
-    }
-
-    @NonNull
-    @Override
-    public final ListenableFuture<Result<Key, Value>> loadAfter(
-            final @NonNull ListenablePageKeyedDataSource.LoadParams<Key> params) {
-        final ResolvableFuture<Result<Key, Value>> future = ResolvableFuture.create();
-        getExecutor().execute(new Runnable() {
-            @Override
-            public void run() {
-                loadAfter(new LoadParams<>(
-                        params.key,
-                        params.requestedLoadSize), getFutureAsCallback(future));
-            }
-        });
-        return future;
-    }
-
-    /**
-     * Load initial data.
-     * <p>
-     * This method is called first to initialize a PagedList with data. If it's possible to count
-     * the items that can be loaded by the DataSource, it's recommended to pass the loaded data to
-     * the callback via the three-parameter
-     * {@link LoadInitialCallback#onResult(List, int, int, Key, Key)}. This enables PagedLists
-     * presenting data from this source to display placeholders to represent unloaded items.
-     * <p>
-     * {@link LoadInitialParams#requestedLoadSize} is a hint, not a requirement, so it may be may be
-     * altered or ignored.
-     *
-     * @param params Parameters for initial load, including requested load size.
-     * @param callback Callback that receives initial load data.
-     */
-    public abstract void loadInitial(@NonNull LoadInitialParams<Key> params,
-            @NonNull LoadInitialCallback<Key, Value> callback);
-
-    /**
-     * Prepend page with the key specified by {@link LoadParams#key LoadParams.key}.
-     * <p>
-     * It's valid to return a different list size than the page size if it's easier, e.g. if your
-     * backend defines page sizes. It is generally preferred to increase the number loaded than
-     * reduce.
-     * <p>
-     * Data may be passed synchronously during the load method, or deferred and called at a
-     * later time. Further loads going down will be blocked until the callback is called.
-     * <p>
-     * If data cannot be loaded (for example, if the request is invalid, or the data would be stale
-     * and inconsistent), it is valid to call {@link #invalidate()} to invalidate the data source,
-     * and prevent further loading.
-     *
-     * @param params Parameters for the load, including the key for the new page, and requested load
-     *               size.
-     * @param callback Callback that receives loaded data.
-     */
-    public abstract void loadBefore(@NonNull LoadParams<Key> params,
-            @NonNull LoadCallback<Key, Value> callback);
-
-    /**
-     * Append page with the key specified by {@link LoadParams#key LoadParams.key}.
-     * <p>
-     * It's valid to return a different list size than the page size if it's easier, e.g. if your
-     * backend defines page sizes. It is generally preferred to increase the number loaded than
-     * reduce.
-     * <p>
-     * Data may be passed synchronously during the load method, or deferred and called at a
-     * later time. Further loads going down will be blocked until the callback is called.
-     * <p>
-     * If data cannot be loaded (for example, if the request is invalid, or the data would be stale
-     * and inconsistent), it is valid to call {@link #invalidate()} to invalidate the data source,
-     * and prevent further loading.
-     *
-     * @param params Parameters for the load, including the key for the new page, and requested load
-     *               size.
-     * @param callback Callback that receives loaded data.
-     */
-    public abstract void loadAfter(@NonNull LoadParams<Key> params,
-            @NonNull LoadCallback<Key, Value> callback);
-
-    @NonNull
-    @Override
-    public final <ToValue> PageKeyedDataSource<Key, ToValue> mapByPage(
-            @NonNull Function<List<Value>, List<ToValue>> function) {
-        return new WrapperPageKeyedDataSource<>(this, function);
-    }
-
-    @NonNull
-    @Override
-    public final <ToValue> PageKeyedDataSource<Key, ToValue> map(
-            @NonNull Function<Value, ToValue> function) {
-        return mapByPage(createListFunction(function));
-    }
-}
diff --git a/paging/common/src/main/java/androidx/paging/PagedList.java b/paging/common/src/main/java/androidx/paging/PagedList.java
deleted file mode 100644
index 2339f2b..0000000
--- a/paging/common/src/main/java/androidx/paging/PagedList.java
+++ /dev/null
@@ -1,1443 +0,0 @@
-/*
- * 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.AnyThread;
-import androidx.annotation.IntRange;
-import androidx.annotation.MainThread;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-import androidx.annotation.WorkerThread;
-import androidx.arch.core.util.Function;
-import androidx.paging.futures.DirectExecutor;
-import androidx.paging.futures.Futures;
-
-import com.google.common.util.concurrent.ListenableFuture;
-
-import java.lang.ref.WeakReference;
-import java.util.AbstractList;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Executor;
-
-/**
- * Lazy loading list that pages in immutable content from a {@link DataSource}.
- * <p>
- * A PagedList is a {@link List} which loads its data in chunks (pages) from a {@link DataSource}.
- * Items can be accessed with {@link #get(int)}, and further loading can be triggered with
- * {@link #loadAround(int)}. To display a PagedList, see {@link androidx.paging.PagedListAdapter}, which enables the
- * binding of a PagedList to a {@link androidx.recyclerview.widget.RecyclerView}.
- * <h4>Loading Data</h4>
- * <p>
- * All data in a PagedList is loaded from its {@link DataSource}. Creating a PagedList loads the
- * first chunk of data from the DataSource immediately, and should for this reason be done on a
- * background thread. The constructed PagedList may then be passed to and used on the UI thread.
- * This is done to prevent passing a list with no loaded content to the UI thread, which should
- * generally not be presented to the user.
- * <p>
- * A PagedList initially presents this first partial load as its content, and expands over time as
- * content is loaded in. When {@link #loadAround} is called, items will be loaded in near the passed
- * list index. If placeholder {@code null}s are present in the list, they will be replaced as
- * content is loaded. If not, newly loaded items will be inserted at the beginning or end of the
- * list.
- * <p>
- * PagedList can present data for an unbounded, infinite scrolling list, or a very large but
- * countable list. Use {@link Config} to control how many items a PagedList loads, and when.
- * <p>
- * If you use {@link androidx.paging.LivePagedListBuilder} to get a
- * {@link androidx.lifecycle.LiveData}, it will initialize PagedLists on a
- * background thread for you.
- * <h4>Placeholders</h4>
- * <p>
- * There are two ways that PagedList can represent its not-yet-loaded data - with or without
- * {@code null} placeholders.
- * <p>
- * With placeholders, the PagedList is always the full size of the data set. {@code get(N)} returns
- * the {@code N}th item in the data set, or {@code null} if its not yet loaded.
- * <p>
- * Without {@code null} placeholders, the PagedList is the sublist of data that has already been
- * loaded. The size of the PagedList is the number of currently loaded items, and {@code get(N)}
- * returns the {@code N}th <em>loaded</em> item. This is not necessarily the {@code N}th item in the
- * data set.
- * <p>
- * Placeholders have several benefits:
- * <ul>
- *     <li>They express the full sized list to the presentation layer (often a
- *     {@link androidx.paging.PagedListAdapter}), and so can support scrollbars (without jumping as pages are
- *     loaded or dropped) and fast-scrolling to any position, loaded or not.
- *     <li>They avoid the need for a loading spinner at the end of the loaded list, since the list
- *     is always full sized.
- * </ul>
- * <p>
- * They also have drawbacks:
- * <ul>
- *     <li>Your Adapter needs to account for {@code null} items. This often means providing default
- *     values in data you bind to a {@link androidx.recyclerview.widget.RecyclerView.ViewHolder}.
- *     <li>They don't work well if your item views are of different sizes, as this will prevent
- *     loading items from cross-fading nicely.
- *     <li>They require you to count your data set, which can be expensive or impossible, depending
- *     on your DataSource.
- * </ul>
- * <p>
- * Placeholders are enabled by default, but can be disabled in two ways. They are disabled if the
- * DataSource does not count its data set in its initial load, or if  {@code false} is passed to
- * {@link Config.Builder#setEnablePlaceholders(boolean)} when building a {@link Config}.
- * <h4>Mutability and Snapshots</h4>
- * A PagedList is <em>mutable</em> while loading, or ready to load from its DataSource.
- * As loads succeed, a mutable PagedList will be updated via Runnables on the main thread. You can
- * listen to these updates with a {@link Callback}. (Note that {@link androidx.paging.PagedListAdapter} will listen
- * to these to signal RecyclerView about the updates/changes).
- * <p>
- * If a PagedList attempts to load from an invalid DataSource, it will {@link #detach()}
- * from the DataSource, meaning that it will no longer attempt to load data. It will return true
- * from {@link #isImmutable()}, and a new DataSource / PagedList pair must be created to load
- * further data. See {@link DataSource} and {@link androidx.paging.LivePagedListBuilder} for how new PagedLists are
- * created to represent changed data.
- * <p>
- * A PagedList snapshot is simply an immutable shallow copy of the current state of the PagedList as
- * a {@code List}. It will reference the same inner items, and contain the same {@code null}
- * placeholders, if present.
- *
- * @param <T> The type of the entries in the list.
- */
-public abstract class PagedList<T> extends AbstractList<T> {
-
-    /**
-     * Type of load a PagedList can perform.
-     * <p>
-     * You can use a {@link LoadStateListener} to observe {@link LoadState} of
-     * any {@link LoadType}. For UI purposes (swipe refresh, loading spinner, retry button), this
-     * is typically done by registering a Listener with the {@code PagedListAdapter} or
-     * {@code AsyncPagedListDiffer}.
-     *
-     * @see LoadState
-     */
-    public enum LoadType {
-        /**
-         * PagedList content being reloaded, may contain content updates.
-         */
-        REFRESH,
-
-        /**
-         * Load at the start of the PagedList.
-         */
-        START,
-
-        /**
-         * Load at the end of the PagedList.
-         */
-        END
-    }
-
-    /**
-     * State of a PagedList load - associated with a {@code LoadType}
-     * <p>
-     * You can use a {@link LoadStateListener} to observe {@link LoadState} of
-     * any {@link LoadType}. For UI purposes (swipe refresh, loading spinner, retry button), this
-     * is typically done by registering a Listener with the {@code PagedListAdapter} or
-     * {@code AsyncPagedListDiffer}.
-     */
-    public enum LoadState {
-        /**
-         * Indicates the PagedList is not currently loading, and no error currently observed.
-         */
-        IDLE,
-
-        /**
-         * Loading is in progress.
-         */
-        LOADING,
-
-        /**
-         * Loading is complete.
-         */
-        DONE,
-
-        /**
-         * Loading hit a non-retryable error.
-         */
-        ERROR,
-
-        /**
-         * Loading hit a retryable error.
-         *
-         * @see #retry()
-         */
-        RETRYABLE_ERROR,
-    }
-
-    /**
-     * Listener for changes to loading state - whether the refresh, prepend, or append is idle,
-     * loading, or has an error.
-     * <p>
-     * Can be used to observe the {@link LoadState} of any {@link LoadType} (REFRESH/START/END).
-     * For UI purposes (swipe refresh, loading spinner, retry button), this is typically done by
-     * registering a Listener with the {@code PagedListAdapter} or {@code AsyncPagedListDiffer}.
-     * <p>
-     * These calls will be dispatched on the executor defined by
-     * {@link Builder#setNotifyExecutor(Executor)}, which is generally the main/UI thread.
-     *
-     * @see LoadType
-     * @see LoadState
-     */
-    public interface LoadStateListener {
-        /**
-         * Called when the LoadState has changed - whether the refresh, prepend, or append is
-         * idle, loading, or has an error.
-         * <p>
-         * REFRESH events can be used to drive a {@code SwipeRefreshLayout}, or START/END events
-         * can be used to drive loading spinner items in your {@code RecyclerView}.
-         *
-         * @param type Type of load - START, END, or REFRESH.
-         * @param state State of load - IDLE, LOADING, DONE, ERROR, or RETRYABLE_ERROR
-         * @param error Error, if in an error state, null otherwise.
-         *
-         * @see #retry()
-         */
-        void onLoadStateChanged(@NonNull LoadType type,
-                @NonNull LoadState state, @Nullable Throwable error);
-    }
-
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
-    static boolean equalsHelper(@Nullable Object a, @Nullable Object b) {
-        // (Because Objects.equals() is API 19+)
-        //noinspection EqualsReplaceableByObjectsCall
-        return a == b || (a != null && a.equals(b));
-    }
-
-    abstract static class LoadStateManager {
-        @NonNull
-        private LoadState mRefresh = LoadState.IDLE;
-        @Nullable
-        private Throwable mRefreshError = null;
-        @NonNull
-        private LoadState mStart = LoadState.IDLE;
-        @Nullable
-        private Throwable mStartError = null;
-        @NonNull
-        private LoadState mEnd = LoadState.IDLE;
-        @Nullable
-        private Throwable mEndError = null;
-
-        @NonNull
-        public LoadState getRefresh() {
-            return mRefresh;
-        }
-
-        @NonNull
-        public LoadState getStart() {
-            return mStart;
-        }
-
-        @NonNull
-        public LoadState getEnd() {
-            return mEnd;
-        }
-
-        void setState(@NonNull LoadType type, @NonNull LoadState state, @Nullable Throwable error) {
-            boolean expectError = state == LoadState.RETRYABLE_ERROR || state == LoadState.ERROR;
-            boolean hasError = error != null;
-            if (expectError != hasError) {
-                throw new IllegalArgumentException(
-                        "Error states must be accompanied by a throwable, other states must not");
-            }
-
-            // deduplicate signals
-            switch (type) {
-                case REFRESH:
-                    if (mRefresh.equals(state) && equalsHelper(mRefreshError, error)) return;
-                    mRefresh = state;
-                    mRefreshError = error;
-                    break;
-                case START:
-                    if (mStart.equals(state) && equalsHelper(mStartError, error)) return;
-                    mStart = state;
-                    mStartError = error;
-                    break;
-                case END:
-                    if (mEnd.equals(state) && equalsHelper(mEndError, error)) return;
-                    mEnd = state;
-                    mEndError = error;
-                    break;
-            }
-            onStateChanged(type, state, error);
-        }
-
-        protected abstract void onStateChanged(@NonNull LoadType type,
-                @NonNull LoadState state, @Nullable Throwable error);
-
-        void dispatchCurrentLoadState(LoadStateListener listener) {
-            listener.onLoadStateChanged(PagedList.LoadType.REFRESH, mRefresh, mRefreshError);
-            listener.onLoadStateChanged(PagedList.LoadType.START, mStart, mStartError);
-            listener.onLoadStateChanged(PagedList.LoadType.END, mEnd, mEndError);
-        }
-    }
-
-    void setInitialLoadState(@NonNull LoadState loadState, @Nullable Throwable error) {}
-
-    /**
-     * Retry any retryable errors associated with this PagedList.
-     * <p>
-     * If for example a network DataSource append timed out, calling this method will retry the
-     * failed append load. Note that your DataSource will need to pass {@code true} to
-     * {@code onError()} to signify the error as retryable.
-     * <p>
-     * You can observe loading state via {@link #addWeakLoadStateListener(LoadStateListener)},
-     * though generally this is done through the {@link androidx.paging.PagedListAdapter} or
-     * {@link androidx.paging.AsyncPagedListDiffer}.
-     *
-     * @see #addWeakLoadStateListener(LoadStateListener)
-     * @see #removeWeakLoadStateListener(LoadStateListener)
-     */
-    public void retry() {}
-
-    @NonNull
-    final Executor mMainThreadExecutor;
-    @NonNull
-    final Executor mBackgroundThreadExecutor;
-    @Nullable
-    final BoundaryCallback<T> mBoundaryCallback;
-    @NonNull
-    final Config mConfig;
-    @NonNull
-    final PagedStorage<T> mStorage;
-    @Nullable
-    Runnable mRefreshRetryCallback = null;
-
-    void setRetryCallback(@Nullable Runnable refreshRetryCallback) {
-        mRefreshRetryCallback = refreshRetryCallback;
-    }
-
-    /**
-     * Last access location, in total position space (including offset).
-     * <p>
-     * Used by positional data
-     * sources to initialize loading near viewport
-     */
-    int mLastLoad = 0;
-    T mLastItem = null;
-
-    final int mRequiredRemainder;
-
-    // if set to true, mBoundaryCallback is non-null, and should
-    // be dispatched when nearby load has occurred
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
-    boolean mBoundaryCallbackBeginDeferred = false;
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
-    boolean mBoundaryCallbackEndDeferred = false;
-
-    // lowest and highest index accessed by loadAround. Used to
-    // decide when mBoundaryCallback should be dispatched
-    private int mLowestIndexAccessed = Integer.MAX_VALUE;
-    private int mHighestIndexAccessed = Integer.MIN_VALUE;
-
-    private final ArrayList<WeakReference<Callback>> mCallbacks = new ArrayList<>();
-
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
-    final ArrayList<WeakReference<LoadStateListener>> mListeners = new ArrayList<>();
-
-    void dispatchStateChange(@NonNull LoadType type, @NonNull LoadState state,
-            @Nullable Throwable error) {
-        for (int i = mListeners.size() - 1; i >= 0; i--) {
-            final LoadStateListener currentListener = mListeners.get(i).get();
-            if (currentListener == null) {
-                mListeners.remove(i);
-            } else {
-                currentListener.onLoadStateChanged(type, state, error);
-            }
-        }
-    }
-
-    PagedList(@NonNull PagedStorage<T> storage,
-            @NonNull Executor mainThreadExecutor,
-            @NonNull Executor backgroundThreadExecutor,
-            @Nullable BoundaryCallback<T> boundaryCallback,
-            @NonNull Config config) {
-        mStorage = storage;
-        mMainThreadExecutor = mainThreadExecutor;
-        mBackgroundThreadExecutor = backgroundThreadExecutor;
-        mBoundaryCallback = boundaryCallback;
-        mConfig = config;
-        mRequiredRemainder = mConfig.prefetchDistance * 2 + mConfig.pageSize;
-    }
-
-    /**
-     * Create a PagedList which loads data from the provided data source on a background thread,
-     * posting updates to the main thread.
-     *
-     *
-     * @param dataSource DataSource providing data to the PagedList
-     * @param notifyExecutor Thread tat will use and consume data from the PagedList.
-     *                       Generally, this is the UI/main thread.
-     * @param fetchExecutor Data loading will be done via this executor -
-     *                      should be a background thread.
-     * @param boundaryCallback Optional boundary callback to attach to the list.
-     * @param config PagedList Config, which defines how the PagedList will load data.
-     * @param <K> Key type that indicates to the DataSource what data to load.
-     * @param <T> Type of items to be held and loaded by the PagedList.
-     *
-     * @return ListenableFuture for newly created PagedList, which will page in data from the
-     * DataSource as needed.
-     */
-    @NonNull
-    static <K, T> ListenableFuture<PagedList<T>> create(
-            @NonNull final DataSource<K, T> dataSource,
-            @NonNull final Executor notifyExecutor,
-            @NonNull final Executor fetchExecutor,
-            @NonNull final Executor initialLoadExecutor,
-            @Nullable final BoundaryCallback<T> boundaryCallback,
-            @NonNull final Config config,
-            @Nullable K key) {
-        dataSource.initExecutor(initialLoadExecutor);
-
-        final int lastLoad = (dataSource.mType == DataSource.KeyType.POSITIONAL && key != null)
-                ? (Integer) key : ContiguousPagedList.LAST_LOAD_UNSPECIFIED;
-
-        return Futures.transform(
-            dataSource.load(
-                new DataSource.Params<>(
-                        DataSource.LoadType.INITIAL,
-                        key,
-                        config.initialLoadSizeHint,
-                        config.enablePlaceholders,
-                        config.pageSize)),
-                new Function<DataSource.BaseResult<T>, PagedList<T>>() {
-                    @Override
-                    public PagedList<T> apply(DataSource.BaseResult<T> initialResult) {
-                        dataSource.initExecutor(fetchExecutor);
-                        return new ContiguousPagedList<>(dataSource,
-                                notifyExecutor,
-                                fetchExecutor,
-                                boundaryCallback,
-                                config,
-                                initialResult,
-                                lastLoad);
-                    }
-                },
-                DirectExecutor.INSTANCE);
-    }
-
-    /**
-     * Builder class for PagedList.
-     * <p>
-     * DataSource, Config, main thread and background executor must all be provided.
-     * <p>
-     * A PagedList queries initial data from its DataSource during construction, to avoid empty
-     * PagedLists being presented to the UI when possible. It's preferred to present initial data,
-     * so that the UI doesn't show an empty list, or placeholders for a few frames, just before
-     * showing initial content.
-     * <p>
-     * {@link androidx.paging.LivePagedListBuilder} does this creation on a background thread automatically, if you
-     * want to receive a {@code LiveData<PagedList<...>>}.
-     *
-     * @param <Key> Type of key used to load data from the DataSource.
-     * @param <Value> Type of items held and loaded by the PagedList.
-     */
-    @SuppressWarnings("WeakerAccess")
-    public static final class Builder<Key, Value> {
-        private final DataSource<Key, Value> mDataSource;
-        private final Config mConfig;
-        private Executor mNotifyExecutor;
-        private Executor mFetchExecutor;
-        private BoundaryCallback<Value> mBoundaryCallback;
-        private Key mInitialKey;
-
-        /**
-         * Create a PagedList.Builder with the provided {@link DataSource} and {@link Config}.
-         *
-         * @param dataSource DataSource the PagedList will load from.
-         * @param config Config that defines how the PagedList loads data from its DataSource.
-         */
-        public Builder(@NonNull DataSource<Key, Value> dataSource, @NonNull Config config) {
-            //noinspection ConstantConditions
-            if (dataSource == null) {
-                throw new IllegalArgumentException("DataSource may not be null");
-            }
-            //noinspection ConstantConditions
-            if (config == null) {
-                throw new IllegalArgumentException("Config may not be null");
-            }
-            mDataSource = dataSource;
-            mConfig = config;
-        }
-
-        /**
-         * Create a PagedList.Builder with the provided {@link DataSource} and page size.
-         * <p>
-         * This method is a convenience for:
-         * <pre>
-         * PagedList.Builder(dataSource,
-         *         new PagedList.Config.Builder().setPageSize(pageSize).build());
-         * </pre>
-         *
-         * @param dataSource DataSource the PagedList will load from.
-         * @param pageSize Config that defines how the PagedList loads data from its DataSource.
-         */
-        public Builder(@NonNull DataSource<Key, Value> dataSource, int pageSize) {
-            this(dataSource, new PagedList.Config.Builder().setPageSize(pageSize).build());
-        }
-        /**
-         * The executor defining where page loading updates are dispatched.
-         *
-         * @param notifyExecutor Executor that receives PagedList updates, and where
-         * {@link Callback} calls are dispatched. Generally, this is the ui/main thread.
-         * @return this
-         */
-        @NonNull
-        public Builder<Key, Value> setNotifyExecutor(@NonNull Executor notifyExecutor) {
-            mNotifyExecutor = notifyExecutor;
-            return this;
-        }
-
-        /**
-         * The executor used to fetch additional pages from the DataSource.
-         *
-         * Does not affect initial load, which will be done immediately on whichever thread the
-         * PagedList is created on.
-         *
-         * @param fetchExecutor Executor used to fetch from DataSources, generally a background
-         *                      thread pool for e.g. I/O or network loading.
-         * @return this
-         */
-        @NonNull
-        public Builder<Key, Value> setFetchExecutor(@NonNull Executor fetchExecutor) {
-            mFetchExecutor = fetchExecutor;
-            return this;
-        }
-
-        /**
-         * The BoundaryCallback for out of data events.
-         * <p>
-         * Pass a BoundaryCallback to listen to when the PagedList runs out of data to load.
-         *
-         * @param boundaryCallback BoundaryCallback for listening to out-of-data events.
-         * @return this
-         */
-        @SuppressWarnings("unused")
-        @NonNull
-        public Builder<Key, Value> setBoundaryCallback(
-                @Nullable BoundaryCallback<Value> boundaryCallback) {
-            mBoundaryCallback = boundaryCallback;
-            return this;
-        }
-
-        /**
-         * Sets the initial key the DataSource should load around as part of initialization.
-         *
-         * @param initialKey Key the DataSource should load around as part of initialization.
-         * @return this
-         */
-        @NonNull
-        public Builder<Key, Value> setInitialKey(@Nullable Key initialKey) {
-            mInitialKey = initialKey;
-            return this;
-        }
-
-        /**
-         * Creates a {@link PagedList} with the given parameters.
-         * <p>
-         * This call will dispatch the {@link androidx.paging.DataSource}'s loadInitial method immediately on the
-         * current thread, and block the current on the result. This method should always be called
-         * on a worker thread to prevent blocking the main thread.
-         * <p>
-         * It's fine to create a PagedList with an async DataSource on the main thread, such as in
-         * the constructor of a ViewModel. An async network load won't block the initialLoad
-         * function. For a synchronous DataSource such as one created from a Room database, a
-         * {@code LiveData<PagedList>} can be safely constructed with {@link androidx.paging.LivePagedListBuilder}
-         * on the main thread, since actual construction work is deferred, and done on a background
-         * thread.
-         * <p>
-         * While build() will always return a PagedList, it's important to note that the PagedList
-         * initial load may fail to acquire data from the DataSource. This can happen for example if
-         * the DataSource is invalidated during its initial load. If this happens, the PagedList
-         * will be immediately {@link PagedList#isDetached() detached}, and you can retry
-         * construction (including setting a new DataSource).
-         *
-         * @deprecated This method has no means of handling errors encountered during initial load,
-         * and blocks on the initial load result. Use {@link #buildAsync()} instead.
-         *
-         * @return The newly constructed PagedList
-         */
-        @Deprecated
-        @WorkerThread
-        @NonNull
-        public PagedList<Value> build() {
-            // TODO: define defaults, once they can be used in module without android dependency
-            if (mNotifyExecutor == null) {
-                throw new IllegalArgumentException("MainThreadExecutor required");
-            }
-            if (mFetchExecutor == null) {
-                throw new IllegalArgumentException("BackgroundThreadExecutor required");
-            }
-
-            try {
-                return create(DirectExecutor.INSTANCE).get();
-            } catch (InterruptedException e) {
-                throw new RuntimeException(e);
-            } catch (ExecutionException e) {
-                throw new RuntimeException(e);
-            }
-        }
-
-        /**
-         * Creates a {@link PagedList} asynchronously with the given parameters.
-         * <p>
-         * This call will dispatch the {@link DataSource}'s loadInitial method immediately, and
-         * return a {@code ListenableFuture<PagedList<T>>} that will resolve (triggering listeners)
-         * once the initial load is completed (success or failure).
-         *
-         * @return The newly constructed PagedList
-         */
-        @SuppressWarnings("unused")
-        @NonNull
-        public ListenableFuture<PagedList<Value>> buildAsync() {
-            // TODO: define defaults, once they can be used in module without android dependency
-            if (mNotifyExecutor == null) {
-                throw new IllegalArgumentException("MainThreadExecutor required");
-            }
-            if (mFetchExecutor == null) {
-                throw new IllegalArgumentException("BackgroundThreadExecutor required");
-            }
-
-            return create(mFetchExecutor);
-        }
-
-        private ListenableFuture<PagedList<Value>> create(@NonNull Executor initialFetchExecutor) {
-            return PagedList.create(
-                    mDataSource,
-                    mNotifyExecutor,
-                    mFetchExecutor,
-                    initialFetchExecutor,
-                    mBoundaryCallback,
-                    mConfig,
-                    mInitialKey);
-        }
-    }
-
-    /**
-     * Get the item in the list of loaded items at the provided index.
-     *
-     * @param index Index in the loaded item list. Must be >= 0, and &lt; {@link #size()}
-     * @return The item at the passed index, or null if a null placeholder is at the specified
-     *         position.
-     *
-     * @see #size()
-     */
-    @Override
-    @Nullable
-    public T get(int index) {
-        T item = mStorage.get(index);
-        if (item != null) {
-            mLastItem = item;
-        }
-        return item;
-    }
-
-    /**
-     * Load adjacent items to passed index.
-     *
-     * @param index Index at which to load.
-     */
-    public void loadAround(int index) {
-        if (index < 0 || index >= size()) {
-            throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size());
-        }
-
-        mLastLoad = index + getPositionOffset();
-        loadAroundInternal(index);
-
-        mLowestIndexAccessed = Math.min(mLowestIndexAccessed, index);
-        mHighestIndexAccessed = Math.max(mHighestIndexAccessed, index);
-
-        /*
-         * mLowestIndexAccessed / mHighestIndexAccessed have been updated, so check if we need to
-         * dispatch boundary callbacks. Boundary callbacks are deferred until last items are loaded,
-         * and accesses happen near the boundaries.
-         *
-         * Note: we post here, since RecyclerView may want to add items in response, and this
-         * call occurs in PagedListAdapter bind.
-         */
-        tryDispatchBoundaryCallbacks(true);
-    }
-
-    // Creation thread for initial synchronous load, otherwise main thread
-    // Safe to access main thread only state - no other thread has reference during construction
-    @AnyThread
-    void deferBoundaryCallbacks(final boolean deferEmpty,
-            final boolean deferBegin, final boolean deferEnd) {
-        if (mBoundaryCallback == null) {
-            throw new IllegalStateException("Can't defer BoundaryCallback, no instance");
-        }
-
-        /*
-         * If lowest/highest haven't been initialized, set them to storage size,
-         * since placeholders must already be computed by this point.
-         *
-         * This is just a minor optimization so that BoundaryCallback callbacks are sent immediately
-         * if the initial load size is smaller than the prefetch window (see
-         * TiledPagedListTest#boundaryCallback_immediate())
-         */
-        if (mLowestIndexAccessed == Integer.MAX_VALUE) {
-            mLowestIndexAccessed = mStorage.size();
-        }
-        if (mHighestIndexAccessed == Integer.MIN_VALUE) {
-            mHighestIndexAccessed = 0;
-        }
-
-        if (deferEmpty || deferBegin || deferEnd) {
-            // Post to the main thread, since we may be on creation thread currently
-            mMainThreadExecutor.execute(new Runnable() {
-                @Override
-                public void run() {
-                    // on is dispatched immediately, since items won't be accessed
-                    //noinspection ConstantConditions
-                    if (deferEmpty) {
-                        mBoundaryCallback.onZeroItemsLoaded();
-                    }
-
-                    // for other callbacks, mark deferred, and only dispatch if loadAround
-                    // has been called near to the position
-                    if (deferBegin) {
-                        mBoundaryCallbackBeginDeferred = true;
-                    }
-                    if (deferEnd) {
-                        mBoundaryCallbackEndDeferred = true;
-                    }
-                    tryDispatchBoundaryCallbacks(false);
-                }
-            });
-        }
-    }
-
-    /**
-     * Call this when mLowest/HighestIndexAccessed are changed, or
-     * mBoundaryCallbackBegin/EndDeferred is set.
-     */
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
-    void tryDispatchBoundaryCallbacks(boolean post) {
-        final boolean dispatchBegin = mBoundaryCallbackBeginDeferred
-                && mLowestIndexAccessed <= mConfig.prefetchDistance;
-        final boolean dispatchEnd = mBoundaryCallbackEndDeferred
-                && mHighestIndexAccessed >= size() - 1 - mConfig.prefetchDistance;
-
-        if (!dispatchBegin && !dispatchEnd) {
-            return;
-        }
-
-        if (dispatchBegin) {
-            mBoundaryCallbackBeginDeferred = false;
-        }
-        if (dispatchEnd) {
-            mBoundaryCallbackEndDeferred = false;
-        }
-        if (post) {
-            mMainThreadExecutor.execute(new Runnable() {
-                @Override
-                public void run() {
-                    dispatchBoundaryCallbacks(dispatchBegin, dispatchEnd);
-                }
-            });
-        } else {
-            dispatchBoundaryCallbacks(dispatchBegin, dispatchEnd);
-        }
-    }
-
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
-    void dispatchBoundaryCallbacks(boolean begin, boolean end) {
-        // safe to deref mBoundaryCallback here, since we only defer if mBoundaryCallback present
-        if (begin) {
-            //noinspection ConstantConditions
-            mBoundaryCallback.onItemAtFrontLoaded(mStorage.getFirstLoadedItem());
-        }
-        if (end) {
-            //noinspection ConstantConditions
-            mBoundaryCallback.onItemAtEndLoaded(mStorage.getLastLoadedItem());
-        }
-    }
-
-    /** @hide */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
-    void offsetAccessIndices(int offset) {
-        // update last loadAround index
-        mLastLoad += offset;
-
-        // update access range
-        mLowestIndexAccessed += offset;
-        mHighestIndexAccessed += offset;
-    }
-
-    /**
-     * Returns size of the list, including any not-yet-loaded null padding.
-     *
-     * To get the number of loaded items, not counting placeholders, use {@link #getLoadedCount()}.
-     *
-     * @return Current total size of the list, including placeholders.
-     *
-     * @see #getLoadedCount()
-     */
-    @Override
-    public int size() {
-        return mStorage.size();
-    }
-
-    /**
-     * Returns the number of items loaded in the PagedList.
-     *
-     * Unlike {@link #size()} this counts only loaded items, not placeholders.
-     * <p>
-     * If placeholders are {@link Config#enablePlaceholders disabled}, this method is equivalent to
-     * {@link #size()}.
-     *
-     * @return Number of items currently loaded, not counting placeholders.
-     *
-     * @see #size()
-     */
-    public int getLoadedCount() {
-        return mStorage.getLoadedCount();
-    }
-
-    /**
-     * Returns whether the list is immutable.
-     *
-     * Immutable lists may not become mutable again, and may safely be accessed from any thread.
-     * <p>
-     * In the future, this method may return true when a PagedList has completed loading from its
-     * DataSource. Currently, it is equivalent to {@link #isDetached()}.
-     *
-     * @return True if the PagedList is immutable.
-     */
-    @SuppressWarnings("WeakerAccess")
-    public boolean isImmutable() {
-        return isDetached();
-    }
-
-    /**
-     * Returns an immutable snapshot of the PagedList in its current state.
-     *
-     * If this PagedList {@link #isImmutable() is immutable} due to its DataSource being invalid, it
-     * will be returned.
-     *
-     * @return Immutable snapshot of PagedList data.
-     */
-    @SuppressWarnings("WeakerAccess")
-    @NonNull
-    public List<T> snapshot() {
-        if (isImmutable()) {
-            return this;
-        }
-        return new SnapshotPagedList<>(this);
-    }
-
-    abstract boolean isContiguous();
-
-    /**
-     * Return the Config used to construct this PagedList.
-     *
-     * @return the Config of this PagedList
-     */
-    @NonNull
-    public Config getConfig() {
-        return mConfig;
-    }
-
-    /**
-     * Return the DataSource that provides data to this PagedList.
-     *
-     * @return the DataSource of this PagedList.
-     */
-    @NonNull
-    public abstract DataSource<?, T> getDataSource();
-
-    /**
-     * Return the key for the position passed most recently to {@link #loadAround(int)}.
-     * <p>
-     * When a PagedList is invalidated, you can pass the key returned by this function to initialize
-     * the next PagedList. This ensures (depending on load times) that the next PagedList that
-     * arrives will have data that overlaps. If you use androidx.paging.LivePagedListBuilder, it will do
-     * this for you.
-     *
-     * @return Key of position most recently passed to {@link #loadAround(int)}.
-     */
-    @Nullable
-    public abstract Object getLastKey();
-
-    /**
-     * True if the PagedList has detached the DataSource it was loading from, and will no longer
-     * load new data.
-     * <p>
-     * A detached list is {@link #isImmutable() immutable}.
-     *
-     * @return True if the data source is detached.
-     */
-    public abstract boolean isDetached();
-
-    /**
-     * Detach the PagedList from its DataSource, and attempt to load no more data.
-     * <p>
-     * This is called automatically when a DataSource is observed to be invalid, which is a
-     * signal to stop loading. The PagedList will continue to present existing data, but will not
-     * initiate new loads.
-     */
-    @SuppressWarnings("unused")
-    public abstract void detach();
-
-    /**
-     * Position offset of the data in the list.
-     * <p>
-     * If data is supplied by a {@link PositionalDataSource}, the item returned from
-     * <code>get(i)</code> has a position of <code>i + getPositionOffset()</code>.
-     * <p>
-     * If the DataSource is a {@link ItemKeyedDataSource} or {@link PageKeyedDataSource}, it
-     * doesn't use positions, returns 0.
-     */
-    public int getPositionOffset() {
-        return mStorage.getPositionOffset();
-    }
-
-    /**
-     * Add a LoadStateListener to observe the loading state of the PagedList.
-     *
-     * @param listener Listener to receive updates.
-     *
-     * @see #removeWeakLoadStateListener(LoadStateListener)
-     */
-    public void addWeakLoadStateListener(@NonNull LoadStateListener listener) {
-        // first, clean up any empty weak refs
-        for (int i = mListeners.size() - 1; i >= 0; i--) {
-            final LoadStateListener currentListener = mListeners.get(i).get();
-            if (currentListener == null) {
-                mListeners.remove(i);
-            }
-        }
-
-        // then add the new one
-        mListeners.add(new WeakReference<>(listener));
-        dispatchCurrentLoadState(listener);
-    }
-
-    /**
-     * Remove a previously registered LoadStateListener.
-     *
-     * @param listener Previously registered listener.
-     * @see #addWeakLoadStateListener(LoadStateListener)
-     */
-    public void removeWeakLoadStateListener(@NonNull LoadStateListener listener) {
-        for (int i = mListeners.size() - 1; i >= 0; i--) {
-            final LoadStateListener currentListener = mListeners.get(i).get();
-            if (currentListener == null || currentListener == listener) {
-                // found Listener, or empty weak ref
-                mListeners.remove(i);
-            }
-        }
-    }
-
-    abstract void dispatchCurrentLoadState(LoadStateListener listener);
-
-    /**
-     * Adds a callback, and issues updates since the previousSnapshot was created.
-     * <p>
-     * If previousSnapshot is passed, the callback will also immediately be dispatched any
-     * differences between the previous snapshot, and the current state. For example, if the
-     * previousSnapshot was of 5 nulls, 10 items, 5 nulls, and the current state was 5 nulls,
-     * 12 items, 3 nulls, the callback would immediately receive a call of
-     * <code>onChanged(14, 2)</code>.
-     * <p>
-     * This allows an observer that's currently presenting a snapshot to catch up to the most recent
-     * version, including any changes that may have been made.
-     * <p>
-     * The callback is internally held as weak reference, so PagedList doesn't hold a strong
-     * reference to its observer, such as a {@link androidx.paging.PagedListAdapter}. If an adapter were held with a
-     * strong reference, it would be necessary to clear its PagedList observer before it could be
-     * GC'd.
-     *
-     * @param previousSnapshot Snapshot previously captured from this List, or null.
-     * @param callback Callback to dispatch to.
-     *
-     * @see #removeWeakCallback(Callback)
-     */
-    @SuppressWarnings("WeakerAccess")
-    public void addWeakCallback(@Nullable List<T> previousSnapshot, @NonNull Callback callback) {
-        if (previousSnapshot != null && previousSnapshot != this) {
-
-            if (previousSnapshot.isEmpty()) {
-                if (!mStorage.isEmpty()) {
-                    // If snapshot is empty, diff is trivial - just notify number new items.
-                    // Note: occurs in async init, when snapshot taken before init page arrives
-                    callback.onInserted(0, mStorage.size());
-                }
-            } else {
-                PagedList<T> storageSnapshot = (PagedList<T>) previousSnapshot;
-
-                //noinspection unchecked
-                dispatchUpdatesSinceSnapshot(storageSnapshot, callback);
-            }
-        }
-
-        // first, clean up any empty weak refs
-        for (int i = mCallbacks.size() - 1; i >= 0; i--) {
-            final Callback currentCallback = mCallbacks.get(i).get();
-            if (currentCallback == null) {
-                mCallbacks.remove(i);
-            }
-        }
-
-        // then add the new one
-        mCallbacks.add(new WeakReference<>(callback));
-    }
-    /**
-     * Removes a previously added callback.
-     *
-     * @param callback Callback, previously added.
-     * @see #addWeakCallback(List, Callback)
-     */
-    @SuppressWarnings("WeakerAccess")
-    public void removeWeakCallback(@NonNull Callback callback) {
-        for (int i = mCallbacks.size() - 1; i >= 0; i--) {
-            final Callback currentCallback = mCallbacks.get(i).get();
-            if (currentCallback == null || currentCallback == callback) {
-                // found callback, or empty weak ref
-                mCallbacks.remove(i);
-            }
-        }
-    }
-
-    void notifyInserted(int position, int count) {
-        if (count != 0) {
-            for (int i = mCallbacks.size() - 1; i >= 0; i--) {
-                final Callback callback = mCallbacks.get(i).get();
-                if (callback != null) {
-                    callback.onInserted(position, count);
-                }
-            }
-        }
-    }
-
-    void notifyChanged(int position, int count) {
-        if (count != 0) {
-            for (int i = mCallbacks.size() - 1; i >= 0; i--) {
-                final Callback callback = mCallbacks.get(i).get();
-
-                if (callback != null) {
-                    callback.onChanged(position, count);
-                }
-            }
-        }
-    }
-
-    void notifyRemoved(int position, int count) {
-        if (count != 0) {
-            for (int i = mCallbacks.size() - 1; i >= 0; i--) {
-                final Callback callback = mCallbacks.get(i).get();
-
-                if (callback != null) {
-                    callback.onRemoved(position, count);
-                }
-            }
-        }
-    }
-
-    /**
-     * Dispatch updates since the non-empty snapshot was taken.
-     *
-     * @param snapshot Non-empty snapshot.
-     * @param callback Callback for updates that have occurred since snapshot.
-     */
-    abstract void dispatchUpdatesSinceSnapshot(@NonNull PagedList<T> snapshot,
-            @NonNull Callback callback);
-
-    abstract void loadAroundInternal(int index);
-
-    /**
-     * Callback signaling when content is loaded into the list.
-     * <p>
-     * Can be used to listen to items being paged in and out. These calls will be dispatched on
-     * the executor defined by {@link Builder#setNotifyExecutor(Executor)}, which is generally
-     * the main/UI thread.
-     */
-    public abstract static class Callback {
-        /**
-         * Called when null padding items have been loaded to signal newly available data, or when
-         * data that hasn't been used in a while has been dropped, and swapped back to null.
-         *
-         * @param position Position of first newly loaded items, out of total number of items
-         *                 (including padded nulls).
-         * @param count    Number of items loaded.
-         */
-        public abstract void onChanged(int position, int count);
-
-        /**
-         * Called when new items have been loaded at the end or beginning of the list.
-         *
-         * @param position Position of the first newly loaded item (in practice, either
-         *                 <code>0</code> or <code>size - 1</code>.
-         * @param count    Number of items loaded.
-         */
-        public abstract void onInserted(int position, int count);
-
-        /**
-         * Called when items have been removed at the end or beginning of the list, and have not
-         * been replaced by padded nulls.
-         *
-         * @param position Position of the first newly loaded item (in practice, either
-         *                 <code>0</code> or <code>size - 1</code>.
-         * @param count    Number of items loaded.
-         */
-        @SuppressWarnings("unused")
-        public abstract void onRemoved(int position, int count);
-    }
-
-    /**
-     * Configures how a PagedList loads content from its DataSource.
-     * <p>
-     * Use a Config {@link Builder} to construct and define custom loading behavior, such as
-     * {@link Builder#setPageSize(int)}, which defines number of items loaded at a time}.
-     */
-    public static class Config {
-        /**
-         * When {@link #maxSize} is set to {@code MAX_SIZE_UNBOUNDED}, the maximum number of items
-         * loaded is unbounded, and pages will never be dropped.
-         */
-        @SuppressWarnings("WeakerAccess")
-        public static final int MAX_SIZE_UNBOUNDED = Integer.MAX_VALUE;
-
-        /**
-         * Size of each page loaded by the PagedList.
-         */
-        public final int pageSize;
-
-        /**
-         * Prefetch distance which defines how far ahead to load.
-         * <p>
-         * If this value is set to 50, the paged list will attempt to load 50 items in advance of
-         * data that's already been accessed.
-         *
-         * @see PagedList#loadAround(int)
-         */
-        @SuppressWarnings("WeakerAccess")
-        public final int prefetchDistance;
-
-        /**
-         * Defines whether the PagedList may display null placeholders, if the DataSource provides
-         * them.
-         */
-        @SuppressWarnings("WeakerAccess")
-        public final boolean enablePlaceholders;
-
-        /**
-         * Defines the maximum number of items that may be loaded into this pagedList before pages
-         * should be dropped.
-         * <p>
-         * {@link PageKeyedDataSource} does not currently support dropping pages - when
-         * loading from a {@code PageKeyedDataSource}, this value is ignored.
-         *
-         * @see #MAX_SIZE_UNBOUNDED
-         * @see Builder#setMaxSize(int)
-         */
-        public final int maxSize;
-
-        /**
-         * Size hint for initial load of PagedList, often larger than a regular page.
-         */
-        @SuppressWarnings("WeakerAccess")
-        public final int initialLoadSizeHint;
-
-        Config(int pageSize, int prefetchDistance,
-                boolean enablePlaceholders, int initialLoadSizeHint, int maxSize) {
-            this.pageSize = pageSize;
-            this.prefetchDistance = prefetchDistance;
-            this.enablePlaceholders = enablePlaceholders;
-            this.initialLoadSizeHint = initialLoadSizeHint;
-            this.maxSize = maxSize;
-        }
-
-        /**
-         * Builder class for {@link Config}.
-         * <p>
-         * You must at minimum specify page size with {@link #setPageSize(int)}.
-         */
-        public static final class Builder {
-            static final int DEFAULT_INITIAL_PAGE_MULTIPLIER = 3;
-
-            private int mPageSize = -1;
-            private int mPrefetchDistance = -1;
-            private int mInitialLoadSizeHint = -1;
-            private boolean mEnablePlaceholders = true;
-            private int mMaxSize = MAX_SIZE_UNBOUNDED;
-
-            /**
-             * Defines the number of items loaded at once from the DataSource.
-             * <p>
-             * Should be several times the number of visible items onscreen.
-             * <p>
-             * Configuring your page size depends on how your data is being loaded and used. Smaller
-             * page sizes improve memory usage, latency, and avoid GC churn. Larger pages generally
-             * improve loading throughput, to a point
-             * (avoid loading more than 2MB from SQLite at once, since it incurs extra cost).
-             * <p>
-             * If you're loading data for very large, social-media style cards that take up most of
-             * a screen, and your database isn't a bottleneck, 10-20 may make sense. If you're
-             * displaying dozens of items in a tiled grid, which can present items during a scroll
-             * much more quickly, consider closer to 100.
-             *
-             * @param pageSize Number of items loaded at once from the DataSource.
-             * @return this
-             */
-            @NonNull
-            public Builder setPageSize(@IntRange(from = 1) int pageSize) {
-                if (pageSize < 1) {
-                    throw new IllegalArgumentException("Page size must be a positive number");
-                }
-                mPageSize = pageSize;
-                return this;
-            }
-
-            /**
-             * Defines how far from the edge of loaded content an access must be to trigger further
-             * loading.
-             * <p>
-             * Should be several times the number of visible items onscreen.
-             * <p>
-             * If not set, defaults to page size.
-             * <p>
-             * A value of 0 indicates that no list items will be loaded until they are specifically
-             * requested. This is generally not recommended, so that users don't observe a
-             * placeholder item (with placeholders) or end of list (without) while scrolling.
-             *
-             * @param prefetchDistance Distance the PagedList should prefetch.
-             * @return this
-             */
-            @NonNull
-            public Builder setPrefetchDistance(@IntRange(from = 0) int prefetchDistance) {
-                mPrefetchDistance = prefetchDistance;
-                return this;
-            }
-
-            /**
-             * Pass false to disable null placeholders in PagedLists using this Config.
-             * <p>
-             * If not set, defaults to true.
-             * <p>
-             * A PagedList will present null placeholders for not-yet-loaded content if two
-             * conditions are met:
-             * <p>
-             * 1) Its DataSource can count all unloaded items (so that the number of nulls to
-             * present is known).
-             * <p>
-             * 2) placeholders are not disabled on the Config.
-             * <p>
-             * Call {@code setEnablePlaceholders(false)} to ensure the receiver of the PagedList
-             * (often a {@link androidx.paging.PagedListAdapter}) doesn't need to account for null items.
-             * <p>
-             * If placeholders are disabled, not-yet-loaded content will not be present in the list.
-             * Paging will still occur, but as items are loaded or removed, they will be signaled
-             * as inserts to the {@link PagedList.Callback}.
-             * {@link PagedList.Callback#onChanged(int, int)} will not be issued as part of loading,
-             * though a {@link androidx.paging.PagedListAdapter} may still receive change events as a result of
-             * PagedList diffing.
-             *
-             * @param enablePlaceholders False if null placeholders should be disabled.
-             * @return this
-             */
-            @SuppressWarnings("SameParameterValue")
-            @NonNull
-            public Builder setEnablePlaceholders(boolean enablePlaceholders) {
-                mEnablePlaceholders = enablePlaceholders;
-                return this;
-            }
-
-            /**
-             * Defines how many items to load when first load occurs.
-             * <p>
-             * This value is typically larger than page size, so on first load data there's a large
-             * enough range of content loaded to cover small scrolls.
-             * <p>
-             * When using a {@link PositionalDataSource}, the initial load size will be coerced to
-             * an integer multiple of pageSize, to enable efficient tiling.
-             * <p>
-             * If not set, defaults to three times page size.
-             *
-             * @param initialLoadSizeHint Number of items to load while initializing the PagedList.
-             * @return this
-             */
-            @SuppressWarnings("WeakerAccess")
-            @NonNull
-            public Builder setInitialLoadSizeHint(@IntRange(from = 1) int initialLoadSizeHint) {
-                mInitialLoadSizeHint = initialLoadSizeHint;
-                return this;
-            }
-
-            /**
-             * Defines how many items to keep loaded at once.
-             * <p>
-             * This can be used to cap the number of items kept in memory by dropping pages. This
-             * value is typically many pages so old pages are cached in case the user scrolls back.
-             * <p>
-             * This value must be at least two times the
-             * {@link #setPrefetchDistance(int)} prefetch distance} plus the
-             * {@link #setPageSize(int) page size}). This constraint prevent loads from being
-             * continuously fetched and discarded due to prefetching.
-             * <p>
-             * The max size specified here best effort, not a guarantee. In practice, if maxSize
-             * is many times the page size, the number of items held by the PagedList will not grow
-             * above this number. Exceptions are made as necessary to guarantee:
-             * <ul>
-             *     <li>Pages are never dropped until there are more than two pages loaded. Note that
-             *     a DataSource may not be held strictly to
-             *     {@link Config#pageSize requested pageSize}, so two pages may be larger than
-             *     expected.
-             *     <li>Pages are never dropped if they are within a prefetch window (defined to be
-             *     {@code pageSize + (2 * prefetchDistance)}) of the most recent load.
-             * </ul>
-             * <p>
-             * {@link PageKeyedDataSource} does not currently support dropping pages - when
-             * loading from a {@code PageKeyedDataSource}, this value is ignored.
-             * <p>
-             * If not set, defaults to {@code MAX_SIZE_UNBOUNDED}, which disables page dropping.
-             *
-             * @param maxSize Maximum number of items to keep in memory, or
-             *                {@code MAX_SIZE_UNBOUNDED} to disable page dropping.
-             * @return this
-             *
-             * @see Config#MAX_SIZE_UNBOUNDED
-             * @see Config#maxSize
-             */
-            @NonNull
-            public Builder setMaxSize(@IntRange(from = 2) int maxSize) {
-                mMaxSize = maxSize;
-                return this;
-            }
-
-            /**
-             * Creates a {@link Config} with the given parameters.
-             *
-             * @return A new Config.
-             */
-            @NonNull
-            public Config build() {
-                if (mPrefetchDistance < 0) {
-                    mPrefetchDistance = mPageSize;
-                }
-                if (mInitialLoadSizeHint < 0) {
-                    mInitialLoadSizeHint = mPageSize * DEFAULT_INITIAL_PAGE_MULTIPLIER;
-                }
-                if (!mEnablePlaceholders && mPrefetchDistance == 0) {
-                    throw new IllegalArgumentException("Placeholders and prefetch are the only ways"
-                            + " to trigger loading of more data in the PagedList, so either"
-                            + " placeholders must be enabled, or prefetch distance must be > 0.");
-                }
-                if (mMaxSize != MAX_SIZE_UNBOUNDED) {
-                    if (mMaxSize < mPageSize + mPrefetchDistance * 2) {
-                        throw new IllegalArgumentException("Maximum size must be at least"
-                                + " pageSize + 2*prefetchDist, pageSize=" + mPageSize
-                                + ", prefetchDist=" + mPrefetchDistance + ", maxSize=" + mMaxSize);
-                    }
-                }
-
-                return new Config(mPageSize, mPrefetchDistance,
-                        mEnablePlaceholders, mInitialLoadSizeHint, mMaxSize);
-            }
-        }
-    }
-
-    /**
-     * Signals when a PagedList has reached the end of available data.
-     * <p>
-     * When local storage is a cache of network data, it's common to set up a streaming pipeline:
-     * Network data is paged into the database, database is paged into UI. Paging from the database
-     * to UI can be done with a {@code LiveData<PagedList>}, but it's still necessary to know when
-     * to trigger network loads.
-     * <p>
-     * BoundaryCallback does this signaling - when a DataSource runs out of data at the end of
-     * the list, {@link #onItemAtEndLoaded(Object)} is called, and you can start an async network
-     * load that will write the result directly to the database. Because the database is being
-     * observed, the UI bound to the {@code LiveData<PagedList>} will update automatically to
-     * account for the new items.
-     * <p>
-     * Note that a BoundaryCallback instance shared across multiple PagedLists (e.g. when passed to
-     * {@link androidx.paging.LivePagedListBuilder#setBoundaryCallback}, the callbacks may be issued multiple
-     * times. If for example {@link #onItemAtEndLoaded(Object)} triggers a network load, it should
-     * avoid triggering it again while the load is ongoing.
-     * <p>
-     * The database + network Repository in the
-     * <a href="https://github.com/googlesamples/android-architecture-components/blob/master/PagingWithNetworkSample/README.md">PagingWithNetworkSample</a>
-     * shows how to implement a network BoundaryCallback using
-     * <a href="https://square.github.io/retrofit/">Retrofit</a>, while
-     * handling swipe-to-refresh, network errors, and retry.
-     * <h4>Requesting Network Data</h4>
-     * BoundaryCallback only passes the item at front or end of the list when out of data. This
-     * makes it an easy fit for item-keyed network requests, where you can use the item passed to
-     * the BoundaryCallback to request more data from the network. In these cases, the source of
-     * truth for next page to load is coming from local storage, based on what's already loaded.
-     * <p>
-     * If you aren't using an item-keyed network API, you may be using page-keyed, or page-indexed.
-     * If this is the case, the paging library doesn't know about the page key or index used in the
-     * BoundaryCallback, so you need to track it yourself. You can do this in one of two ways:
-     * <h5>Local storage Page key</h5>
-     * If you want to perfectly resume your query, even if the app is killed and resumed, you can
-     * store the key on disk. Note that with a positional/page index network API, there's a simple
-     * way to do this, by using the {@code listSize} as an input to the next load (or
-     * {@code listSize / NETWORK_PAGE_SIZE}, for page indexing).
-     * <p>
-     * The current list size isn't passed to the BoundaryCallback though. This is because the
-     * PagedList doesn't necessarily know the number of items in local storage. Placeholders may be
-     * disabled, or the DataSource may not count total number of items.
-     * <p>
-     * Instead, for these positional cases, you can query the database for the number of items, and
-     * pass that to the network.
-     * <h5>In-Memory Page key</h5>
-     * Often it doesn't make sense to query the next page from network if the last page you fetched
-     * was loaded many hours or days before. If you keep the key in memory, you can refresh any time
-     * you start paging from a network source.
-     * <p>
-     * Store the next key in memory, inside your BoundaryCallback. When you create a new
-     * BoundaryCallback when creating a new {@code LiveData}/{@code Observable} of
-     * {@code PagedList}, refresh data. For example,
-     * <a href="https://codelabs.developers.google.com/codelabs/android-paging/index.html#8">in the
-     * Paging Codelab</a>, the GitHub network page index is stored in memory.
-     *
-     * @param <T> Type loaded by the PagedList.
-     */
-    @MainThread
-    public abstract static class BoundaryCallback<T> {
-        /**
-         * Called when zero items are returned from an initial load of the PagedList's data source.
-         */
-        public void onZeroItemsLoaded() {}
-
-        /**
-         * Called when the item at the front of the PagedList has been loaded, and access has
-         * occurred within {@link Config#prefetchDistance} of it.
-         * <p>
-         * No more data will be prepended to the PagedList before this item.
-         *
-         * @param itemAtFront The first item of PagedList
-         */
-        public void onItemAtFrontLoaded(@NonNull T itemAtFront) {}
-
-        /**
-         * Called when the item at the end of the PagedList has been loaded, and access has
-         * occurred within {@link Config#prefetchDistance} of it.
-         * <p>
-         * No more data will be appended to the PagedList after this item.
-         *
-         * @param itemAtEnd The first item of PagedList
-         */
-        public void onItemAtEndLoaded(@NonNull T itemAtEnd) {}
-    }
-}
diff --git a/paging/common/src/main/java/androidx/paging/PagedStorage.java b/paging/common/src/main/java/androidx/paging/PagedStorage.java
deleted file mode 100644
index 232302f..0000000
--- a/paging/common/src/main/java/androidx/paging/PagedStorage.java
+++ /dev/null
@@ -1,663 +0,0 @@
-/*
- * 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 java.util.AbstractList;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Class holding the pages of data backing a PagedList, presenting sparse loaded data as a List.
- * <p>
- * It has two modes of operation: contiguous and non-contiguous (tiled). This class only holds
- * data, and does not have any notion of the ideas of async loads, or prefetching.
- */
-final class PagedStorage<T> extends AbstractList<T> implements Pager.AdjacentProvider<T> {
-    /**
-     * Lists instances are compared (with instance equality) to PLACEHOLDER_LIST to check if an item
-     * in that position is already loading. We use a singleton placeholder list that is distinct
-     * from Collections.emptyList() for safety.
-     */
-    @SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
-    private static final List PLACEHOLDER_LIST = new ArrayList();
-
-    private int mLeadingNullCount;
-    /**
-     * List of pages in storage.
-     *
-     * Two storage modes:
-     *
-     * Contiguous - all content in mPages is valid and loaded, but may return false from isTiled().
-     *     Safe to access any item in any page.
-     *
-     * Non-contiguous - mPages may have nulls or a placeholder page, isTiled() always returns true.
-     *     mPages may have nulls, or placeholder (empty) pages while content is loading.
-     */
-    private final ArrayList<List<T>> mPages;
-    private int mTrailingNullCount;
-
-    private int mPositionOffset;
-    /**
-     * Number of loaded items held by {@link #mPages}. When tiling, doesn't count unloaded pages in
-     * {@link #mPages}. If tiling is disabled, same as {@link #mStorageCount}.
-     *
-     * This count is the one used for trimming.
-     */
-    private int mLoadedCount;
-
-    /**
-     * Number of items represented by {@link #mPages}. If tiling is enabled, unloaded items in
-     * {@link #mPages} may be null, but this value still counts them.
-     */
-    private int mStorageCount;
-
-    // If mPageSize > 0, tiling is enabled, 'mPages' may have gaps, and leadingPages is set
-    private int mPageSize;
-
-    private int mNumberPrepended;
-    private int mNumberAppended;
-
-    PagedStorage() {
-        mLeadingNullCount = 0;
-        mPages = new ArrayList<>();
-        mTrailingNullCount = 0;
-        mPositionOffset = 0;
-        mLoadedCount = 0;
-        mStorageCount = 0;
-        mPageSize = 1;
-        mNumberPrepended = 0;
-        mNumberAppended = 0;
-    }
-
-    PagedStorage(int leadingNulls, List<T> page, int trailingNulls) {
-        this();
-        init(leadingNulls, page, trailingNulls, 0);
-    }
-
-    private PagedStorage(PagedStorage<T> other) {
-        mLeadingNullCount = other.mLeadingNullCount;
-        mPages = new ArrayList<>(other.mPages);
-        mTrailingNullCount = other.mTrailingNullCount;
-        mPositionOffset = other.mPositionOffset;
-        mLoadedCount = other.mLoadedCount;
-        mStorageCount = other.mStorageCount;
-        mPageSize = other.mPageSize;
-        mNumberPrepended = other.mNumberPrepended;
-        mNumberAppended = other.mNumberAppended;
-    }
-
-    PagedStorage<T> snapshot() {
-        return new PagedStorage<>(this);
-    }
-
-    private void init(int leadingNulls, List<T> page, int trailingNulls, int positionOffset) {
-        mLeadingNullCount = leadingNulls;
-        mPages.clear();
-        mPages.add(page);
-        mTrailingNullCount = trailingNulls;
-
-        mPositionOffset = positionOffset;
-        mLoadedCount = page.size();
-        mStorageCount = mLoadedCount;
-
-                // initialized as tiled. There may be 3 nulls, 2 items, but we still call this tiled
-        // even if it will break if nulls convert.
-        mPageSize = page.size();
-
-        mNumberPrepended = 0;
-        mNumberAppended = 0;
-    }
-
-    void init(int leadingNulls, @NonNull List<T> page, int trailingNulls, int positionOffset,
-            @NonNull Callback callback) {
-        init(leadingNulls, page, trailingNulls, positionOffset);
-        callback.onInitialized(size());
-    }
-
-    @Override
-    public T get(int i) {
-        if (i < 0 || i >= size()) {
-            throw new IndexOutOfBoundsException("Index: " + i + ", Size: " + size());
-        }
-
-        // is it definitely outside 'mPages'?
-        int localIndex = i - mLeadingNullCount;
-        if (localIndex < 0 || localIndex >= mStorageCount) {
-            return null;
-        }
-
-        int localPageIndex;
-        int pageInternalIndex;
-
-        if (isTiled()) {
-            // it's inside mPages, and we're tiled. Jump to correct tile.
-            localPageIndex = localIndex / mPageSize;
-            pageInternalIndex = localIndex % mPageSize;
-        } else {
-            // it's inside mPages, but page sizes aren't regular. Walk to correct tile.
-            // Pages can only be null while tiled, so accessing page count is safe.
-            pageInternalIndex = localIndex;
-            final int localPageCount = mPages.size();
-            for (localPageIndex = 0; localPageIndex < localPageCount; localPageIndex++) {
-                int pageSize = mPages.get(localPageIndex).size();
-                if (pageSize > pageInternalIndex) {
-                    // stop, found the page
-                    break;
-                }
-                pageInternalIndex -= pageSize;
-            }
-        }
-
-        List<T> page = mPages.get(localPageIndex);
-        if (page == null || page.size() == 0) {
-            // can only occur in tiled case, with untouched inner/placeholder pages
-            return null;
-        }
-        return page.get(pageInternalIndex);
-    }
-
-    /**
-     * Returns true if all pages are the same size, except for the last, which may be smaller
-     */
-    boolean isTiled() {
-        return mPageSize > 0;
-    }
-
-    int getLeadingNullCount() {
-        return mLeadingNullCount;
-    }
-
-    int getTrailingNullCount() {
-        return mTrailingNullCount;
-    }
-
-    int getStorageCount() {
-        return mStorageCount;
-    }
-
-    int getNumberAppended() {
-        return mNumberAppended;
-    }
-
-    int getNumberPrepended() {
-        return mNumberPrepended;
-    }
-
-    int getPageCount() {
-        return mPages.size();
-    }
-
-    int getLoadedCount() {
-        return mLoadedCount;
-    }
-
-    interface Callback {
-        void onInitialized(int count);
-        void onPagePrepended(int leadingNulls, int changed, int added);
-        void onPageAppended(int endPosition, int changed, int added);
-        void onPagePlaceholderInserted(int pageIndex);
-        void onPageInserted(int start, int count);
-        void onPagesRemoved(int startOfDrops, int count);
-        void onPagesSwappedToPlaceholder(int startOfDrops, int count);
-    }
-
-    int getPositionOffset() {
-        return mPositionOffset;
-    }
-
-    int getMiddleOfLoadedRange() {
-        return mLeadingNullCount + mPositionOffset + mStorageCount / 2;
-    }
-
-    @Override
-    public int size() {
-        return mLeadingNullCount + mStorageCount + mTrailingNullCount;
-    }
-
-    int computeLeadingNulls() {
-        int total = mLeadingNullCount;
-        final int pageCount = mPages.size();
-        for (int i = 0; i < pageCount; i++) {
-            List page = mPages.get(i);
-            if (page != null && page != PLACEHOLDER_LIST) {
-                break;
-            }
-            total += mPageSize;
-        }
-        return total;
-    }
-
-    int computeTrailingNulls() {
-        int total = mTrailingNullCount;
-        for (int i = mPages.size() - 1; i >= 0; i--) {
-            List page = mPages.get(i);
-            if (page != null && page != PLACEHOLDER_LIST) {
-                break;
-            }
-            total += mPageSize;
-        }
-        return total;
-    }
-
-    // ---------------- Trimming API -------------------
-    // Trimming is always done at the beginning or end of the list, as content is loaded.
-    // In addition to trimming pages in the storage, we also support pre-trimming pages (dropping
-    // them just before they're added) to avoid dispatching an add followed immediately by a trim.
-    //
-    // Note - we avoid trimming down to a single page to reduce chances of dropping page in
-    // viewport, since we don't strictly know the viewport. If trim is aggressively set to size of a
-    // single page, trimming while the user can see a page boundary is dangerous. To be safe, we
-    // just avoid trimming in these cases entirely.
-
-    private boolean needsTrim(int maxSize, int requiredRemaining, int localPageIndex) {
-        List<T> page = mPages.get(localPageIndex);
-        return page == null || (mLoadedCount > maxSize
-                && mPages.size() > 2
-                && page != PLACEHOLDER_LIST
-                && mLoadedCount - page.size() >= requiredRemaining);
-    }
-
-    boolean needsTrimFromFront(int maxSize, int requiredRemaining) {
-        return needsTrim(maxSize, requiredRemaining, 0);
-    }
-
-    boolean needsTrimFromEnd(int maxSize, int requiredRemaining) {
-        return needsTrim(maxSize, requiredRemaining, mPages.size() - 1);
-    }
-
-    boolean shouldPreTrimNewPage(int maxSize, int requiredRemaining, int countToBeAdded) {
-        return mLoadedCount + countToBeAdded > maxSize
-                && mPages.size() > 1
-                && mLoadedCount >= requiredRemaining;
-    }
-
-    boolean trimFromFront(boolean insertNulls, int maxSize, int requiredRemaining,
-            @NonNull Callback callback) {
-        int totalRemoved = 0;
-        while (needsTrimFromFront(maxSize, requiredRemaining)) {
-            List page = mPages.remove(0);
-            int removed = (page == null) ? mPageSize : page.size();
-            totalRemoved += removed;
-            mStorageCount -= removed;
-            mLoadedCount -= (page == null) ? 0 : page.size();
-        }
-
-        if (totalRemoved > 0) {
-            if (insertNulls) {
-                // replace removed items with nulls
-                int previousLeadingNulls = mLeadingNullCount;
-                mLeadingNullCount += totalRemoved;
-                callback.onPagesSwappedToPlaceholder(previousLeadingNulls, totalRemoved);
-            } else {
-                // simply remove, and handle offset
-                mPositionOffset += totalRemoved;
-                callback.onPagesRemoved(mLeadingNullCount, totalRemoved);
-            }
-        }
-        return totalRemoved > 0;
-    }
-
-    boolean trimFromEnd(boolean insertNulls, int maxSize, int requiredRemaining,
-            @NonNull Callback callback) {
-        int totalRemoved = 0;
-        while (needsTrimFromEnd(maxSize, requiredRemaining)) {
-            List page = mPages.remove(mPages.size() - 1);
-            int removed = (page == null) ? mPageSize : page.size();
-            totalRemoved += removed;
-            mStorageCount -= removed;
-            mLoadedCount -= (page == null) ? 0 : page.size();
-        }
-
-        if (totalRemoved > 0) {
-            int newEndPosition = mLeadingNullCount + mStorageCount;
-            if (insertNulls) {
-                // replace removed items with nulls
-                mTrailingNullCount += totalRemoved;
-                callback.onPagesSwappedToPlaceholder(newEndPosition, totalRemoved);
-            } else {
-                // items were just removed, signal
-                callback.onPagesRemoved(newEndPosition, totalRemoved);
-            }
-        }
-        return totalRemoved > 0;
-    }
-
-    // ---------------- Contiguous API -------------------
-
-    void prependPage(@NonNull List<T> page, @NonNull Callback callback) {
-        final int count = page.size();
-        if (count == 0) {
-            // Nothing returned from source, nothing to do
-            return;
-        }
-        if (mPageSize > 0 && count != mPageSize) {
-            if (mPages.size() == 1 && count > mPageSize) {
-                // prepending to a single item - update current page size to that of 'inner' page
-                mPageSize = count;
-            } else {
-                // no longer tiled
-                mPageSize = -1;
-            }
-        }
-
-        mPages.add(0, page);
-        mLoadedCount += count;
-        mStorageCount += count;
-
-        final int changedCount = Math.min(mLeadingNullCount, count);
-        final int addedCount = count - changedCount;
-
-        if (changedCount != 0) {
-            mLeadingNullCount -= changedCount;
-        }
-        mPositionOffset -= addedCount;
-        mNumberPrepended += count;
-
-        callback.onPagePrepended(mLeadingNullCount, changedCount, addedCount);
-    }
-
-    void appendPage(@NonNull List<T> page, @NonNull Callback callback) {
-        final int count = page.size();
-        if (count == 0) {
-            // Nothing returned from source, nothing to do
-            return;
-        }
-
-        if (mPageSize > 0) {
-            // if the previous page was smaller than mPageSize,
-            // or if this page is larger than the previous, disable tiling
-            if (mPages.get(mPages.size() - 1).size() != mPageSize
-                    || count > mPageSize) {
-                mPageSize = -1;
-            }
-        }
-
-        mPages.add(page);
-        mLoadedCount += count;
-        mStorageCount += count;
-
-        final int changedCount = Math.min(mTrailingNullCount, count);
-        final int addedCount = count - changedCount;
-
-        if (changedCount != 0) {
-            mTrailingNullCount -= changedCount;
-        }
-        mNumberAppended += count;
-        callback.onPageAppended(mLeadingNullCount + mStorageCount - count,
-                changedCount, addedCount);
-    }
-
-    // ------------- Adjacent Provider interface (contiguous-only) ------------------
-
-    @Override
-    public T getFirstLoadedItem() {
-        // safe to access first page's first item here:
-        // If contiguous, mPages can't be empty, can't hold null Pages, and items can't be empty
-        return mPages.get(0).get(0);
-    }
-
-    @Override
-    public T getLastLoadedItem() {
-        // safe to access last page's last item here:
-        // If contiguous, mPages can't be empty, can't hold null Pages, and items can't be empty
-        List<T> page = mPages.get(mPages.size() - 1);
-        return page.get(page.size() - 1);
-    }
-
-    @Override
-    public int getFirstLoadedItemIndex() {
-        return getLeadingNullCount() + getPositionOffset();
-    }
-
-    @Override
-    public int getLastLoadedItemIndex() {
-        return getLeadingNullCount() + getStorageCount() - 1 + getPositionOffset();
-    }
-
-    @Override
-    public void onPageResultResolution(@NonNull PagedList.LoadType type,
-            @NonNull DataSource.BaseResult<T> pageResult) {
-        // ignored
-    }
-
-    // ------------------ Non-Contiguous API (tiling required) ----------------------
-
-    /**
-     * Return true if the page at the passed position would be the first (if trimFromFront) or last
-     * page that's currently loading.
-     */
-    boolean pageWouldBeBoundary(int positionOfPage, boolean trimFromFront) {
-        if (mPageSize < 1 || mPages.size() < 2) {
-            throw new IllegalStateException("Trimming attempt before sufficient load");
-        }
-
-        if (positionOfPage < mLeadingNullCount) {
-            // position represent page in leading nulls
-            return trimFromFront;
-        }
-
-        if (positionOfPage >= mLeadingNullCount + mStorageCount) {
-            // position represent page in trailing nulls
-            return !trimFromFront;
-        }
-
-        int localPageIndex = (positionOfPage - mLeadingNullCount) / mPageSize;
-
-        // walk outside in, return false if we find non-placeholder page before localPageIndex
-        if (trimFromFront) {
-            for (int i = 0; i < localPageIndex; i++) {
-                if (mPages.get(i) != null) {
-                    return false;
-                }
-            }
-        } else {
-            for (int i = mPages.size() - 1; i > localPageIndex; i--) {
-                if (mPages.get(i) != null) {
-                    return false;
-                }
-            }
-        }
-
-        // didn't find another page, so this one would be a boundary
-        return true;
-    }
-
-    void initAndSplit(int leadingNulls, @NonNull List<T> multiPageList,
-            int trailingNulls, int positionOffset, int pageSize, @NonNull Callback callback) {
-        int pageCount = (multiPageList.size() + (pageSize - 1)) / pageSize;
-        for (int i = 0; i < pageCount; i++) {
-            int beginInclusive = i * pageSize;
-            int endExclusive = Math.min(multiPageList.size(), (i + 1) * pageSize);
-
-            List<T> sublist = multiPageList.subList(beginInclusive, endExclusive);
-
-            if (i == 0) {
-                // Trailing nulls for first page includes other pages in multiPageList
-                int initialTrailingNulls = trailingNulls + multiPageList.size() - sublist.size();
-                init(leadingNulls, sublist, initialTrailingNulls, positionOffset);
-            } else {
-                int insertPosition = leadingNulls + beginInclusive;
-                insertPage(insertPosition, sublist, null);
-            }
-        }
-        callback.onInitialized(size());
-    }
-
-    void tryInsertPageAndTrim(
-            int position,
-            @NonNull List<T> page,
-            int lastLoad,
-            int maxSize,
-            int requiredRemaining,
-            @NonNull Callback callback) {
-        boolean trim = maxSize != PagedList.Config.MAX_SIZE_UNBOUNDED;
-        boolean trimFromFront = lastLoad > getMiddleOfLoadedRange();
-
-        boolean pageInserted = !trim
-                || !shouldPreTrimNewPage(maxSize, requiredRemaining, page.size())
-                || !pageWouldBeBoundary(position, trimFromFront);
-
-        if (pageInserted) {
-            insertPage(position, page, callback);
-        } else {
-            // trim would have us drop the page we just loaded - swap it to null
-            int localPageIndex = (position - mLeadingNullCount) / mPageSize;
-            mPages.set(localPageIndex, null);
-
-            // note: we also remove it, so we don't have to guess how large a 'null' page is later
-            mStorageCount -= page.size();
-            if (trimFromFront) {
-                mPages.remove(0);
-                mLeadingNullCount += page.size();
-            } else {
-                mPages.remove(mPages.size() - 1);
-                mTrailingNullCount += page.size();
-            }
-        }
-
-        if (trim) {
-            if (trimFromFront) {
-                trimFromFront(true, maxSize, requiredRemaining, callback);
-            } else {
-                trimFromEnd(true, maxSize, requiredRemaining, callback);
-            }
-        }
-    }
-
-    public void insertPage(int position, @NonNull List<T> page, @Nullable Callback callback) {
-        final int newPageSize = page.size();
-        if (newPageSize != mPageSize) {
-            // differing page size is OK in 2 cases, when the page is being added:
-            // 1) to the end (in which case, ignore new smaller size)
-            // 2) only the last page has been added so far (in which case, adopt new bigger size)
-
-            int size = size();
-            boolean addingLastPage = position == (size - size % mPageSize)
-                    && newPageSize < mPageSize;
-            boolean onlyEndPagePresent = mTrailingNullCount == 0 && mPages.size() == 1
-                    && newPageSize > mPageSize;
-
-            // OK only if existing single page, and it's the last one
-            if (!onlyEndPagePresent && !addingLastPage) {
-                throw new IllegalArgumentException("page introduces incorrect tiling");
-            }
-            if (onlyEndPagePresent) {
-                mPageSize = newPageSize;
-            }
-        }
-
-        int pageIndex = position / mPageSize;
-
-        allocatePageRange(pageIndex, pageIndex);
-
-        int localPageIndex = pageIndex - mLeadingNullCount / mPageSize;
-
-        List<T> oldPage = mPages.get(localPageIndex);
-        if (oldPage != null && oldPage != PLACEHOLDER_LIST) {
-            throw new IllegalArgumentException(
-                    "Invalid position " + position + ": data already loaded");
-        }
-        mPages.set(localPageIndex, page);
-        mLoadedCount += newPageSize;
-        if (callback != null) {
-            callback.onPageInserted(position, newPageSize);
-        }
-    }
-
-    void allocatePageRange(final int minimumPage, final int maximumPage) {
-        int leadingNullPages = mLeadingNullCount / mPageSize;
-
-        if (minimumPage < leadingNullPages) {
-            for (int i = 0; i < leadingNullPages - minimumPage; i++) {
-                mPages.add(0, null);
-            }
-            int newStorageAllocated = (leadingNullPages - minimumPage) * mPageSize;
-            mStorageCount += newStorageAllocated;
-            mLeadingNullCount -= newStorageAllocated;
-
-            leadingNullPages = minimumPage;
-        }
-        if (maximumPage >= leadingNullPages + mPages.size()) {
-            int newStorageAllocated = Math.min(mTrailingNullCount,
-                    (maximumPage + 1 - (leadingNullPages + mPages.size())) * mPageSize);
-            for (int i = mPages.size(); i <= maximumPage - leadingNullPages; i++) {
-                mPages.add(mPages.size(), null);
-            }
-            mStorageCount += newStorageAllocated;
-            mTrailingNullCount -= newStorageAllocated;
-        }
-    }
-
-    public void allocatePlaceholders(int index, int prefetchDistance,
-            int pageSize, Callback callback) {
-        if (pageSize != mPageSize) {
-            if (pageSize < mPageSize) {
-                throw new IllegalArgumentException("Page size cannot be reduced");
-            }
-            if (mPages.size() != 1 || mTrailingNullCount != 0) {
-                // not in single, last page allocated case - can't change page size
-                throw new IllegalArgumentException(
-                        "Page size can change only if last page is only one present");
-            }
-            mPageSize = pageSize;
-        }
-
-        final int maxPageCount = (size() + mPageSize - 1) / mPageSize;
-        int minimumPage = Math.max((index - prefetchDistance) / mPageSize, 0);
-        int maximumPage = Math.min((index + prefetchDistance) / mPageSize, maxPageCount - 1);
-
-        allocatePageRange(minimumPage, maximumPage);
-        int leadingNullPages = mLeadingNullCount / mPageSize;
-        for (int pageIndex = minimumPage; pageIndex <= maximumPage; pageIndex++) {
-            int localPageIndex = pageIndex - leadingNullPages;
-            if (mPages.get(localPageIndex) == null) {
-                //noinspection unchecked
-                mPages.set(localPageIndex, PLACEHOLDER_LIST);
-                callback.onPagePlaceholderInserted(pageIndex);
-            }
-        }
-    }
-
-    public boolean hasPage(int pageSize, int index) {
-        // NOTE: we pass pageSize here to avoid in case mPageSize
-        // not fully initialized (when last page only one loaded)
-        int leadingNullPages = mLeadingNullCount / pageSize;
-
-        if (index < leadingNullPages || index >= leadingNullPages + mPages.size()) {
-            return false;
-        }
-
-        List<T> page = mPages.get(index - leadingNullPages);
-
-        return page != null && page != PLACEHOLDER_LIST;
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder ret = new StringBuilder("leading " + mLeadingNullCount
-                + ", storage " + mStorageCount
-                + ", trailing " + getTrailingNullCount());
-
-        for (int i = 0; i < mPages.size(); i++) {
-            ret.append(" ").append(mPages.get(i));
-        }
-        return ret.toString();
-    }
-}
diff --git a/paging/common/src/main/java/androidx/paging/Pager.java b/paging/common/src/main/java/androidx/paging/Pager.java
deleted file mode 100644
index 92ae848..0000000
--- a/paging/common/src/main/java/androidx/paging/Pager.java
+++ /dev/null
@@ -1,370 +0,0 @@
-/*
- * 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.
- */
-
-package androidx.paging;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.paging.futures.FutureCallback;
-import androidx.paging.futures.Futures;
-
-import com.google.common.util.concurrent.ListenableFuture;
-
-import java.util.concurrent.Executor;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-class Pager<K, V> {
-    @SuppressWarnings("unchecked")
-    Pager(@NonNull PagedList.Config config,
-            @NonNull DataSource<K, V> source,
-            @NonNull Executor notifyExecutor,
-            @NonNull Executor fetchExecutor,
-            @NonNull PageConsumer<V> pageConsumer,
-            @Nullable AdjacentProvider<V> adjacentProvider,
-            @NonNull DataSource.BaseResult<V> result) {
-        mConfig = config;
-        mSource = source;
-        mNotifyExecutor = notifyExecutor;
-        mFetchExecutor = fetchExecutor;
-        mPageConsumer = pageConsumer;
-        if (adjacentProvider == null) {
-            adjacentProvider = new SimpleAdjacentProvider<>();
-        }
-        mAdjacentProvider = adjacentProvider;
-        mPrevKey = (K) result.prevKey;
-        mNextKey = (K) result.nextKey;
-        adjacentProvider.onPageResultResolution(PagedList.LoadType.REFRESH, result);
-        mTotalCount = result.totalCount();
-
-        // TODO: move this validation to tiled paging impl, once that's added back
-        if (mSource.mType == DataSource.KeyType.POSITIONAL && mConfig.enablePlaceholders) {
-            result.validateForInitialTiling(mConfig.pageSize);
-        }
-    }
-
-    private final int mTotalCount;
-
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
-    @NonNull
-    final PagedList.Config mConfig;
-
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
-    @NonNull
-    final DataSource<K, V> mSource;
-
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
-    @NonNull
-    final Executor mNotifyExecutor;
-
-    @NonNull
-    private final Executor mFetchExecutor;
-
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
-    @NonNull
-    final PageConsumer<V> mPageConsumer;
-
-    @NonNull
-    private final AdjacentProvider<V> mAdjacentProvider;
-
-    @Nullable
-    private K mPrevKey;
-
-    @Nullable
-    private K mNextKey;
-
-    private final AtomicBoolean mDetached = new AtomicBoolean(false);
-
-    PagedList.LoadStateManager mLoadStateManager = new PagedList.LoadStateManager() {
-        @Override
-        protected void onStateChanged(@NonNull PagedList.LoadType type,
-                @NonNull PagedList.LoadState state, @Nullable Throwable error) {
-            mPageConsumer.onStateChanged(type, state, error);
-        }
-    };
-
-    private void listenTo(@NonNull final PagedList.LoadType type,
-            @NonNull final ListenableFuture<? extends DataSource.BaseResult<V>> future) {
-        // First listen on the BG thread if the DataSource is invalid, since it can be expensive
-        future.addListener(new Runnable() {
-            @Override
-            public void run() {
-                // if invalid, drop result on the floor
-                if (mSource.isInvalid()) {
-                    detach();
-                    return;
-                }
-
-                // Source has been verified to be valid after producing data, so sent data to UI
-                Futures.addCallback(future, new FutureCallback<DataSource.BaseResult<V>>() {
-                    @Override
-                    public void onSuccess(DataSource.BaseResult<V> value) {
-                        onLoadSuccess(type, value);
-                    }
-
-                    @Override
-                    public void onError(@NonNull Throwable throwable) {
-                        onLoadError(type, throwable);
-                    }
-                }, mNotifyExecutor);
-            }
-        }, mFetchExecutor);
-    }
-
-    interface PageConsumer<V> {
-        // return true if we need to fetch more
-        boolean onPageResult(
-                @NonNull PagedList.LoadType type,
-                @NonNull DataSource.BaseResult<V> pageResult);
-
-        void onStateChanged(@NonNull PagedList.LoadType type,
-                @NonNull PagedList.LoadState state, @Nullable Throwable error);
-    }
-
-    interface AdjacentProvider<V> {
-        V getFirstLoadedItem();
-
-        V getLastLoadedItem();
-
-        int getFirstLoadedItemIndex();
-
-        int getLastLoadedItemIndex();
-
-        /**
-         * Notify the AdjacentProvider of new loaded data, to update first/last item/index.
-         *
-         * NOTE: this data may not be committed (e.g. it may be dropped due to max size). Up to the
-         * implementation of the AdjacentProvider to handle this (generally by ignoring this
-         * call if dropping is supported).
-         */
-        void onPageResultResolution(
-                @NonNull PagedList.LoadType type,
-                @NonNull DataSource.BaseResult<V> result);
-    }
-
-    @SuppressWarnings("WeakerAccess") /* synthetic accessor */
-    void onLoadSuccess(PagedList.LoadType type, DataSource.BaseResult<V> value) {
-        if (isDetached()) {
-            // abort!
-            return;
-        }
-
-        mAdjacentProvider.onPageResultResolution(type, value);
-
-        if (mPageConsumer.onPageResult(type, value)) {
-            if (type.equals(PagedList.LoadType.START)) {
-                //noinspection unchecked
-                mPrevKey = (K) value.prevKey;
-                schedulePrepend();
-            } else if (type.equals(PagedList.LoadType.END)) {
-                //noinspection unchecked
-                mNextKey = (K) value.nextKey;
-                scheduleAppend();
-            } else {
-                throw new IllegalStateException("Can only fetch more during append/prepend");
-            }
-        } else {
-            PagedList.LoadState state =
-                    value.data.isEmpty() ? PagedList.LoadState.DONE : PagedList.LoadState.IDLE;
-            mLoadStateManager.setState(type, state, null);
-        }
-    }
-
-    @SuppressWarnings("WeakerAccess") /* synthetic accessor */
-    void onLoadError(PagedList.LoadType type, Throwable throwable) {
-        if (isDetached()) {
-            // abort!
-            return;
-        }
-        // TODO: handle nesting
-        PagedList.LoadState state = mSource.isRetryableError(throwable)
-                ? PagedList.LoadState.RETRYABLE_ERROR : PagedList.LoadState.ERROR;
-        mLoadStateManager.setState(type, state, throwable);
-    }
-
-    public void trySchedulePrepend() {
-        if (mLoadStateManager.getStart().equals(PagedList.LoadState.IDLE)) {
-            schedulePrepend();
-        }
-    }
-
-    public void tryScheduleAppend() {
-        if (mLoadStateManager.getEnd().equals(PagedList.LoadState.IDLE)) {
-            scheduleAppend();
-        }
-    }
-
-    private boolean canPrepend() {
-        if (mTotalCount == DataSource.BaseResult.TOTAL_COUNT_UNKNOWN) {
-            // don't know count / position from initial load, so be conservative, return true
-            return true;
-        }
-
-        // position is known, do we have space left?
-        return mAdjacentProvider.getFirstLoadedItemIndex() > 0;
-    }
-
-    private boolean canAppend() {
-        if (mTotalCount == DataSource.BaseResult.TOTAL_COUNT_UNKNOWN) {
-            // don't know count / position from initial load, so be conservative, return true
-            return true;
-        }
-
-        // count is known, do we have space left?
-        return mAdjacentProvider.getLastLoadedItemIndex() < mTotalCount - 1;
-    }
-
-    private void schedulePrepend() {
-        if (!canPrepend()) {
-            onLoadSuccess(PagedList.LoadType.START, DataSource.BaseResult.<V>empty());
-            return;
-        }
-        K key;
-        switch(mSource.mType) {
-            case POSITIONAL:
-                //noinspection unchecked
-                key = (K) ((Integer) (mAdjacentProvider.getFirstLoadedItemIndex() - 1));
-                break;
-            case PAGE_KEYED:
-                key = mPrevKey;
-                break;
-            case ITEM_KEYED:
-                key = mSource.getKey(mAdjacentProvider.getFirstLoadedItem());
-                break;
-            default:
-                throw new IllegalArgumentException("unknown source type");
-        }
-        mLoadStateManager.setState(
-                PagedList.LoadType.START, PagedList.LoadState.LOADING, null);
-        listenTo(PagedList.LoadType.START, mSource.load(new DataSource.Params<>(
-                DataSource.LoadType.START,
-                key,
-                mConfig.initialLoadSizeHint,
-                mConfig.enablePlaceholders,
-                mConfig.pageSize)));
-    }
-
-    private void scheduleAppend() {
-        if (!canAppend()) {
-            onLoadSuccess(PagedList.LoadType.END, DataSource.BaseResult.<V>empty());
-            return;
-        }
-
-        K key;
-        switch(mSource.mType) {
-            case POSITIONAL:
-                //noinspection unchecked
-                key = (K) ((Integer) (mAdjacentProvider.getLastLoadedItemIndex() + 1));
-                break;
-            case PAGE_KEYED:
-                key = mNextKey;
-                break;
-            case ITEM_KEYED:
-                key = mSource.getKey(mAdjacentProvider.getLastLoadedItem());
-                break;
-            default:
-                throw new IllegalArgumentException("unknown source type");
-        }
-
-        mLoadStateManager.setState(PagedList.LoadType.END, PagedList.LoadState.LOADING, null);
-        listenTo(PagedList.LoadType.END, mSource.load(new DataSource.Params<>(
-                DataSource.LoadType.END,
-                key,
-                mConfig.initialLoadSizeHint,
-                mConfig.enablePlaceholders,
-                mConfig.pageSize)));
-    }
-
-    void retry() {
-        if (mLoadStateManager.getStart().equals(PagedList.LoadState.RETRYABLE_ERROR)) {
-            schedulePrepend();
-        }
-        if (mLoadStateManager.getEnd().equals(PagedList.LoadState.RETRYABLE_ERROR)) {
-            scheduleAppend();
-        }
-    }
-
-    boolean isDetached() {
-        return mDetached.get();
-    }
-
-    void detach() {
-        mDetached.set(true);
-    }
-
-    static class SimpleAdjacentProvider<V> implements AdjacentProvider<V> {
-        private int mFirstIndex;
-        private int mLastIndex;
-
-        private V mFirstItem;
-        private V mLastItem;
-
-        boolean mCounted;
-        int mLeadingUnloadedCount;
-        int mTrailingUnloadedCount;
-
-        @Override
-        public V getFirstLoadedItem() {
-            return mFirstItem;
-        }
-
-        @Override
-        public V getLastLoadedItem() {
-            return mLastItem;
-        }
-
-        @Override
-        public int getFirstLoadedItemIndex() {
-            return mFirstIndex;
-        }
-
-        @Override
-        public int getLastLoadedItemIndex() {
-            return mLastIndex;
-        }
-
-        @Override
-        public void onPageResultResolution(@NonNull PagedList.LoadType type,
-                @NonNull DataSource.BaseResult<V> result) {
-            if (result.data.isEmpty()) {
-                return;
-            }
-            if (type == PagedList.LoadType.START) {
-                mFirstIndex -= result.data.size();
-                mFirstItem = result.data.get(0);
-                if (mCounted) {
-                    mLeadingUnloadedCount -= result.data.size();
-                }
-            } else if (type == PagedList.LoadType.END) {
-                mLastIndex += result.data.size();
-                mLastItem = result.data.get(result.data.size() - 1);
-                if (mCounted) {
-                    mTrailingUnloadedCount -= result.data.size();
-                }
-            } else {
-                mFirstIndex = result.leadingNulls + result.offset;
-                mLastIndex = mFirstIndex + result.data.size() - 1;
-                mFirstItem = result.data.get(0);
-                mLastItem = result.data.get(result.data.size() - 1);
-
-                if (result.counted) {
-                    mCounted = true;
-                    mLeadingUnloadedCount = result.leadingNulls;
-                    mTrailingUnloadedCount = result.trailingNulls;
-                }
-            }
-        }
-    }
-}
diff --git a/paging/common/src/main/java/androidx/paging/PositionalDataSource.java b/paging/common/src/main/java/androidx/paging/PositionalDataSource.java
deleted file mode 100644
index 3cb0217..0000000
--- a/paging/common/src/main/java/androidx/paging/PositionalDataSource.java
+++ /dev/null
@@ -1,418 +0,0 @@
-/*
- * 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.WorkerThread;
-import androidx.arch.core.util.Function;
-import androidx.concurrent.futures.ResolvableFuture;
-
-import com.google.common.util.concurrent.ListenableFuture;
-
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Position-based data loader for a fixed-size, countable data set, supporting fixed-size loads at
- * arbitrary page positions.
- * <p>
- * Extend PositionalDataSource if you can load pages of a requested size at arbitrary
- * positions, and provide a fixed item count. If your data source can't support loading arbitrary
- * requested page sizes (e.g. when network page size constraints are only known at runtime), either
- * use {@link PageKeyedDataSource} or {@link ItemKeyedDataSource}, or pass the initial result with
- * the two parameter {@link LoadInitialCallback#onResult(List, int)}.
- * <p>
- * Room can generate a Factory of PositionalDataSources for you:
- * <pre>
- * {@literal @}Dao
- * interface UserDao {
- *     {@literal @}Query("SELECT * FROM user ORDER BY mAge DESC")
- *     public abstract DataSource.Factory&lt;Integer, User> loadUsersByAgeDesc();
- * }</pre>
- *
- * @see ListenablePositionalDataSource
- *
- * @param <T> Type of items being loaded by the PositionalDataSource.
- */
-public abstract class PositionalDataSource<T> extends ListenablePositionalDataSource<T> {
-
-    /**
-     * Holder object for inputs to {@link #loadInitial(LoadInitialParams, LoadInitialCallback)}.
-     */
-    public static class LoadInitialParams extends ListenablePositionalDataSource.LoadInitialParams {
-        public LoadInitialParams(
-                int requestedStartPosition,
-                int requestedLoadSize,
-                int pageSize,
-                boolean placeholdersEnabled) {
-            super(requestedStartPosition, requestedLoadSize, pageSize, placeholdersEnabled);
-        }
-    }
-
-    /**
-     * Holder object for inputs to {@link #loadRange(LoadRangeParams, LoadRangeCallback)}.
-     */
-    public static class LoadRangeParams extends ListenablePositionalDataSource.LoadRangeParams {
-        public LoadRangeParams(int startPosition, int loadSize) {
-            super(startPosition, loadSize);
-        }
-    }
-
-    /**
-     * Callback for {@link #loadInitial(LoadInitialParams, LoadInitialCallback)}
-     * to return data, position, and count.
-     * <p>
-     * A callback should be called only once, and may throw if called again.
-     * <p>
-     * It is always valid for a DataSource loading method that takes a callback to stash the
-     * callback and call it later. This enables DataSources to be fully asynchronous, and to handle
-     * temporary, recoverable error states (such as a network error that can be retried).
-     *
-     * @param <T> Type of items being loaded.
-     */
-    public abstract static class LoadInitialCallback<T> {
-        /**
-         * Called to pass initial load state from a DataSource.
-         * <p>
-         * Call this method from your DataSource's {@code loadInitial} function to return data,
-         * and inform how many placeholders should be shown before and after. If counting is cheap
-         * to compute (for example, if a network load returns the information regardless), it's
-         * recommended to pass the total size to the totalCount parameter. If placeholders are not
-         * requested (when {@link LoadInitialParams#placeholdersEnabled} is false), you can instead
-         * call {@link #onResult(List, int)}.
-         *
-         * @param data List of items loaded from the DataSource. If this is empty, the DataSource
-         *             is treated as empty, and no further loads will occur.
-         * @param position Position of the item at the front of the list. If there are {@code N}
-         *                 items before the items in data that can be loaded from this DataSource,
-         *                 pass {@code N}.
-         * @param totalCount Total number of items that may be returned from this DataSource.
-         *                   Includes the number in the initial {@code data} parameter
-         *                   as well as any items that can be loaded in front or behind of
-         *                   {@code data}.
-         */
-        public abstract void onResult(@NonNull List<T> data, int position, int totalCount);
-
-        /**
-         * Called to pass initial load state from a DataSource without total count,
-         * when placeholders aren't requested.
-         * <p class="note"><strong>Note:</strong> This method can only be called when placeholders
-         * are disabled ({@link LoadInitialParams#placeholdersEnabled} is false).
-         * <p>
-         * Call this method from your DataSource's {@code loadInitial} function to return data,
-         * if position is known but total size is not. If placeholders are requested, call the three
-         * parameter variant: {@link #onResult(List, int, int)}.
-         *
-         * @param data List of items loaded from the DataSource. If this is empty, the DataSource
-         *             is treated as empty, and no further loads will occur.
-         * @param position Position of the item at the front of the list. If there are {@code N}
-         *                 items before the items in data that can be provided by this DataSource,
-         *                 pass {@code N}.
-         */
-        public abstract void onResult(@NonNull List<T> data, int position);
-
-        /**
-         * Called to report an error from a DataSource.
-         * <p>
-         * Call this method to report an error from
-         * {@link #loadInitial(LoadInitialParams, LoadInitialCallback)}.
-         *
-         * @param error The error that occurred during loading.
-         */
-        public void onError(@NonNull Throwable error) {
-            // TODO: remove default implementation in 3.0
-            throw new IllegalStateException(
-                    "You must implement onError if implementing your own load callback");
-        }
-    }
-
-    /**
-     * Callback for PositionalDataSource {@link #loadRange(LoadRangeParams, LoadRangeCallback)}
-     * to return data.
-     * <p>
-     * A callback should be called only once, and may throw if called again.
-     * <p>
-     * It is always valid for a DataSource loading method that takes a callback to stash the
-     * callback and call it later. This enables DataSources to be fully asynchronous, and to handle
-     * temporary, recoverable error states (such as a network error that can be retried).
-     *
-     * @param <T> Type of items being loaded.
-     */
-    public abstract static class LoadRangeCallback<T> {
-        /**
-         * Called to pass loaded data from {@link #loadRange(LoadRangeParams, LoadRangeCallback)}.
-         *
-         * @param data List of items loaded from the DataSource. Must be same size as requested,
-         *             unless at end of list.
-         */
-        public abstract void onResult(@NonNull List<T> data);
-
-        /**
-         * Called to report an error from a DataSource.
-         * <p>
-         * Call this method to report an error from
-         * {@link #loadRange(LoadRangeParams, LoadRangeCallback)}.
-         *
-         * @param error The error that occurred during loading.
-         */
-        public void onError(@NonNull Throwable error) {
-            // TODO: remove default implementation in 3.0
-            throw new IllegalStateException(
-                    "You must implement onError if implementing your own load callback");
-        }
-    }
-
-    @NonNull
-    @Override
-    public final ListenableFuture<InitialResult<T>> loadInitial(
-            @NonNull final ListenablePositionalDataSource.LoadInitialParams params) {
-        final ResolvableFuture<InitialResult<T>> future = ResolvableFuture.create();
-        getExecutor().execute(new Runnable() {
-            @Override
-            public void run() {
-                final LoadInitialParams newParams = new LoadInitialParams(
-                        params.requestedStartPosition,
-                        params.requestedLoadSize,
-                        params.pageSize,
-                        params.placeholdersEnabled);
-                LoadInitialCallback<T> callback = new LoadInitialCallback<T>() {
-                    @Override
-                    public void onResult(@NonNull List<T> data, int position, int totalCount) {
-                        if (isInvalid()) {
-                            // NOTE: this isInvalid() check works around
-                            // https://issuetracker.google.com/issues/124511903
-                            future.set(new InitialResult<>(Collections.<T>emptyList(), 0, 0));
-                        } else {
-                            setFuture(newParams, new InitialResult<>(data, position, totalCount));
-                        }
-                    }
-
-                    @Override
-                    public void onResult(@NonNull List<T> data, int position) {
-                        if (isInvalid()) {
-                            // NOTE: this isInvalid() check works around
-                            // https://issuetracker.google.com/issues/124511903
-                            future.set(new InitialResult<>(Collections.<T>emptyList(), 0));
-                        } else {
-                            setFuture(newParams, new InitialResult<>(data, position));
-                        }
-                    }
-
-                    private void setFuture(
-                            @NonNull ListenablePositionalDataSource.LoadInitialParams params,
-                            @NonNull InitialResult<T> result) {
-                        if (params.placeholdersEnabled) {
-                            result.validateForInitialTiling(params.pageSize);
-                        }
-                        future.set(result);
-                    }
-
-
-                    @Override
-                    public void onError(@NonNull Throwable error) {
-                        future.setException(error);
-                    }
-                };
-                loadInitial(newParams,
-                        callback);
-            }
-        });
-        return future;
-    }
-
-    @NonNull
-    @Override
-    public final ListenableFuture<RangeResult<T>> loadRange(
-            final @NonNull ListenablePositionalDataSource.LoadRangeParams params) {
-        final ResolvableFuture<RangeResult<T>> future = ResolvableFuture.create();
-        getExecutor().execute(new Runnable() {
-            @Override
-            public void run() {
-                LoadRangeCallback<T> callback = new LoadRangeCallback<T>() {
-                    @Override
-                    public void onResult(@NonNull List<T> data) {
-                        if (isInvalid()) {
-                            future.set(new RangeResult<>(Collections.<T>emptyList()));
-                        } else {
-                            future.set(new RangeResult<>(data));
-                        }
-                    }
-
-                    @Override
-                    public void onError(@NonNull Throwable error) {
-                        future.setException(error);
-                    }
-                };
-                loadRange(new LoadRangeParams(
-                                params.startPosition,
-                                params.loadSize),
-                        callback);
-            }
-        });
-        return future;
-    }
-
-    /**
-     * Load initial list data.
-     * <p>
-     * This method is called to load the initial page(s) from the DataSource.
-     * <p>
-     * Result list must be a multiple of pageSize to enable efficient tiling.
-     *
-     * @param params Parameters for initial load, including requested start position, load size, and
-     *               page size.
-     * @param callback Callback that receives initial load data, including
-     *                 position and total data set size.
-     */
-    @WorkerThread
-    public abstract void loadInitial(
-            @NonNull LoadInitialParams params,
-            @NonNull LoadInitialCallback<T> callback);
-
-    /**
-     * Called to load a range of data from the DataSource.
-     * <p>
-     * This method is called to load additional pages from the DataSource after the
-     * LoadInitialCallback passed to dispatchLoadInitial has initialized a PagedList.
-     * <p>
-     * Unlike {@link #loadInitial(LoadInitialParams, LoadInitialCallback)}, this method must return
-     * the number of items requested, at the position requested.
-     *
-     * @param params Parameters for load, including start position and load size.
-     * @param callback Callback that receives loaded data.
-     */
-    @WorkerThread
-    public abstract void loadRange(@NonNull LoadRangeParams params,
-            @NonNull LoadRangeCallback<T> callback);
-
-    @Override
-    boolean isContiguous() {
-        return false;
-    }
-
-    /**
-     * Helper for computing an initial position in
-     * {@link #loadInitial(LoadInitialParams, LoadInitialCallback)} when total data set size can be
-     * computed ahead of loading.
-     * <p>
-     * The value computed by this function will do bounds checking, page alignment, and positioning
-     * based on initial load size requested.
-     * <p>
-     * Example usage in a PositionalDataSource subclass:
-     * <pre>
-     * class ItemDataSource extends PositionalDataSource&lt;Item> {
-     *     private int computeCount() {
-     *         // actual count code here
-     *     }
-     *
-     *     private List&lt;Item> loadRangeInternal(int startPosition, int loadCount) {
-     *         // actual load code here
-     *     }
-     *
-     *     {@literal @}Override
-     *     public void loadInitial({@literal @}NonNull LoadInitialParams params,
-     *             {@literal @}NonNull LoadInitialCallback&lt;Item> callback) {
-     *         int totalCount = computeCount();
-     *         int position = computeInitialLoadPosition(params, totalCount);
-     *         int loadSize = computeInitialLoadSize(params, position, totalCount);
-     *         callback.onResult(loadRangeInternal(position, loadSize), position, totalCount);
-     *     }
-     *
-     *     {@literal @}Override
-     *     public void loadRange({@literal @}NonNull LoadRangeParams params,
-     *             {@literal @}NonNull LoadRangeCallback&lt;Item> callback) {
-     *         callback.onResult(loadRangeInternal(params.startPosition, params.loadSize));
-     *     }
-     * }</pre>
-     *
-     * @param params Params passed to {@link #loadInitial(LoadInitialParams, LoadInitialCallback)},
-     *               including page size, and requested start/loadSize.
-     * @param totalCount Total size of the data set.
-     * @return Position to start loading at.
-     *
-     *
-     * @see #computeInitialLoadSize(LoadInitialParams, int, int)
-     */
-    public static int computeInitialLoadPosition(
-            @NonNull LoadInitialParams params, int totalCount) {
-        return ListenablePositionalDataSource.computeInitialLoadPosition(params, totalCount);
-    }
-
-    /**
-     * Helper for computing an initial load size in
-     * {@link #loadInitial(LoadInitialParams, LoadInitialCallback)} when total data set size can be
-     * computed ahead of loading.
-     * <p>
-     * This function takes the requested load size, and bounds checks it against the value returned
-     * by
-     * {@link #computeInitialLoadPosition(LoadInitialParams, int)}.
-     * <p>
-     * Example usage in a PositionalDataSource subclass:
-     * <pre>
-     * class ItemDataSource extends PositionalDataSource&lt;Item> {
-     *     private int computeCount() {
-     *         // actual count code here
-     *     }
-     *
-     *     private List&lt;Item> loadRangeInternal(int startPosition, int loadCount) {
-     *         // actual load code here
-     *     }
-     *
-     *     {@literal @}Override
-     *     public void loadInitial({@literal @}NonNull LoadInitialParams params,
-     *             {@literal @}NonNull LoadInitialCallback&lt;Item> callback) {
-     *         int totalCount = computeCount();
-     *         int position = computeInitialLoadPosition(params, totalCount);
-     *         int loadSize = computeInitialLoadSize(params, position, totalCount);
-     *         callback.onResult(loadRangeInternal(position, loadSize), position, totalCount);
-     *     }
-     *
-     *     {@literal @}Override
-     *     public void loadRange({@literal @}NonNull LoadRangeParams params,
-     *             {@literal @}NonNull LoadRangeCallback&lt;Item> callback) {
-     *         callback.onResult(loadRangeInternal(params.startPosition, params.loadSize));
-     *     }
-     * }</pre>
-     *
-     * @param params Params passed to {@link #loadInitial(LoadInitialParams, LoadInitialCallback)},
-     *               including page size, and requested start/loadSize.
-     * @param initialLoadPosition Value returned by
-     *   {@link #computeInitialLoadPosition(LoadInitialParams, int)}
-     * @param totalCount Total size of the data set.
-     * @return Number of items to load.
-     *
-     * @see #computeInitialLoadPosition(LoadInitialParams, int)
-     */
-    public static int computeInitialLoadSize(
-            @NonNull LoadInitialParams params, int initialLoadPosition, int totalCount) {
-        return ListenablePositionalDataSource.computeInitialLoadSize(params, initialLoadPosition,
-                totalCount);
-    }
-
-    @NonNull
-    @Override
-    public final <V> PositionalDataSource<V> mapByPage(
-            @NonNull Function<List<T>, List<V>> function) {
-        return new WrapperPositionalDataSource<>(this, function);
-    }
-
-    @NonNull
-    @Override
-    public final <V> PositionalDataSource<V> map(@NonNull Function<T, V> function) {
-        return mapByPage(createListFunction(function));
-    }
-}
diff --git a/paging/common/src/main/java/androidx/paging/SnapshotPagedList.java b/paging/common/src/main/java/androidx/paging/SnapshotPagedList.java
deleted file mode 100644
index a3e5a12..0000000
--- a/paging/common/src/main/java/androidx/paging/SnapshotPagedList.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * 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;
-
-class SnapshotPagedList<T> extends PagedList<T> {
-    private final boolean mContiguous;
-    private final Object mLastKey;
-    private final DataSource<?, T> mDataSource;
-
-    SnapshotPagedList(@NonNull PagedList<T> pagedList) {
-        super(pagedList.mStorage.snapshot(),
-                pagedList.mMainThreadExecutor,
-                pagedList.mBackgroundThreadExecutor,
-                null,
-                pagedList.mConfig);
-        mDataSource = pagedList.getDataSource();
-        mContiguous = pagedList.isContiguous();
-        mLastLoad = pagedList.mLastLoad;
-        mLastKey = pagedList.getLastKey();
-    }
-
-    @Override
-    public boolean isImmutable() {
-        return true;
-    }
-
-    @Override
-    public void detach() {}
-
-    @Override
-    public boolean isDetached() {
-        return true;
-    }
-
-    @Override
-    boolean isContiguous() {
-        return mContiguous;
-    }
-
-    @Nullable
-    @Override
-    public Object getLastKey() {
-        return mLastKey;
-    }
-
-    @NonNull
-    @Override
-    public DataSource<?, T> getDataSource() {
-        return mDataSource;
-    }
-
-    @Override
-    void dispatchUpdatesSinceSnapshot(@NonNull PagedList<T> storageSnapshot,
-            @NonNull Callback callback) {
-    }
-
-    @Override
-    void dispatchCurrentLoadState(LoadStateListener listener) {
-    }
-
-    @Override
-    void loadAroundInternal(int index) {
-    }
-}
diff --git a/paging/common/src/main/java/androidx/paging/TiledDataSource.java b/paging/common/src/main/java/androidx/paging/TiledDataSource.java
deleted file mode 100644
index 7c45879..0000000
--- a/paging/common/src/main/java/androidx/paging/TiledDataSource.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * 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.annotation.RestrictTo;
-import androidx.annotation.WorkerThread;
-
-import java.util.Collections;
-import java.util.List;
-
-// NOTE: Room 1.0 depends on this class, so it should not be removed until
-// we can require a version of Room that uses PositionalDataSource directly
-/**
- * @param <T> Type loaded by the TiledDataSource.
- *
- * @deprecated Use {@link PositionalDataSource}
- * @hide
- */
-@SuppressWarnings("DeprecatedIsStillUsed")
-@Deprecated
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
-public abstract class TiledDataSource<T> extends PositionalDataSource<T> {
-
-    @WorkerThread
-    public abstract int countItems();
-
-    @Override
-    boolean isContiguous() {
-        return false;
-    }
-
-    @Nullable
-    @WorkerThread
-    public abstract List<T> loadRange(int startPosition, int count);
-
-    @Override
-    public void loadInitial(@NonNull LoadInitialParams params,
-            @NonNull LoadInitialCallback<T> callback) {
-        int totalCount = countItems();
-        if (totalCount == 0) {
-            callback.onResult(Collections.<T>emptyList(), 0, 0);
-            return;
-        }
-
-        // bound the size requested, based on known count
-        final int firstLoadPosition = computeInitialLoadPosition(params, totalCount);
-        final int firstLoadSize = computeInitialLoadSize(params, firstLoadPosition, totalCount);
-
-        // convert from legacy behavior
-        List<T> list = loadRange(firstLoadPosition, firstLoadSize);
-        if (list != null && list.size() == firstLoadSize) {
-            callback.onResult(list, firstLoadPosition, totalCount);
-        } else {
-            // null list, or size doesn't match request
-            // The size check is a WAR for Room 1.0, subsequent versions do the check in Room
-            invalidate();
-        }
-    }
-
-    @Override
-    public void loadRange(@NonNull LoadRangeParams params,
-            @NonNull LoadRangeCallback<T> callback) {
-        List<T> list = loadRange(params.startPosition, params.loadSize);
-        if (list != null) {
-            callback.onResult(list);
-        } else {
-            invalidate();
-        }
-    }
-}
diff --git a/paging/common/src/main/java/androidx/paging/WrapperDataSource.java b/paging/common/src/main/java/androidx/paging/WrapperDataSource.java
deleted file mode 100644
index d8c4df4..0000000
--- a/paging/common/src/main/java/androidx/paging/WrapperDataSource.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * 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.
- */
-
-package androidx.paging;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.arch.core.util.Function;
-import androidx.paging.futures.DirectExecutor;
-import androidx.paging.futures.Futures;
-
-import com.google.common.util.concurrent.ListenableFuture;
-
-import java.util.IdentityHashMap;
-import java.util.List;
-
-/**
- * @param <Key> DataSource key type, same for original and wrapped.
- * @param <ValueFrom> Value type of original DataSource.
- * @param <ValueTo> Value type of new DataSource.
- */
-class WrapperDataSource<Key, ValueFrom, ValueTo> extends DataSource<Key, ValueTo> {
-    private final DataSource<Key, ValueFrom> mSource;
-
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
-    final Function<List<ValueFrom>, List<ValueTo>> mListFunction;
-
-
-    private final IdentityHashMap<ValueTo, Key> mKeyMap;
-
-    WrapperDataSource(@NonNull DataSource<Key, ValueFrom> source,
-            @NonNull Function<List<ValueFrom>, List<ValueTo>> listFunction) {
-        super(source.mType);
-        mSource = source;
-        mListFunction = listFunction;
-        mKeyMap = source.mType == KeyType.ITEM_KEYED ? new IdentityHashMap<ValueTo, Key>() : null;
-    }
-
-    @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();
-    }
-
-    @Nullable
-    @Override
-    Key getKey(@NonNull ValueTo item) {
-        if (mKeyMap != null) {
-            synchronized (mKeyMap) {
-                return mKeyMap.get(item);
-            }
-        }
-        // positional / page-keyed
-        return null;
-    }
-
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
-    void stashKeysIfNeeded(@NonNull List<ValueFrom> source, @NonNull List<ValueTo> dest) {
-        if (mKeyMap != null) {
-            synchronized (mKeyMap) {
-                for (int i = 0; i < dest.size(); i++) {
-                    mKeyMap.put(dest.get(i), mSource.getKey(source.get(i)));
-                }
-            }
-        }
-    }
-
-    @Override
-    final ListenableFuture<? extends BaseResult> load(@NonNull Params params) {
-        //noinspection unchecked
-        return Futures.transform(
-                mSource.load(params),
-                new Function<BaseResult<ValueFrom>, BaseResult<ValueTo>>() {
-                    @Override
-                    public BaseResult<ValueTo> apply(BaseResult<ValueFrom> input) {
-                        BaseResult<ValueTo> result = new BaseResult<>(input, mListFunction);
-                        stashKeysIfNeeded(input.data, result.data);
-                        return result;
-                    }
-                },
-                DirectExecutor.INSTANCE);
-    }
-}
diff --git a/paging/common/src/main/java/androidx/paging/WrapperItemKeyedDataSource.java b/paging/common/src/main/java/androidx/paging/WrapperItemKeyedDataSource.java
deleted file mode 100644
index 3a1b5c9..0000000
--- a/paging/common/src/main/java/androidx/paging/WrapperItemKeyedDataSource.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * 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.arch.core.util.Function;
-
-import java.util.IdentityHashMap;
-import java.util.List;
-
-class WrapperItemKeyedDataSource<K, A, B> extends ItemKeyedDataSource<K, B> {
-    private final ItemKeyedDataSource<K, A> mSource;
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
-    final Function<List<A>, List<B>> mListFunction;
-
-    private final IdentityHashMap<B, K> mKeyMap = new IdentityHashMap<>();
-
-    WrapperItemKeyedDataSource(ItemKeyedDataSource<K, A> source,
-            Function<List<A>, List<B>> listFunction) {
-        mSource = source;
-        mListFunction = listFunction;
-    }
-
-    @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();
-    }
-
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
-    List<B> convertWithStashedKeys(List<A> source) {
-        List<B> dest = convert(mListFunction, source);
-        synchronized (mKeyMap) {
-            // synchronize on mKeyMap, since multiple loads may occur simultaneously.
-            // Note: manually sync avoids locking per-item (e.g. Collections.synchronizedMap)
-            for (int i = 0; i < dest.size(); i++) {
-                mKeyMap.put(dest.get(i), mSource.getKey(source.get(i)));
-            }
-        }
-        return dest;
-    }
-
-    @Override
-    public void loadInitial(@NonNull LoadInitialParams<K> params,
-            final @NonNull LoadInitialCallback<B> callback) {
-        mSource.loadInitial(params, new LoadInitialCallback<A>() {
-            @Override
-            public void onResult(@NonNull List<A> data, int position, int totalCount) {
-                callback.onResult(convertWithStashedKeys(data), position, totalCount);
-            }
-
-            @Override
-            public void onResult(@NonNull List<A> data) {
-                callback.onResult(convertWithStashedKeys(data));
-            }
-
-            @Override
-            public void onError(@NonNull Throwable error) {
-                callback.onError(error);
-            }
-        });
-    }
-
-    @Override
-    public void loadAfter(@NonNull LoadParams<K> params,
-            final @NonNull LoadCallback<B> callback) {
-        mSource.loadAfter(params, new LoadCallback<A>() {
-            @Override
-            public void onResult(@NonNull List<A> data) {
-                callback.onResult(convertWithStashedKeys(data));
-            }
-
-            @Override
-            public void onError(@NonNull Throwable error) {
-                callback.onError(error);
-            }
-        });
-    }
-
-    @Override
-    public void loadBefore(@NonNull LoadParams<K> params,
-            final @NonNull LoadCallback<B> callback) {
-        mSource.loadBefore(params, new LoadCallback<A>() {
-            @Override
-            public void onResult(@NonNull List<A> data) {
-                callback.onResult(convertWithStashedKeys(data));
-            }
-
-            @Override
-            public void onError(@NonNull Throwable error) {
-                callback.onError(error);
-            }
-        });
-    }
-
-    @NonNull
-    @Override
-    public K getKey(@NonNull B item) {
-        synchronized (mKeyMap) {
-            return mKeyMap.get(item);
-        }
-    }
-}
diff --git a/paging/common/src/main/java/androidx/paging/WrapperPageKeyedDataSource.java b/paging/common/src/main/java/androidx/paging/WrapperPageKeyedDataSource.java
deleted file mode 100644
index 3ffc79d..0000000
--- a/paging/common/src/main/java/androidx/paging/WrapperPageKeyedDataSource.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * 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.util.Function;
-
-import java.util.List;
-
-class WrapperPageKeyedDataSource<K, A, B> extends PageKeyedDataSource<K, B> {
-    private final PageKeyedDataSource<K, A> mSource;
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
-    final Function<List<A>, List<B>> mListFunction;
-
-    WrapperPageKeyedDataSource(PageKeyedDataSource<K, A> source,
-            Function<List<A>, List<B>> listFunction) {
-        mSource = source;
-        mListFunction = listFunction;
-    }
-
-    @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
-    public void loadInitial(@NonNull LoadInitialParams<K> params,
-            final @NonNull LoadInitialCallback<K, B> callback) {
-        mSource.loadInitial(params, new LoadInitialCallback<K, A>() {
-            @Override
-            public void onResult(@NonNull List<A> data, int position, int totalCount,
-                    @Nullable K previousPageKey, @Nullable K nextPageKey) {
-                callback.onResult(convert(mListFunction, data), position, totalCount,
-                        previousPageKey, nextPageKey);
-            }
-
-            @Override
-            public void onResult(@NonNull List<A> data, @Nullable K previousPageKey,
-                    @Nullable K nextPageKey) {
-                callback.onResult(convert(mListFunction, data), previousPageKey, nextPageKey);
-            }
-
-            @Override
-            public void onError(@NonNull Throwable error) {
-                callback.onError(error);
-            }
-        });
-    }
-
-    @Override
-    public void loadBefore(@NonNull LoadParams<K> params,
-            final @NonNull LoadCallback<K, B> callback) {
-        mSource.loadBefore(params, new LoadCallback<K, A>() {
-            @Override
-            public void onResult(@NonNull List<A> data, @Nullable K adjacentPageKey) {
-                callback.onResult(convert(mListFunction, data), adjacentPageKey);
-            }
-
-            @Override
-            public void onError(@NonNull Throwable error) {
-                callback.onError(error);
-            }
-        });
-    }
-
-    @Override
-    public void loadAfter(@NonNull LoadParams<K> params,
-            final @NonNull LoadCallback<K, B> callback) {
-        mSource.loadAfter(params, new LoadCallback<K, A>() {
-            @Override
-            public void onResult(@NonNull List<A> data, @Nullable K adjacentPageKey) {
-                callback.onResult(convert(mListFunction, data), adjacentPageKey);
-            }
-
-            @Override
-            public void onError(@NonNull Throwable error) {
-                callback.onError(error);
-            }
-        });
-    }
-}
diff --git a/paging/common/src/main/java/androidx/paging/WrapperPositionalDataSource.java b/paging/common/src/main/java/androidx/paging/WrapperPositionalDataSource.java
deleted file mode 100644
index fdc2b9a..0000000
--- a/paging/common/src/main/java/androidx/paging/WrapperPositionalDataSource.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * 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.arch.core.util.Function;
-
-import java.util.List;
-
-class WrapperPositionalDataSource<A, B> extends PositionalDataSource<B> {
-    private final PositionalDataSource<A> mSource;
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
-    final Function<List<A>, List<B>> mListFunction;
-
-    WrapperPositionalDataSource(PositionalDataSource<A> source,
-            Function<List<A>, List<B>> listFunction) {
-        mSource = source;
-        mListFunction = listFunction;
-    }
-
-    @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
-    public void loadInitial(@NonNull LoadInitialParams params,
-            final @NonNull LoadInitialCallback<B> callback) {
-        mSource.loadInitial(params, new LoadInitialCallback<A>() {
-            @Override
-            public void onResult(@NonNull List<A> data, int position, int totalCount) {
-                callback.onResult(convert(mListFunction, data), position, totalCount);
-            }
-
-            @Override
-            public void onResult(@NonNull List<A> data, int position) {
-                callback.onResult(convert(mListFunction, data), position);
-            }
-
-            @Override
-            public void onError(@NonNull Throwable error) {
-                callback.onError(error);
-            }
-        });
-    }
-
-    @Override
-    public void loadRange(@NonNull LoadRangeParams params,
-            final @NonNull LoadRangeCallback<B> callback) {
-        mSource.loadRange(params, new LoadRangeCallback<A>() {
-            @Override
-            public void onResult(@NonNull List<A> data) {
-                callback.onResult(convert(mListFunction, data));
-            }
-
-            @Override
-            public void onError(@NonNull Throwable error) {
-                callback.onError(error);
-            }
-        });
-    }
-}
diff --git a/paging/common/src/main/java/androidx/paging/futures/DirectExecutor.java b/paging/common/src/main/java/androidx/paging/futures/DirectExecutor.java
deleted file mode 100644
index 46eeb29..0000000
--- a/paging/common/src/main/java/androidx/paging/futures/DirectExecutor.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * 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.
- */
-
-package androidx.paging.futures;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
-
-import java.util.concurrent.Executor;
-
-/**
- * Executor that runs each task in the thread that invokes {@link Executor#execute execute}
- * @hide
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class DirectExecutor implements Executor {
-    /**
-     * Returns an {@link Executor} that runs each task in the thread that invokes {@link
-     * Executor#execute execute}.
-     *
-     * <p>This instance is equivalent to:
-     *
-     * <pre>{@code
-     * final class DirectExecutor implements Executor {
-     *   public void execute(Runnable r) {
-     *     r.run();
-     *   }
-     * }
-     * }</pre>
-     */
-    @NonNull
-    public static DirectExecutor INSTANCE = new DirectExecutor();
-
-    private DirectExecutor() {}
-    @Override
-    public void execute(@NonNull Runnable runnable) {
-        runnable.run();
-    }
-}
diff --git a/paging/common/src/main/java/androidx/paging/futures/FutureCallback.java b/paging/common/src/main/java/androidx/paging/futures/FutureCallback.java
deleted file mode 100644
index 7d4aa0d..0000000
--- a/paging/common/src/main/java/androidx/paging/futures/FutureCallback.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * 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.
- */
-
-package androidx.paging.futures;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
-
-import com.google.common.util.concurrent.ListenableFuture;
-
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Future;
-
-/**
- * A callback for accepting the results of a {@link Future} computation asynchronously.
- *
- * <p>To attach to a {@link ListenableFuture} use {@link Futures#addCallback}.
- * @param <V> Type of the Future result.
- * @hide
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public interface FutureCallback<V> {
-    /** Invoked with the result of the {@code Future} computation when it is successful. */
-    @SuppressWarnings("UnknownNullness")
-    void onSuccess(V value);
-
-    /**
-     * Invoked when a {@code Future} computation fails or is canceled.
-     *
-     * <p>If the future's {@link Future#get() get} method throws an {@link ExecutionException}, then
-     * the cause is passed to this method. Any other thrown object is passed unaltered.
-     */
-    void onError(@NonNull Throwable throwable);
-}
diff --git a/paging/common/src/main/java/androidx/paging/futures/Futures.java b/paging/common/src/main/java/androidx/paging/futures/Futures.java
deleted file mode 100644
index 4ad52f8..0000000
--- a/paging/common/src/main/java/androidx/paging/futures/Futures.java
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * 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.
- */
-
-package androidx.paging.futures;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
-import androidx.arch.core.util.Function;
-import androidx.concurrent.futures.ResolvableFuture;
-
-import com.google.common.util.concurrent.ListenableFuture;
-
-import java.util.concurrent.CancellationException;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Executor;
-
-/**
- * @hide
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class Futures {
-    private Futures() {}
-  /**
-   * Registers separate success and failure callbacks to be run when the {@code Future}'s
-   * computation is complete or, if the computation is already complete, immediately.
-   *
-   * <p>The callback is run on {@code executor}. There is no guaranteed ordering of execution of
-   * callbacks, but any callback added through this method is guaranteed to be called once the
-   * computation is complete.
-   *
-   * <p>Example:
-   *
-   * <pre>{@code
-   * ListenableFuture<QueryResult> future = ...;
-   * Executor e = ...
-   * addCallback(future,
-   *     new FutureCallback<QueryResult>() {
-   *       public void onSuccess(QueryResult result) {
-   *         storeInCache(result);
-   *       }
-   *       public void onFailure(Throwable t) {
-   *         reportError(t);
-   *       }
-   *     }, e);
-   * }</pre>
-   *
-   * <p>When selecting an executor, note that {@code directExecutor} is dangerous in some cases. See
-   * the discussion in the {@link ListenableFuture#addListener ListenableFuture.addListener}
-   * documentation. All its warnings about heavyweight listeners are also applicable to heavyweight
-   * callbacks passed to this method.
-   *
-   * <p>For a more general interface to attach a completion listener to a {@code Future}, see {@link
-   * ListenableFuture#addListener addListener}.
-   *
-   * @param future The future attach the callback to.
-   * @param callback The callback to invoke when {@code future} is completed.
-   * @param executor The executor to run {@code callback} when the future completes.
-   */
-    public static <V> void addCallback(@NonNull final ListenableFuture<V> future,
-            @NonNull final FutureCallback<? super V> callback, @NonNull Executor executor) {
-        future.addListener(new Runnable() {
-            @Override
-            public void run() {
-                final V value;
-                try {
-                    value = future.get();
-                } catch (ExecutionException e) {
-                    callback.onError(e.getCause());
-                    return;
-                } catch (Throwable e) {
-                    callback.onError(e);
-                    return;
-                }
-                callback.onSuccess(value);
-            }
-        }, executor);
-    }
-
-    /**
-     * Returns a new {@code Future} whose result is derived from the result of the given {@code
-     * Future}. If {@code input} fails, the returned {@code Future} fails with the same exception
-     * (and the function is not invoked). Example usage:
-     *
-     * <pre>{@code
-     * ListenableFuture<QueryResult> queryFuture = ...;
-     * ListenableFuture<List<Row>> rowsFuture =
-     *     transform(queryFuture, QueryResult::getRows, executor);
-     * }</pre>
-     *
-     * <p>When selecting an executor, note that {@code directExecutor} is dangerous in some cases.
-     * See the discussion in the {@link ListenableFuture#addListener ListenableFuture.addListener}
-     * documentation. All its warnings about heavyweight listeners are also applicable to
-     * heavyweight functions passed to this method.
-     *
-     * <p>The returned {@code Future} attempts to keep its cancellation state in sync with that of
-     * the input future. That is, if the returned {@code Future} is cancelled, it will attempt to
-     * cancel the input, and if the input is cancelled, the returned {@code Future} will receive a
-     * callback in which it will attempt to cancel itself.
-     *
-     * <p>An example use of this method is to convert a serializable object returned from an RPC
-     * into a POJO.
-     *
-     * @param input The future to transform
-     * @param function A Function to transform the results of the provided future to the results of
-     *     the returned future.
-     * @param executor Executor to run the function in.
-     * @return A future that holds result of the transformation.
-     */
-    @NonNull
-    public static <I, O> ListenableFuture<O> transform(
-            @NonNull final ListenableFuture<I> input,
-            @NonNull final Function<? super I, ? extends O> function,
-            @NonNull final Executor executor) {
-        final ResolvableFuture<O> out = ResolvableFuture.create();
-
-        // add success/error callback
-        addCallback(input, new FutureCallback<I>() {
-            @Override
-            public void onSuccess(I value) {
-                out.set(function.apply(value));
-            }
-
-            @Override
-            public void onError(@NonNull Throwable throwable) {
-                out.setException(throwable);
-            }
-        }, executor);
-
-        // propagate output future's cancellation to input future
-        addCallback(out, new FutureCallback<O>() {
-            @Override
-            public void onSuccess(O value) {}
-
-            @Override
-            public void onError(@NonNull Throwable throwable) {
-                if (throwable instanceof CancellationException) {
-                    input.cancel(false);
-                }
-            }
-        }, executor);
-        return out;
-    }
-}
diff --git a/paging/common/src/main/kotlin/androidx/paging/ContiguousPagedList.kt b/paging/common/src/main/kotlin/androidx/paging/ContiguousPagedList.kt
new file mode 100644
index 0000000..1574a84
--- /dev/null
+++ b/paging/common/src/main/kotlin/androidx/paging/ContiguousPagedList.kt
@@ -0,0 +1,351 @@
+/*
+ * 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.
+ */
+
+package androidx.paging
+
+import androidx.annotation.MainThread
+import androidx.annotation.RestrictTo
+import java.util.concurrent.Executor
+
+/**
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+open class ContiguousPagedList<K : Any, V : Any>(
+    override val dataSource: DataSource<K, V>,
+    mainThreadExecutor: Executor,
+    backgroundThreadExecutor: Executor,
+    boundaryCallback: BoundaryCallback<V>?,
+    config: Config,
+    initialResult: DataSource.BaseResult<V>,
+    lastLoad: Int
+) : PagedList<V>(
+    PagedStorage<V>(),
+    mainThreadExecutor,
+    backgroundThreadExecutor,
+    boundaryCallback,
+    config
+), PagedStorage.Callback, Pager.PageConsumer<V> {
+    internal companion object {
+        internal const val LAST_LOAD_UNSPECIFIED = -1
+
+        internal fun getPrependItemsRequested(
+            prefetchDistance: Int,
+            index: Int,
+            leadingNulls: Int
+        ) = prefetchDistance - (index - leadingNulls)
+
+        internal fun getAppendItemsRequested(
+            prefetchDistance: Int,
+            index: Int,
+            itemsBeforeTrailingNulls: Int
+        ) = index + prefetchDistance + 1 - itemsBeforeTrailingNulls
+    }
+
+    @Suppress("MemberVisibilityCanBePrivate") /* synthetic access */
+    var prependItemsRequested = 0
+
+    @Suppress("MemberVisibilityCanBePrivate") /* synthetic access */
+    var appendItemsRequested = 0
+
+    @Suppress("MemberVisibilityCanBePrivate") /* synthetic access */
+    var replacePagesWithNulls = false
+
+    @Suppress("MemberVisibilityCanBePrivate") /* synthetic access */
+    val shouldTrim: Boolean
+
+    private val pager: Pager<K, V>
+
+    override val isDetached
+        get() = pager.isDetached
+
+    override val isContiguous = true
+
+    override val lastKey
+        get() = when (dataSource.type) {
+            DataSource.KeyType.POSITIONAL -> {
+                @Suppress("UNCHECKED_CAST")
+                lastLoad as K
+            }
+            else -> lastItem?.let { dataSource.getKeyInternal(it) }
+        }
+
+    /**
+     * Given a page result, apply or drop it, and return whether more loading is needed.
+     */
+    override fun onPageResult(type: LoadType, pageResult: DataSource.BaseResult<V>): Boolean {
+        var continueLoading = false
+        val page = pageResult.data
+
+        // if we end up trimming, we trim from side that's furthest from most recent access
+        val trimFromFront = lastLoad > storage.middleOfLoadedRange
+
+        // is the new page big enough to warrant pre-trimming (i.e. dropping) it?
+        val skipNewPage = shouldTrim && storage.shouldPreTrimNewPage(
+            config.maxSize,
+            requiredRemainder,
+            page.size
+        )
+
+        if (type == LoadType.END) {
+            if (skipNewPage && !trimFromFront) {
+                // don't append this data, drop it
+                appendItemsRequested = 0
+            } else {
+                storage.appendPage(page, this@ContiguousPagedList)
+                appendItemsRequested -= page.size
+                if (appendItemsRequested > 0 && page.size != 0) {
+                    continueLoading = true
+                }
+            }
+        } else if (type == LoadType.START) {
+            if (skipNewPage && trimFromFront) {
+                // don't append this data, drop it
+                prependItemsRequested = 0
+            } else {
+                storage.prependPage(page, this@ContiguousPagedList)
+                prependItemsRequested -= page.size
+                if (prependItemsRequested > 0 && page.size != 0) {
+                    continueLoading = true
+                }
+            }
+        } else {
+            throw IllegalArgumentException("unexpected result type $type")
+        }
+
+        if (shouldTrim) {
+            // Try and trim, but only if the side being trimmed isn't actually fetching.
+            // For simplicity (both of impl here, and contract w/ DataSource) we don't
+            // allow fetches in same direction - this means reading the load state is safe.
+            if (trimFromFront) {
+                if (pager.loadStateManager.start != LoadState.LOADING) {
+                    if (storage.trimFromFront(
+                            replacePagesWithNulls,
+                            config.maxSize,
+                            requiredRemainder,
+                            this@ContiguousPagedList
+                        )
+                    ) {
+                        // trimmed from front, ensure we can fetch in that dir
+                        pager.loadStateManager.setState(
+                            LoadType.START,
+                            LoadState.IDLE,
+                            null
+                        )
+                    }
+                }
+            } else {
+                if (pager.loadStateManager.end != LoadState.LOADING) {
+                    if (storage.trimFromEnd(
+                            replacePagesWithNulls,
+                            config.maxSize,
+                            requiredRemainder,
+                            this@ContiguousPagedList
+                        )
+                    ) {
+                        pager.loadStateManager.setState(LoadType.END, LoadState.IDLE, null)
+                    }
+                }
+            }
+        }
+
+        triggerBoundaryCallback(type, page)
+        return continueLoading
+    }
+
+    override fun onStateChanged(type: LoadType, state: LoadState, error: Throwable?) =
+        dispatchStateChange(type, state, error)
+
+    private fun triggerBoundaryCallback(type: LoadType, page: List<V>) {
+        if (boundaryCallback != null) {
+            val deferEmpty = storage.size == 0
+            val deferBegin = (!deferEmpty && type == LoadType.START && page.isEmpty())
+            val deferEnd = (!deferEmpty && type == LoadType.END && page.isEmpty())
+            deferBoundaryCallbacks(deferEmpty, deferBegin, deferEnd)
+        }
+    }
+
+    override fun retry() {
+        super.retry()
+        pager.retry()
+
+        if (pager.loadStateManager.refresh == LoadState.RETRYABLE_ERROR) {
+            // Loading the next PagedList failed, signal the retry callback.
+            refreshRetryCallback?.run()
+        }
+    }
+
+    init {
+        this.lastLoad = lastLoad
+        pager = Pager(
+            config,
+            dataSource,
+            mainThreadExecutor,
+            backgroundThreadExecutor,
+            this,
+            storage,
+            initialResult
+        )
+
+        if (config.enablePlaceholders) {
+            // Placeholders enabled, pass raw data to storage init
+            storage.init(
+                initialResult.leadingNulls,
+                initialResult.data,
+                initialResult.trailingNulls,
+                initialResult.offset,
+                this
+            )
+        } else {
+            // If placeholder are disabled, avoid passing leading/trailing nulls,
+            // since DataSource may have passed them anyway
+            storage.init(
+                0,
+                initialResult.data,
+                0,
+                initialResult.offset + initialResult.leadingNulls,
+                this
+            )
+        }
+
+        shouldTrim =
+            dataSource.supportsPageDropping && config.maxSize != Config.MAX_SIZE_UNBOUNDED
+
+        if (this.lastLoad == LAST_LOAD_UNSPECIFIED) {
+            // Because the ContiguousPagedList wasn't initialized with a last load position,
+            // initialize it to the middle of the initial load
+            this.lastLoad = (initialResult.leadingNulls + initialResult.offset +
+                    initialResult.data.size / 2)
+        }
+        triggerBoundaryCallback(LoadType.REFRESH, initialResult.data)
+    }
+
+    override fun dispatchCurrentLoadState(listener: LoadStateListener) =
+        pager.loadStateManager.dispatchCurrentLoadState(listener)
+
+    override fun setInitialLoadState(loadState: LoadState, error: Throwable?) =
+        pager.loadStateManager.setState(LoadType.REFRESH, loadState, error)
+
+    @MainThread
+    override fun dispatchUpdatesSinceSnapshot(snapshot: PagedList<V>, callback: Callback) {
+        val snapshotStorage = snapshot.storage
+
+        val newlyAppended = storage.numberAppended - snapshotStorage.numberAppended
+        val newlyPrepended = storage.numberPrepended - snapshotStorage.numberPrepended
+
+        val previousTrailing = snapshotStorage.trailingNullCount
+        val previousLeading = snapshotStorage.leadingNullCount
+
+        // Validate that the snapshot looks like a previous version of this list - if it's not,
+        // we can't be sure we'll dispatch callbacks safely
+        if (snapshotStorage.isEmpty() ||
+            newlyAppended < 0 ||
+            newlyPrepended < 0 ||
+            storage.trailingNullCount != maxOf(previousTrailing - newlyAppended, 0) ||
+            storage.leadingNullCount != maxOf(previousLeading - newlyPrepended, 0) ||
+            storage.storageCount != snapshotStorage.storageCount + newlyAppended + newlyPrepended
+        ) {
+            throw IllegalArgumentException(
+                "Invalid snapshot provided - doesn't appear to be a snapshot of this PagedList"
+            )
+        }
+
+        if (newlyAppended != 0) {
+            val changedCount = minOf(previousTrailing, newlyAppended)
+            val addedCount = newlyAppended - changedCount
+
+            val endPosition = snapshotStorage.leadingNullCount + snapshotStorage.storageCount
+            if (changedCount != 0) {
+                callback.onChanged(endPosition, changedCount)
+            }
+            if (addedCount != 0) {
+                callback.onInserted(endPosition + changedCount, addedCount)
+            }
+        }
+        if (newlyPrepended != 0) {
+            val changedCount = minOf(previousLeading, newlyPrepended)
+            val addedCount = newlyPrepended - changedCount
+
+            if (changedCount != 0) {
+                callback.onChanged(previousLeading, changedCount)
+            }
+            if (addedCount != 0) {
+                callback.onInserted(0, addedCount)
+            }
+        }
+    }
+
+    @MainThread
+    override fun loadAroundInternal(index: Int) {
+        val prependItems =
+            getPrependItemsRequested(config.prefetchDistance, index, storage.leadingNullCount)
+        val appendItems = getAppendItemsRequested(
+            config.prefetchDistance,
+            index,
+            storage.leadingNullCount + storage.storageCount
+        )
+
+        prependItemsRequested = maxOf(prependItems, prependItemsRequested)
+        if (prependItemsRequested > 0) {
+            pager.trySchedulePrepend()
+        }
+
+        appendItemsRequested = maxOf(appendItems, appendItemsRequested)
+        if (appendItemsRequested > 0) {
+            pager.tryScheduleAppend()
+        }
+    }
+
+    override fun detach() = pager.detach()
+
+    @MainThread
+    override fun onInitialized(count: Int) {
+        notifyInserted(0, count)
+        // simple heuristic to decide if, when dropping pages, we should replace with placeholders
+        replacePagesWithNulls = storage.leadingNullCount > 0 || storage.trailingNullCount > 0
+    }
+
+    @MainThread
+    override fun onPagePrepended(leadingNulls: Int, changed: Int, added: Int) {
+        // finally dispatch callbacks, after prepend may have already been scheduled
+        notifyChanged(leadingNulls, changed)
+        notifyInserted(0, added)
+
+        offsetAccessIndices(added)
+    }
+
+    @MainThread
+    override fun onPageAppended(endPosition: Int, changed: Int, added: Int) {
+        // finally dispatch callbacks, after append may have already been scheduled
+        notifyChanged(endPosition, changed)
+        notifyInserted(endPosition + changed, added)
+    }
+
+    @MainThread
+    override fun onPagePlaceholderInserted(pageIndex: Int) {
+        throw IllegalStateException("Tiled callback on ContiguousPagedList")
+    }
+
+    @MainThread
+    override fun onPageInserted(start: Int, count: Int) {
+        throw IllegalStateException("Tiled callback on ContiguousPagedList")
+    }
+
+    override fun onPagesRemoved(startOfDrops: Int, count: Int) = notifyRemoved(startOfDrops, count)
+
+    override fun onPagesSwappedToPlaceholder(startOfDrops: Int, count: Int) =
+        notifyChanged(startOfDrops, count)
+}
diff --git a/paging/common/src/main/kotlin/androidx/paging/DataSource.kt b/paging/common/src/main/kotlin/androidx/paging/DataSource.kt
new file mode 100644
index 0000000..eec73ae
--- /dev/null
+++ b/paging/common/src/main/kotlin/androidx/paging/DataSource.kt
@@ -0,0 +1,472 @@
+/*
+ * 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.
+ */
+
+package androidx.paging
+
+import androidx.annotation.AnyThread
+import androidx.annotation.RestrictTo
+import androidx.annotation.WorkerThread
+import androidx.arch.core.util.Function
+import com.google.common.util.concurrent.ListenableFuture
+import java.util.concurrent.CopyOnWriteArrayList
+import java.util.concurrent.Executor
+import java.util.concurrent.atomic.AtomicBoolean
+
+/**
+ * Base class for loading pages of snapshot data into a [PagedList].
+ *
+ * DataSource is queried to load pages of content into a [PagedList]. A PagedList can grow as
+ * it loads more data, but the data loaded cannot be updated. If the underlying data set is
+ * modified, a new PagedList / DataSource pair must be created to represent the new data.
+ *
+ * <h4>Loading Pages</h4>
+ *
+ * PagedList queries data from its DataSource in response to loading hints. PagedListAdapter
+ * calls [PagedList.loadAround] to load content as the user scrolls in a RecyclerView.
+ *
+ * To control how and when a PagedList queries data from its DataSource, see
+ * [PagedList.Config]. The Config object defines things like load sizes and prefetch distance.
+ *
+ * <h4>Updating Paged Data</h4>
+ *
+ * A PagedList / DataSource pair are a snapshot of the data set. A new pair of
+ * PagedList / DataSource must be created if an update occurs, such as a reorder, insert, delete, or
+ * content update occurs. A DataSource must detect that it cannot continue loading its
+ * snapshot (for instance, when Database query notices a table being invalidated), and call
+ * [invalidate]. Then a new PagedList / DataSource pair would be created to load data from the new
+ * state of the Database query.
+ *
+ * To page in data that doesn't update, you can create a single DataSource, and pass it to a single
+ * PagedList. For example, loading from network when the network's paging API doesn't provide
+ * updates.
+ *
+ * To page in data from a source that does provide updates, you can create a [DataSource.Factory],
+ * where each DataSource created is invalidated when an update to the data set occurs that makes the
+ * current snapshot invalid. For example, when paging a query from the Database, and the table being
+ * queried inserts or removes items. You can also use a DataSource.Factory to provide multiple
+ * versions of network-paged lists. If reloading all content (e.g. in response to an action like
+ * swipe-to-refresh) is required to get a new version of data, you can connect an explicit refresh
+ * signal to call [invalidate] on the current [DataSource].
+ *
+ * If you have more granular update signals, such as a network API signaling an update to a single
+ * item in the list, it's recommended to load data from network into memory. Then present that
+ * data to the PagedList via a DataSource that wraps an in-memory snapshot. Each time the in-memory
+ * copy changes, invalidate the previous DataSource, and a new one wrapping the new state of the
+ * snapshot can be created.
+ *
+ * <h4>Implementing a DataSource</h4>
+ *
+ * To implement, extend one of the subclasses: [PageKeyedDataSource], [ItemKeyedDataSource], or
+ * [PositionalDataSource].
+ *
+ * Use [PageKeyedDataSource] if pages you load embed keys for loading adjacent pages. For example a
+ * network response that returns some items, and a next/previous page links.
+ *
+ * Use [ItemKeyedDataSource] if you need to use data from item `N-1` to load item
+ * `N`. For example, if requesting the backend for the next comments in the list
+ * requires the ID or timestamp of the most recent loaded comment, or if querying the next users
+ * from a name-sorted database query requires the name and unique ID of the previous.
+ *
+ * Use [PositionalDataSource] if you can load pages of a requested size at arbitrary
+ * positions, and provide a fixed item count. PositionalDataSource supports querying pages at
+ * arbitrary positions, so can provide data to PagedLists in arbitrary order. Note that
+ * PositionalDataSource is required to respect page size for efficient tiling. If you want to
+ * override page size (e.g. when network page size constraints are only known at runtime), use one
+ * of the other DataSource classes.
+ *
+ * Because a `null` item indicates a placeholder in [PagedList], DataSource may not
+ * return `null` items in lists that it loads. This is so that users of the PagedList
+ * can differentiate unloaded placeholder items from content that has been paged in.
+ *
+ * @param Key Unique identifier for item loaded from DataSource. Often an integer to represent
+ *            position in data set. Note - this is distinct from e.g. Room's `<Value> Value type
+ *            loaded by the DataSource.
+ */
+abstract class DataSource<Key : Any, Value : Any>
+// Since we currently rely on implementation details of two implementations, prevent external
+// subclassing, except through exposed subclasses.
+internal constructor(internal val type: KeyType) {
+    private val onInvalidatedCallbacks = CopyOnWriteArrayList<InvalidatedCallback>()
+
+    private val _invalid = AtomicBoolean(false)
+    /**
+     * @return `true` if the data source is invalid, and can no longer be queried for data.
+     */
+    open val isInvalid
+        @WorkerThread
+        get() = _invalid.get()
+
+    private var _executor: Executor? = null
+    /**
+     * `null` until `loadInitial` is called by [PagedList] construction.
+     *
+     * This backing variable is necessary for back-compatibility with paging-common:2.1.0 Java API,
+     * while still providing synthetic accessors for Kotlin API.
+     */
+    protected val executor: Executor
+        get() = _executor ?: throw IllegalStateException(
+            "This DataSource has not been passed to a PagedList, has no executor yet."
+        )
+
+    /**
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    fun initExecutor(executor: Executor) {
+        _executor = executor
+    }
+
+    /**
+     * Factory for DataSources.
+     *
+     * Data-loading systems of an application or library can implement this interface to allow
+     * `LiveData<PagedList>`s to be created. For example, Room can provide a
+     * [DataSource.Factory] for a given SQL query:
+     *
+     * ```
+     * @Dao
+     * interface UserDao {
+     *     @Query("SELECT * FROM user ORDER BY lastName ASC")
+     *     public abstract DataSource.Factory<Integer, User> usersByLastName();
+     * }
+     * ```
+     *
+     * In the above sample, `Integer` is used because it is the `Key` type of
+     * PositionalDataSource. Currently, Room uses the `LIMIT`/`OFFSET` SQL keywords to
+     * page a large query with a PositionalDataSource.
+     *
+     * @param Key Key identifying items in DataSource.
+     * @param Value Type of items in the list loaded by the DataSources.
+     */
+    abstract class Factory<Key : Any, Value : Any> {
+        /**
+         * Create a [DataSource].
+         *
+         * The [DataSource] should invalidate itself if the snapshot is no longer valid. If a
+         * [DataSource] becomes invalid, the only way to query more data is to create a new
+         * [DataSource] from the Factory.
+         *
+         * [androidx.paging.LivePagedListBuilder] for example will construct a new PagedList and
+         * DataSource when the current DataSource is invalidated, and pass the new PagedList through
+         * the `LiveData<PagedList>` to observers.
+         *
+         * @return the new DataSource.
+         */
+        abstract fun create(): DataSource<Key, Value>
+
+        /**
+         * Applies the given function to each value emitted by DataSources produced by this Factory.
+         *
+         * Same as [mapByPage], but operates on individual items.
+         *
+         * @param function Function that runs on each loaded item, returning items of a potentially
+         *                 new type.
+         * @param ToValue Type of items produced by the new DataSource, from the passed function.
+         * @return A new [DataSource.Factory], which transforms items using the given function.
+         *
+         * @see mapByPage
+         * @see DataSource.map
+         * @see DataSource.mapByPage
+         */
+        open fun <ToValue : Any> map(function: Function<Value, ToValue>): Factory<Key, ToValue> =
+            mapByPage(Function { list -> list.map { function.apply(it) } })
+
+        /**
+         * Applies the given function to each value emitted by DataSources produced by this Factory.
+         *
+         * Same as [map], but allows for batch conversions.
+         *
+         * @param function Function that runs on each loaded page, returning items of a potentially
+         *                 new type.
+         * @param ToValue Type of items produced by the new DataSource, from the passed function.
+         * @return A new [DataSource.Factory], which transforms items using the given function.
+         *
+         * @see map
+         * @see DataSource.map
+         * @see DataSource.mapByPage
+         */
+        open fun <ToValue : Any> mapByPage(
+            function: Function<List<Value>, List<ToValue>>
+        ): Factory<Key, ToValue> = object : Factory<Key, ToValue>() {
+            override fun create(): DataSource<Key, ToValue> =
+                [email protected]().mapByPage(function)
+        }
+    }
+
+    /**
+     * Applies the given function to each value emitted by the DataSource.
+     *
+     * Same as [map], but allows for batch conversions.
+     *
+     * @param function Function that runs on each loaded page, returning items of a potentially
+     *                 new type.
+     * @param ToValue Type of items produced by the new DataSource, from the passed function.
+     * @return A new DataSource, which transforms items using the given function.
+     *
+     * @see map
+     * @see DataSource.Factory.map
+     * @see DataSource.Factory.mapByPage
+     */
+    open fun <ToValue : Any> mapByPage(
+        function: Function<List<Value>, List<ToValue>>
+    ): DataSource<Key, ToValue> = WrapperDataSource(this, function)
+
+    /**
+     * Applies the given function to each value emitted by the DataSource.
+     *
+     * Same as [mapByPage], but operates on individual items.
+     *
+     * @param function Function that runs on each loaded item, returning items of a potentially
+     *                 new type.
+     * @param ToValue Type of items produced by the new DataSource, from the passed function.
+     * @return A new DataSource, which transforms items using the given function.
+     *
+     * @see mapByPage
+     * @see DataSource.Factory.map
+     * @see DataSource.Factory.mapByPage
+     */
+    open fun <ToValue : Any> map(function: Function<Value, ToValue>): DataSource<Key, ToValue> =
+        mapByPage(Function { list -> list.map { function.apply(it) } })
+
+    /**
+     * Returns true if the data source guaranteed to produce a contiguous set of items, never
+     * producing gaps.
+     */
+    internal open val isContiguous = true
+
+    internal open val supportsPageDropping = true
+
+    /**
+     * Invalidation callback for [DataSource].
+     *
+     * Used to signal when a [DataSource] a data source has become invalid, and that a new data
+     * source is needed to continue loading data.
+     */
+    interface InvalidatedCallback {
+        /**
+         * Called when the data backing the list has become invalid. This callback is typically used
+         * to signal that a new data source is needed.
+         *
+         * This callback will be invoked on the thread that calls [invalidate]. It is valid for the
+         * data source to invalidate itself during its load methods, or for an outside source to
+         * invalidate it.
+         */
+        @AnyThread
+        fun onInvalidated()
+    }
+
+    /**
+     * Add a callback to invoke when the DataSource is first invalidated.
+     *
+     * Once invalidated, a data source will not become valid again.
+     *
+     * A data source will only invoke its callbacks once - the first time [invalidate] is called, on
+     * that thread.
+     *
+     * @param onInvalidatedCallback The callback, will be invoked on thread that invalidates the
+     * [DataSource].
+     */
+    @AnyThread
+    open fun addInvalidatedCallback(onInvalidatedCallback: InvalidatedCallback) {
+        onInvalidatedCallbacks.add(onInvalidatedCallback)
+    }
+
+    /**
+     * Remove a previously added invalidate callback.
+     *
+     * @param onInvalidatedCallback The previously added callback.
+     */
+    @AnyThread
+    open fun removeInvalidatedCallback(onInvalidatedCallback: InvalidatedCallback) {
+        onInvalidatedCallbacks.remove(onInvalidatedCallback)
+    }
+
+    /**
+     * Signal the data source to stop loading, and notify its callback.
+     *
+     * If invalidate has already been called, this method does nothing.
+     */
+    @AnyThread
+    open fun invalidate() {
+        if (_invalid.compareAndSet(false, true)) {
+            onInvalidatedCallbacks.forEach { it.onInvalidated() }
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    enum class LoadType {
+        INITIAL,
+        START,
+        END
+    }
+
+    /**
+     * @param K Type of the key used to query the [DataSource].
+     * @property key Can be `null` for init, otherwise non-null
+     *
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    class Params<K : Any> internal constructor(
+        /**
+         * @hide
+         */
+        @RestrictTo(RestrictTo.Scope.LIBRARY)
+        val type: LoadType,
+        val key: K?,
+        val initialLoadSize: Int,
+        val placeholdersEnabled: Boolean,
+        val pageSize: Int
+    )
+
+    /**
+     * @param Value Type of the data produced by a [DataSource].
+     * @property counted Set to true if the result is an initial load that is passed totalCount
+     */
+    open class BaseResult<Value : Any> protected constructor(
+        @JvmField
+        val data: List<Value>,
+        val prevKey: Any?,
+        val nextKey: Any?,
+        val leadingNulls: Int,
+        val trailingNulls: Int,
+        val offset: Int,
+        val counted: Boolean
+    ) {
+        init {
+            validate()
+        }
+
+        // only one of leadingNulls / offset may be used
+        private fun position() = leadingNulls + offset
+
+        internal fun totalCount() = when {
+            // only one of leadingNulls / offset may be used
+            counted -> position() + data.size + trailingNulls
+            else -> TOTAL_COUNT_UNKNOWN
+        }
+
+        private fun validate() {
+            if (leadingNulls < 0 || offset < 0) {
+                throw IllegalArgumentException("Position must be non-negative")
+            }
+            if (data.isEmpty() && (leadingNulls != 0 || trailingNulls != 0)) {
+                throw IllegalArgumentException(
+                    "Initial result cannot be empty if items are" + " present in data set."
+                )
+            }
+            if (trailingNulls < 0) {
+                throw IllegalArgumentException(
+                    "List size + position too large, last item in list beyond totalCount."
+                )
+            }
+        }
+
+        internal fun validateForInitialTiling(pageSize: Int) {
+            if (!counted) {
+                throw IllegalStateException(
+                    "Placeholders requested, but totalCount not provided. Please call the" +
+                            " three-parameter onResult method, or disable placeholders in the" +
+                            " PagedList.Config"
+                )
+            }
+            if (trailingNulls != 0 && data.size % pageSize != 0) {
+                val totalCount = leadingNulls + data.size + trailingNulls
+                throw IllegalArgumentException(
+                    "PositionalDataSource requires initial load size to be a multiple of page" +
+                            " size to support internal tiling. loadSize ${data.size}, position" +
+                            " $leadingNulls, totalCount $totalCount, pageSize $pageSize"
+                )
+            }
+            if (position() % pageSize != 0) {
+                throw IllegalArgumentException(
+                    "Initial load must be pageSize aligned.Position = ${position()}, pageSize =" +
+                            " $pageSize"
+                )
+            }
+        }
+
+        override fun equals(other: Any?) = when (other) {
+            is BaseResult<*> -> data == other.data &&
+                    prevKey == other.prevKey &&
+                    nextKey == other.nextKey &&
+                    leadingNulls == other.leadingNulls &&
+                    trailingNulls == other.trailingNulls &&
+                    offset == other.offset &&
+                    counted == other.counted
+            else -> false
+        }
+
+        internal companion object {
+            internal fun <T : Any> empty() = BaseResult(emptyList<T>(), null, null, 0, 0, 0, true)
+
+            internal const val TOTAL_COUNT_UNKNOWN = -1
+
+            internal fun <ToValue : Any, Value : Any> convert(
+                result: BaseResult<ToValue>,
+                function: Function<List<ToValue>, List<Value>>
+            ) = BaseResult(
+                data = convert(function, result.data),
+                prevKey = result.prevKey,
+                nextKey = result.nextKey,
+                leadingNulls = result.leadingNulls,
+                trailingNulls = result.trailingNulls,
+                offset = result.offset,
+                counted = result.counted
+            )
+        }
+    }
+
+    internal enum class KeyType {
+        POSITIONAL,
+        PAGE_KEYED,
+        ITEM_KEYED
+    }
+
+    internal abstract fun load(params: Params<Key>): ListenableFuture<out BaseResult<Value>>
+
+    /**
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    internal abstract fun getKeyInternal(item: Value): Key
+
+    /**
+     * Determine whether an error passed to a loading method is retryable.
+     *
+     * @param error Throwable returned from an attempted load from this DataSource.
+     * @return `true` if the error is retryable, otherwise false.
+     */
+    open fun isRetryableError(error: Throwable) = false
+
+    internal companion object {
+        internal fun <A, B> convert(
+            function: Function<List<A>, List<B>>,
+            source: List<A>
+        ): List<B> {
+            val dest = function.apply(source)
+            if (dest.size != source.size) {
+                throw IllegalStateException(
+                    "Invalid Function $function changed return size. This is not supported."
+                )
+            }
+            return dest
+        }
+    }
+}
diff --git a/paging/common/src/main/kotlin/androidx/paging/InitialPagedList.kt b/paging/common/src/main/kotlin/androidx/paging/InitialPagedList.kt
new file mode 100644
index 0000000..ef09669
--- /dev/null
+++ b/paging/common/src/main/kotlin/androidx/paging/InitialPagedList.kt
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+package androidx.paging
+
+import androidx.annotation.RestrictTo
+import androidx.paging.futures.DirectExecutor
+
+/**
+ * InitialPagedList is an empty placeholder that's sent at the front of a stream of PagedLists.
+ *
+ * It's used solely for listening to [PagedList.LoadType.REFRESH] loading events, and retrying
+ * any errors that occur during initial load.
+ *
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+class InitialPagedList<K : Any, V : Any>(
+    dataSource: DataSource<K, V>,
+    config: Config,
+    initialKey: K?
+) :
+    ContiguousPagedList<K, V>(
+        dataSource,
+        DirectExecutor,
+        DirectExecutor,
+        null,
+        config,
+        DataSource.BaseResult.empty<V>(),
+        0 // no previous load, so pass 0
+    ) {
+    override val lastKey = initialKey
+}
diff --git a/paging/common/src/main/kotlin/androidx/paging/ItemKeyedDataSource.kt b/paging/common/src/main/kotlin/androidx/paging/ItemKeyedDataSource.kt
new file mode 100644
index 0000000..1924dca
--- /dev/null
+++ b/paging/common/src/main/kotlin/androidx/paging/ItemKeyedDataSource.kt
@@ -0,0 +1,306 @@
+/*
+ * 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.
+ */
+
+package androidx.paging
+
+import androidx.arch.core.util.Function
+import androidx.concurrent.futures.ResolvableFuture
+import com.google.common.util.concurrent.ListenableFuture
+
+/**
+ * Incremental data loader for paging keyed content, where loaded content uses previously loaded
+ * items as input to future loads.
+ *
+ * Implement a DataSource using ItemKeyedDataSource if you need to use data from item `N - 1`
+ * to load item `N`. This is common, for example, in uniquely sorted database queries where
+ * attributes of the item such just before the next query define how to execute it.
+ *
+ * The `InMemoryByItemRepository` in the
+ * [PagingWithNetworkSample](https://github.com/googlesamples/android-architecture-components/blob/master/PagingWithNetworkSample/README.md)
+ * shows how to implement a network ItemKeyedDataSource using
+ * [Retrofit](https://square.github.io/retrofit/), while handling swipe-to-refresh, network errors,
+ * and retry.
+ *
+ * @param Key Type of data used to query Value types out of the DataSource.
+ * @param Value Type of items being loaded by the DataSource.
+ *
+ * @see ListenableItemKeyedDataSource
+ */
+abstract class ItemKeyedDataSource<Key : Any, Value : Any> :
+    ListenableItemKeyedDataSource<Key, Value>() {
+
+    /**
+     * Holder object for inputs to [loadInitial].
+     *
+     * @param Key Type of data used to query Value types out of the DataSource.
+     */
+    open class LoadInitialParams<Key : Any>(
+        requestedInitialKey: Key?,
+        requestedLoadSize: Int,
+        placeholdersEnabled: Boolean
+    ) : ListenableItemKeyedDataSource.LoadInitialParams<Key>(
+        requestedInitialKey,
+        requestedLoadSize,
+        placeholdersEnabled
+    )
+
+    /**
+     * Holder object for inputs to [loadBefore] and [loadAfter].
+     *
+     * @param Key Type of data used to query Value types out of the [DataSource].
+     */
+    open class LoadParams<Key : Any>(key: Key, requestedLoadSize: Int) :
+        ListenableItemKeyedDataSource.LoadParams<Key>(key, requestedLoadSize)
+
+    /**
+     * Callback for [loadInitial]
+     * to return data and, optionally, position/count information.
+     *
+     * A callback can be called only once, and will throw if called again.
+     *
+     * If you can compute the number of items in the data set before and after the loaded range,
+     * call the three parameter [onResult] to pass that information. You can skip passing this
+     * information by calling the single parameter [onResult], either if it's difficult to compute,
+     * or if [LoadInitialParams.placeholdersEnabled] is `false`, so the positioning information will
+     * be ignored.
+     *
+     * It is always valid for a DataSource loading method that takes a callback to stash the
+     * callback and call it later. This enables DataSources to be fully asynchronous, and to handle
+     * temporary, recoverable error states (such as a network error that can be retried).
+     *
+     * @param Value Type of items being loaded.
+     */
+    abstract class LoadInitialCallback<Value> : LoadCallback<Value>() {
+        /**
+         * Called to pass initial load state from a DataSource.
+         *
+         * Call this method from your DataSource's `loadInitial` function to return data,
+         * and inform how many placeholders should be shown before and after. If counting is cheap
+         * to compute (for example, if a network load returns the information regardless), it's
+         * recommended to pass data back through this method.
+         *
+         * It is always valid to pass a different amount of data than what is requested. Pass an
+         * empty list if there is no more data to load.
+         *
+         * @param data List of items loaded from the DataSource. If this is empty, the DataSource
+         *             is treated as empty, and no further loads will occur.
+         * @param position Position of the item at the front of the list. If there are `N`
+         *                 items before the items in data that can be loaded from this DataSource,
+         *                 pass `N`.
+         * @param totalCount Total number of items that may be returned from this [DataSource].
+         *                   Includes the number in the initial `data` parameter as well as any
+         *                   items that can be loaded in front or behind of `data`.
+         */
+        abstract fun onResult(data: List<Value>, position: Int, totalCount: Int)
+    }
+
+    /**
+     * Callback for ItemKeyedDataSource [loadBefore] and [loadAfter] to return data.
+     *
+     * A callback can be called only once, and will throw if called again.
+     *
+     * It is always valid for a DataSource loading method that takes a callback to stash the
+     * callback and call it later. This enables DataSources to be fully asynchronous, and to handle
+     * temporary, recoverable error states (such as a network error that can be retried).
+     *
+     * @param Value Type of items being loaded.
+     */
+    abstract class LoadCallback<Value> {
+        /**
+         * Called to pass loaded data from a DataSource.
+         *
+         * Call this method from your ItemKeyedDataSource's [loadBefore] and [loadAfter] methods to
+         * return data.
+         *
+         * Call this from [loadInitial] to initialize without counting available data, or supporting
+         * placeholders.
+         *
+         * It is always valid to pass a different amount of data than what is requested. Pass an
+         * empty list if there is no more data to load.
+         *
+         * @param data List of items loaded from the [ItemKeyedDataSource].
+         */
+        abstract fun onResult(data: List<Value>)
+
+        /**
+         * Called to report an error from a DataSource.
+         *
+         * Call this method to report an error from [loadInitial], [loadBefore], or [loadAfter]
+         * methods.
+         *
+         * @param error The error that occurred during loading.
+         */
+        open fun onError(error: Throwable) {
+            // TODO: remove default implementation in 3.0
+            throw IllegalStateException(
+                "You must implement onError if implementing your own load callback"
+            )
+        }
+    }
+
+    final override fun loadInitial(
+        params: ListenableItemKeyedDataSource.LoadInitialParams<Key>
+    ): ListenableFuture<InitialResult<Value>> {
+        val future = ResolvableFuture.create<InitialResult<Value>>()
+        executor.execute {
+            val callback = object : LoadInitialCallback<Value>() {
+                override fun onResult(data: List<Value>, position: Int, totalCount: Int) {
+                    future.set(InitialResult(data, position, totalCount))
+                }
+
+                override fun onResult(data: List<Value>) {
+                    future.set(InitialResult(data))
+                }
+
+                override fun onError(error: Throwable) {
+                    future.setException(error)
+                }
+            }
+            loadInitial(
+                LoadInitialParams(
+                    params.requestedInitialKey,
+                    params.requestedLoadSize,
+                    params.placeholdersEnabled
+                ),
+                callback
+            )
+        }
+        return future
+    }
+
+    private fun getFutureAsCallback(future: ResolvableFuture<Result<Value>>): LoadCallback<Value> {
+        return object : LoadCallback<Value>() {
+            override fun onResult(data: List<Value>) {
+                future.set(Result(data))
+            }
+
+            override fun onError(error: Throwable) {
+                future.setException(error)
+            }
+        }
+    }
+
+    final override fun loadBefore(
+        params: ListenableItemKeyedDataSource.LoadParams<Key>
+    ): ListenableFuture<Result<Value>> {
+        val future = ResolvableFuture.create<Result<Value>>()
+        executor.execute {
+            val loadParams = LoadParams(params.key, params.requestedLoadSize)
+            loadBefore(loadParams, getFutureAsCallback(future))
+        }
+        return future
+    }
+
+    final override fun loadAfter(
+        params: ListenableItemKeyedDataSource.LoadParams<Key>
+    ): ListenableFuture<Result<Value>> {
+        val future = ResolvableFuture.create<Result<Value>>()
+        executor.execute {
+            val loadParams = LoadParams(params.key, params.requestedLoadSize)
+            loadAfter(loadParams, getFutureAsCallback(future))
+        }
+        return future
+    }
+
+    /**
+     * Load initial data.
+     *
+     * This method is called first to initialize a [PagedList] with data. If it's possible to count
+     * the items that can be loaded by the DataSource, it's recommended to pass the loaded data to
+     * the callback via the three-parameter [LoadInitialCallback.onResult]. This enables PagedLists
+     * presenting data from this source to display placeholders to represent unloaded items.
+     *
+     * [LoadInitialParams.requestedInitialKey] and [LoadInitialParams.requestedLoadSize]
+     * are hints, not requirements, so they may be altered or ignored. Note that ignoring the
+     * `requestedInitialKey` can prevent subsequent PagedList/DataSource pairs from
+     * initializing at the same location. If your DataSource never invalidates (for example,
+     * loading from the network without the network ever signalling that old data must be reloaded),
+     * it's fine to ignore the `initialLoadKey` and always start from the beginning of the
+     * data set.
+     *
+     * @param params Parameters for initial load, including initial key and requested size.
+     * @param callback Callback that receives initial load data.
+     */
+    abstract fun loadInitial(params: LoadInitialParams<Key>, callback: LoadInitialCallback<Value>)
+
+    /**
+     * Load list data after the key specified in [LoadParams.key].
+     *
+     * It's valid to return a different list size than the page size if it's easier, e.g. if your
+     * backend defines page sizes. It is generally preferred to increase the number loaded than
+     * reduce.
+     *
+     * Data may be passed synchronously during the loadAfter method, or deferred and called at a
+     * later time. Further loads going down will be blocked until the callback is called.
+     *
+     * If data cannot be loaded (for example, if the request is invalid, or the data would be stale
+     * and inconsistent), it is valid to call [invalidate] to invalidate the data source, and
+     * prevent further loading.
+     *
+     * @param params Parameters for the load, including the key to load after, and requested size.
+     * @param callback Callback that receives loaded data.
+     */
+    abstract fun loadAfter(params: LoadParams<Key>, callback: LoadCallback<Value>)
+
+    /**
+     * Load list data before the key specified in [LoadParams.key].
+     *
+     * It's valid to return a different list size than the page size if it's easier, e.g. if your
+     * backend defines page sizes. It is generally preferred to increase the number loaded than
+     * reduce.
+     *
+     * **Note:** Data returned will be prepended just before the key
+     * passed, so if you vary size, ensure that the last item is adjacent to the passed key.
+     *
+     * Data may be passed synchronously during the loadBefore method, or deferred and called at a
+     * later time. Further loads going up will be blocked until the callback is called.
+     *
+     * If data cannot be loaded (for example, if the request is invalid, or the data would be stale
+     * and inconsistent), it is valid to call [invalidate] to invalidate the data source, and
+     * prevent further loading.
+     *
+     * @param params Parameters for the load, including the key to load before, and requested size.
+     * @param callback Callback that receives loaded data.
+     */
+    abstract fun loadBefore(params: LoadParams<Key>, callback: LoadCallback<Value>)
+
+    /**
+     * Return a key associated with the given item.
+     *
+     * If your ItemKeyedDataSource is loading from a source that is sorted and loaded by a unique
+     * integer ID, you would return `item.getID()` here. This key can then be passed to
+     * [loadBefore] or [loadAfter] to load additional items adjacent to the item passed to this
+     * function.
+     *
+     * If your key is more complex, such as when you're sorting by name, then resolving collisions
+     * with integer ID, you'll need to return both. In such a case you would use a wrapper class,
+     * such as `Pair<String, Integer>` or, in Kotlin,
+     * `data class Key(val name: String, val id: Int)`
+     *
+     * @param item Item to get the key from.
+     * @return Key associated with given item.
+     */
+    abstract override fun getKey(item: Value): Key
+
+    final override fun <ToValue : Any> mapByPage(
+        function: Function<List<Value>, List<ToValue>>
+    ): ItemKeyedDataSource<Key, ToValue> = WrapperItemKeyedDataSource(this, function)
+
+    final override fun <ToValue : Any> map(
+        function: Function<Value, ToValue>
+    ): ItemKeyedDataSource<Key, ToValue> =
+        mapByPage(Function { list -> list.map { function.apply(it) } })
+}
diff --git a/paging/common/src/main/kotlin/androidx/paging/ListDataSource.kt b/paging/common/src/main/kotlin/androidx/paging/ListDataSource.kt
new file mode 100644
index 0000000..161a583
--- /dev/null
+++ b/paging/common/src/main/kotlin/androidx/paging/ListDataSource.kt
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+package androidx.paging
+
+import androidx.annotation.VisibleForTesting
+import java.util.ArrayList
+
+@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
+class ListDataSource<T : Any>(list: List<T>) : PositionalDataSource<T>() {
+    private val list: List<T> = ArrayList(list)
+
+    override fun loadInitial(params: LoadInitialParams, callback: LoadInitialCallback<T>) {
+        val totalCount = list.size
+        val position = computeInitialLoadPosition(params, totalCount)
+        val loadSize = computeInitialLoadSize(params, position, totalCount)
+
+        // for simplicity, we could return everything immediately,
+        // but we tile here since it's expected behavior
+        val sublist = list.subList(position, position + loadSize)
+        callback.onResult(sublist, position, totalCount)
+    }
+
+    override fun loadRange(params: LoadRangeParams, callback: LoadRangeCallback<T>) {
+        val end = minOf(list.size, params.startPosition + params.loadSize)
+        callback.onResult(list.subList(params.startPosition, end))
+    }
+}
diff --git a/paging/common/src/main/kotlin/androidx/paging/ListenableItemKeyedDataSource.kt b/paging/common/src/main/kotlin/androidx/paging/ListenableItemKeyedDataSource.kt
new file mode 100644
index 0000000..2289b23
--- /dev/null
+++ b/paging/common/src/main/kotlin/androidx/paging/ListenableItemKeyedDataSource.kt
@@ -0,0 +1,190 @@
+/*
+ * 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.RestrictTo
+import com.google.common.util.concurrent.ListenableFuture
+
+/**
+ * Incremental data loader for paging keyed content, where loaded content uses previously loaded
+ * items as input to future loads.
+ *
+ * Implement a DataSource using ListenableItemKeyedDataSource if you need to use data from item
+ * `N - 1` to load item `N`. This is common, for example, in uniquely sorted database
+ * queries where attributes of the item such just before the next query define how to execute it.
+ *
+ * @see ItemKeyedDataSource
+ *
+ * @param Key Type of data used to query Value types out of the DataSource.
+ * @param Value Type of items being loaded by the DataSource.
+ */
+abstract class ListenableItemKeyedDataSource<Key : Any, Value : Any> :
+    DataSource<Key, Value>(KeyType.ITEM_KEYED) {
+
+    @Suppress("RedundantVisibilityModifier") // Metalava doesn't inherit visibility properly.
+    internal final override fun load(params: Params<Key>): ListenableFuture<out BaseResult<Value>> {
+        when (params.type) {
+            LoadType.INITIAL -> {
+                val initParams = ItemKeyedDataSource.LoadInitialParams(
+                    params.key, params.initialLoadSize, params.placeholdersEnabled
+                )
+                return loadInitial(initParams)
+            }
+            LoadType.START -> {
+                val loadParams = ItemKeyedDataSource.LoadParams(params.key!!, params.pageSize)
+                return loadBefore(loadParams)
+            }
+            LoadType.END -> {
+                val loadParams = ItemKeyedDataSource.LoadParams(params.key!!, params.pageSize)
+                return loadAfter(loadParams)
+            }
+        }
+    }
+
+    /**
+     * Holder object for inputs to [loadInitial].
+     *
+     * @param Key Type of data used to query Value types out of the DataSource.
+     * @property requestedInitialKey Load items around this key, or at the beginning of the data set
+     *                               if `null` is passed.
+     *
+     *                               Note that this key is generally a hint, and may be ignored if
+     *                               you want to always load from the beginning.
+     * @property requestedLoadSize Requested number of items to load.
+     *
+     *                             Note that this may be larger than available data.
+     * @property placeholdersEnabled Defines whether placeholders are enabled, and whether the
+     *                               loaded total count will be ignored.
+     */
+    open class LoadInitialParams<Key : Any>(
+        @JvmField val requestedInitialKey: Key?,
+        @JvmField val requestedLoadSize: Int,
+        @JvmField val placeholdersEnabled: Boolean
+    )
+
+    /**
+     * Holder object for inputs to [loadBefore] and [loadAfter].
+     *
+     * @param Key Type of data used to query Value types out of the DataSource.
+     * @property key Load items before/after this key.
+     *
+     *               Returned data must begin directly adjacent to this position.
+     * @property requestedLoadSize Requested number of items to load.
+     *
+     *                             Returned page can be of this size, but it may be altered if that
+     *                             is easier, e.g. a network data source where the backend defines
+     *                             page size.
+     */
+    open class LoadParams<Key : Any>(@JvmField val key: Key, @JvmField val requestedLoadSize: Int)
+
+    /**
+     * Load initial data.
+     *
+     * This method is called first to initialize a PagedList with data. If it's possible to count
+     * the items that can be loaded by the DataSource, it's recommended to pass `totalCount`
+     * to the [InitialResult] constructor. This enables PagedLists presenting data from this
+     * source to display placeholders to represent unloaded items.
+     *
+     * [ItemKeyedDataSource.LoadInitialParams.requestedInitialKey] and
+     * [ItemKeyedDataSource.LoadInitialParams.requestedLoadSize] are hints, not requirements,
+     * so they may be altered or ignored. Note that ignoring the `requestedInitialKey` can
+     * prevent subsequent PagedList/DataSource pairs from initializing at the same location. If your
+     * DataSource never invalidates (for example, loading from the network without the network ever
+     * signalling that old data must be reloaded), it's fine to ignore the `initialLoadKey`
+     * and always start from the beginning of the data set.
+     *
+     * @param params Parameters for initial load, including initial key and requested size.
+     * @return ListenableFuture of the loaded data.
+     */
+    abstract fun loadInitial(params: LoadInitialParams<Key>): ListenableFuture<InitialResult<Value>>
+
+    /**
+     * Load list data after the key specified in
+     * [LoadParams.key][ItemKeyedDataSource.LoadParams.key].
+     *
+     * It's valid to return a different list size than the page size if it's easier, e.g. if your
+     * backend defines page sizes. It is generally preferred to increase the number loaded than
+     * reduce.
+     *
+     * If data cannot be loaded (for example, if the request is invalid, or the data would be stale
+     * and inconsistent), it is valid to call [invalidate] to invalidate the data source, and
+     * prevent further loading.
+     *
+     * @param params Parameters for the load, including the key to load after, and requested size.
+     * @return [ListenableFuture] of the loaded data.
+     */
+    abstract fun loadAfter(params: LoadParams<Key>): ListenableFuture<Result<Value>>
+
+    /**
+     * Load list data after the key specified in
+     * [LoadParams.key][ItemKeyedDataSource.LoadParams.key].
+     *
+     * It's valid to return a different list size than the page size if it's easier, e.g. if your
+     * backend defines page sizes. It is generally preferred to increase the number loaded than
+     * reduce.
+     *
+     * **Note:** Data returned will be prepended just before the key
+     * passed, so if you don't return a page of the requested size, ensure that the last item is
+     * adjacent to the passed key.
+     * It's valid to return a different list size than the page size if it's easier, e.g. if your
+     * backend defines page sizes. It is generally preferred to increase the number loaded than
+     * reduce.
+     *
+     * If data cannot be loaded (for example, if the request is invalid, or the data would be stale
+     * and inconsistent), it is valid to call [invalidate] to invalidate the data source,
+     * and prevent further loading.
+     *
+     * @param params Parameters for the load, including the key to load before, and requested size.
+     * @return ListenableFuture of the loaded data.
+     */
+    abstract fun loadBefore(params: LoadParams<Key>): ListenableFuture<Result<Value>>
+
+    abstract fun getKey(item: Value): Key
+
+    /**
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    final override fun getKeyInternal(item: Value): Key = getKey(item)
+
+    /**
+     * Type produced by [loadInitial] to represent initially loaded data.
+     *
+     * @param V The type of the data loaded.
+     */
+    open class InitialResult<V : Any> : BaseResult<V> {
+        constructor(data: List<V>, position: Int, totalCount: Int) : super(
+            data,
+            null,
+            null,
+            position,
+            totalCount - data.size - position,
+            position,
+            true
+        )
+
+        constructor(data: List<V>) : super(data, null, null, 0, 0, 0, false)
+    }
+
+    /**
+     * Type produced by [loadBefore] and [loadAfter] to represent a page of loaded data.
+     *
+     * @param V The type of the data loaded.
+     */
+    open class Result<V : Any>(data: List<V>) :
+        DataSource.BaseResult<V>(data, null, null, 0, 0, 0, false)
+}
diff --git a/paging/common/src/main/kotlin/androidx/paging/ListenablePageKeyedDataSource.kt b/paging/common/src/main/kotlin/androidx/paging/ListenablePageKeyedDataSource.kt
new file mode 100644
index 0000000..7780b52
--- /dev/null
+++ b/paging/common/src/main/kotlin/androidx/paging/ListenablePageKeyedDataSource.kt
@@ -0,0 +1,199 @@
+/*
+ * 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.
+ */
+
+package androidx.paging
+
+import androidx.annotation.RestrictTo
+import androidx.concurrent.futures.ResolvableFuture
+import com.google.common.util.concurrent.ListenableFuture
+
+/**
+ * Incremental data loader for page-keyed content, where requests return keys for next/previous
+ * pages.
+ *
+ * Implement a DataSource using PageKeyedDataSource if you need to use data from page `N - 1`
+ * to load page `N`. This is common, for example, in network APIs that include a next/previous
+ * link or key with each page load.
+ *
+ * @param Key Type of data used to query Value types out of the DataSource.
+ * @param Value Type of items being loaded by the DataSource.
+ */
+abstract class ListenablePageKeyedDataSource<Key : Any, Value : Any> :
+    DataSource<Key, Value>(KeyType.PAGE_KEYED) {
+
+    @Suppress("RedundantVisibilityModifier") // Metalava doesn't inherit visibility properly.
+    internal final override fun load(params: Params<Key>): ListenableFuture<out BaseResult<Value>> {
+        if (params.type == LoadType.INITIAL) {
+            val initParams = PageKeyedDataSource.LoadInitialParams<Key>(
+                params.initialLoadSize,
+                params.placeholdersEnabled
+            )
+            return loadInitial(initParams)
+        } else {
+            if (params.key == null) {
+                // null key, immediately return empty data
+                val future = ResolvableFuture.create<BaseResult<Value>>()
+                future.set(BaseResult.empty())
+                return future
+            }
+
+            val loadParams = PageKeyedDataSource.LoadParams(params.key, params.pageSize)
+
+            if (params.type == LoadType.START) {
+                return loadBefore(loadParams)
+            } else if (params.type == LoadType.END) {
+                return loadAfter(loadParams)
+            }
+        }
+        throw IllegalArgumentException("Unsupported type " + params.type.toString())
+    }
+
+    /**
+     * Holder object for inputs to [loadInitial].
+     *
+     * @param Key Type of data used to query pages.
+     * @property requestedLoadSize Requested number of items to load.
+     *
+     *                             Note that this may be larger than available data.
+     * @property placeholdersEnabled Defines whether placeholders are enabled, and whether the
+     *                               loaded total count will be ignored.
+     */
+    open class LoadInitialParams<Key : Any>(
+        @JvmField val requestedLoadSize: Int,
+        @JvmField val placeholdersEnabled: Boolean
+    )
+
+    /**
+     * Holder object for inputs to [loadBefore] and [loadAfter].
+     *
+     * @param Key Type of data used to query pages.
+     * @property key Load items before/after this key.
+     *
+     *               Returned data must begin directly adjacent to this position.
+     * @property requestedLoadSize Requested number of items to load.
+     *
+     *                             Returned page can be of this size, but it may be altered if that
+     *                             is easier, e.g. a network data source where the backend defines
+     *                             page size.
+     */
+    open class LoadParams<Key : Any>(@JvmField val key: Key, @JvmField val requestedLoadSize: Int)
+
+    /**
+     * Load initial data.
+     *
+     * This method is called first to initialize a PagedList with data. If it's possible to count
+     * the items that can be loaded by the DataSource, it's recommended to pass the position and
+     * count to the [InitialResult constructor][InitialResult]. This enables PagedLists presenting
+     * data from this source to display placeholders to represent unloaded items.
+     *
+     * [LoadInitialParams.requestedLoadSize] is a hint, not a requirement, so it may be may be
+     * altered or ignored.
+     *
+     * @param params Parameters for initial load, including requested load size.
+     * @return ListenableFuture of the loaded data.
+     */
+    abstract fun loadInitial(
+        params: LoadInitialParams<Key>
+    ): ListenableFuture<InitialResult<Key, Value>>
+
+    /**
+     * Prepend page with the key specified by [LoadParams.key][PageKeyedDataSource.LoadParams.key].
+     *
+     * It's valid to return a different list size than the page size if it's easier, e.g. if your
+     * backend defines page sizes. It is generally preferred to increase the number loaded than
+     * reduce.
+     *
+     * If data cannot be loaded (for example, if the request is invalid, or the data would be stale
+     * and inconsistent), it is valid to call [invalidate] to invalidate the data source, and
+     * prevent further loading.
+     *
+     * @param params Parameters for the load, including the key for the new page, and requested load
+     *               size.
+     * @return ListenableFuture of the loaded data.
+     */
+    abstract fun loadBefore(params: LoadParams<Key>): ListenableFuture<Result<Key, Value>>
+
+    /**
+     * Append page with the key specified by [LoadParams.key][PageKeyedDataSource.LoadParams.key].
+     *
+     * It's valid to return a different list size than the page size if it's easier, e.g. if your
+     * backend defines page sizes. It is generally preferred to increase the number loaded than
+     * reduce.
+     *
+     * If data cannot be loaded (for example, if the request is invalid, or the data would be stale
+     * and inconsistent), it is valid to call [invalidate] to invalidate the data source, and
+     * prevent further loading.
+     *
+     * @param params Parameters for the load, including the key for the new page, and requested load
+     *               size.
+     * @return ListenableFuture of the loaded data.
+     */
+    abstract fun loadAfter(params: LoadParams<Key>): ListenableFuture<Result<Key, Value>>
+
+    /**
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    override fun getKeyInternal(item: Value): Key =
+        throw IllegalStateException("Cannot get key by item in pageKeyedDataSource")
+
+    /**
+     *  To support page dropping when PageKeyed, we'll need to:
+     *    - Stash keys for every page we have loaded (can id by index relative to loadInitial)
+     *    - Drop keys for any page not adjacent to loaded content
+     *    - And either:
+     *        - Allow impl to signal previous page key: onResult(data, nextPageKey, prevPageKey)
+     *        - Re-trigger loadInitial, and break assumption it will only occur once.
+     */
+    @Suppress("RedundantVisibilityModifier") // Metalava doesn't inherit visibility properly.
+    internal override val supportsPageDropping = false
+
+    /**
+     * Type produced by [loadInitial] to represent initially loaded data.
+     *
+     * @param Key Type of key used to identify pages.
+     * @param Value Type of items being loaded by the DataSource.
+     */
+    open class InitialResult<Key : Any, Value : Any> : BaseResult<Value> {
+        constructor(
+            data: List<Value>,
+            position: Int,
+            totalCount: Int,
+            previousPageKey: Key?,
+            nextPageKey: Key?
+        ) : super(
+            data,
+            previousPageKey,
+            nextPageKey,
+            position,
+            totalCount - data.size - position,
+            position,
+            true
+        )
+
+        constructor(data: List<Value>, previousPageKey: Key?, nextPageKey: Key?) :
+                super(data, previousPageKey, nextPageKey, 0, 0, 0, false)
+    }
+
+    /**
+     * Type produced by [loadBefore] and [loadAfter] to represent a page of loaded data.
+     *
+     * @param Key Type of key used to identify pages.
+     * @param Value Type of items being loaded by the [DataSource].
+     */
+    open class Result<Key : Any, Value : Any>(data: List<Value>, adjacentPageKey: Key?) :
+        DataSource.BaseResult<Value>(data, adjacentPageKey, adjacentPageKey, 0, 0, 0, false)
+}
diff --git a/paging/common/src/main/kotlin/androidx/paging/ListenablePositionalDataSource.kt b/paging/common/src/main/kotlin/androidx/paging/ListenablePositionalDataSource.kt
new file mode 100644
index 0000000..7058c2f
--- /dev/null
+++ b/paging/common/src/main/kotlin/androidx/paging/ListenablePositionalDataSource.kt
@@ -0,0 +1,313 @@
+/*
+ * 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.
+ */
+
+package androidx.paging
+
+import androidx.annotation.RestrictTo
+import androidx.paging.ListenablePositionalDataSource.InitialResult
+import com.google.common.util.concurrent.ListenableFuture
+
+/**
+ * Position-based data loader for a fixed-size, countable data set, supporting fixed-size loads at
+ * arbitrary page positions.
+ *
+ * Extend  [ListenablePositionalDataSource] if you can load pages of a requested size at arbitrary
+ * positions, and provide a fixed item count. If your data source can't support loading arbitrary
+ * requested page sizes (e.g. when network page size constraints are only known at runtime), either
+ * use [PageKeyedDataSource] or [ItemKeyedDataSource], or pass the initial result with the two
+ * parameter [InitialResult constructor][InitialResult].
+ *
+ * @param T Type of items being loaded by the [PositionalDataSource].
+ *
+ * @see PositionalDataSource
+ */
+abstract class ListenablePositionalDataSource<T : Any> : DataSource<Int, T>(KeyType.POSITIONAL) {
+    @Suppress("RedundantVisibilityModifier") // Metalava doesn't inherit visibility properly.
+    internal final override fun load(params: Params<Int>): ListenableFuture<out BaseResult<T>> {
+        if (params.type == LoadType.INITIAL) {
+            var initialPosition = 0
+            var initialLoadSize = params.initialLoadSize
+            if (params.key != null) {
+                initialPosition = params.key
+
+                if (params.placeholdersEnabled) {
+                    // snap load size to page multiple (minimum two)
+                    initialLoadSize =
+                        maxOf(initialLoadSize / params.pageSize, 2) * params.pageSize
+
+                    // move start so the load is centered around the key, not starting at it
+                    val idealStart = initialPosition - initialLoadSize / 2
+                    initialPosition = maxOf(0, idealStart / params.pageSize * params.pageSize)
+                } else {
+                    // not tiled, so don't try to snap or force multiple of a page size
+                    initialPosition -= initialLoadSize / 2
+                }
+            }
+            val initParams = PositionalDataSource.LoadInitialParams(
+                initialPosition,
+                initialLoadSize,
+                params.pageSize,
+                params.placeholdersEnabled
+            )
+            return loadInitial(initParams)
+        } else {
+            var startIndex = params.key!!
+            var loadSize = params.pageSize
+            if (params.type == LoadType.START) {
+                loadSize = minOf(loadSize, startIndex + 1)
+                startIndex = startIndex - loadSize + 1
+            }
+            return loadRange(PositionalDataSource.LoadRangeParams(startIndex, loadSize))
+        }
+    }
+
+    /**
+     * Holder object for inputs to [loadInitial].
+     */
+    open class LoadInitialParams(
+        /**
+         * Initial load position requested.
+         *
+         * Note that this may not be within the bounds of your data set, it may need to be adjusted
+         * before you execute your load.
+         */
+        @JvmField
+        val requestedStartPosition: Int,
+        /**
+         * Requested number of items to load.
+         *
+         * Note that this may be larger than available data.
+         */
+        @JvmField
+        val requestedLoadSize: Int,
+        /**
+         * Defines page size acceptable for return values.
+         *
+         * List of items passed to the callback must be an integer multiple of page size.
+         */
+        @JvmField
+        val pageSize: Int,
+        /**
+         * Defines whether placeholders are enabled, and whether the loaded total count will be
+         * ignored.
+         */
+        @JvmField
+        val placeholdersEnabled: Boolean
+    )
+
+    /**
+     * Holder object for inputs to [loadRange].
+     */
+    open class LoadRangeParams(
+        /**
+         * START position of data to load.
+         *
+         * Returned data must start at this position.
+         */
+        @JvmField
+        val startPosition: Int,
+        /**
+         * Number of items to load.
+         *
+         * Returned data must be of this size, unless at end of the list.
+         */
+        @JvmField
+        val loadSize: Int
+    )
+
+    /**
+     * Load initial list data.
+     *
+     * This method is called to load the initial page(s) from the DataSource.
+     *
+     * Result list must be a multiple of pageSize to enable efficient tiling.
+     *
+     * @param params Parameters for initial load, including requested start position, load size, and
+     *               page size.
+     * @return [ListenableFuture] of the loaded data.
+     */
+    abstract fun loadInitial(params: LoadInitialParams): ListenableFuture<InitialResult<T>>
+
+    /**
+     * Called to load a range of data from the DataSource.
+     *
+     * This method is called to load additional pages from the DataSource after the
+     * [ItemKeyedDataSource.LoadInitialCallback] passed to dispatchLoadInitial has initialized a
+     * [PagedList].
+     *
+     * Unlike [ItemKeyedDataSource.loadInitial], this method must return the number of items
+     * requested, at the position requested.
+     *
+     * @param params Parameters for load, including start position and load size.
+     * @return [ListenableFuture] of the loaded data.
+     */
+    abstract fun loadRange(params: LoadRangeParams): ListenableFuture<RangeResult<T>>
+
+    /**
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    final override fun getKeyInternal(item: T): Int =
+        throw IllegalStateException("Cannot get key by item in positionalDataSource")
+
+    internal companion object {
+        /**
+         * Helper for computing an initial position in [loadInitial] when total data set size can be
+         * computed ahead of loading.
+         *
+         * The value computed by this function will do bounds checking, page alignment, and
+         * positioning based on initial load size requested.
+         *
+         * Example usage in a [PositionalDataSource] subclass:
+         * ```
+         * class ItemDataSource extends PositionalDataSource<Item> {
+         *     private int computeCount() {
+         *         // actual count code here
+         *     }
+         *
+         *     private List<Item> loadRangeInternal(int startPosition, int loadCount) {
+         *        // actual load code here
+         *     }
+         * }
+         * ```
+         *
+         * ```
+         * @Override
+         * public void loadInitial(@NonNull LoadInitialParams params,
+         *     @NonNull LoadInitialCallback<Item> callback) {
+         *     int totalCount = computeCount();
+         *     int position = computeInitialLoadPosition(params, totalCount);
+         *     int loadSize = computeInitialLoadSize(params, position, totalCount);
+         *     callback.onResult(loadRangeInternal(position, loadSize), position, totalCount);
+         * }
+         * ```
+         *
+         * ```
+         * @Override
+         * public void loadRange(@NonNull LoadRangeParams params,
+         *     @NonNull LoadRangeCallback<Item> callback) {
+         *     callback.onResult(loadRangeInternal(params.startPosition, params.loadSize));
+         * }
+         * ```
+         *
+         * @param params Params passed to [loadInitial], including page size, and requested start /
+         *               loadSize.
+         * @param totalCount Total size of the data set.
+         * @return Position to start loading at.
+         *
+         * @see [computeInitialLoadSize]
+         */
+        @JvmStatic
+        fun computeInitialLoadPosition(params: LoadInitialParams, totalCount: Int): Int {
+            val position = params.requestedStartPosition
+            val initialLoadSize = params.requestedLoadSize
+            val pageSize = params.pageSize
+
+            var pageStart = position / pageSize * pageSize
+
+            // maximum start pos is that which will encompass end of list
+            val maximumLoadPage =
+                (totalCount - initialLoadSize + pageSize - 1) / pageSize * pageSize
+            pageStart = minOf(maximumLoadPage, pageStart)
+
+            // minimum start position is 0
+            pageStart = maxOf(0, pageStart)
+
+            return pageStart
+        }
+
+        /**
+         * Helper for computing an initial load size in [loadInitial] when total data set size can
+         * be computed ahead of loading.
+         *
+         * This function takes the requested load size, and bounds checks it against the value
+         * returned by [computeInitialLoadPosition].
+         *
+         * Example usage in a PositionalDataSource subclass:
+         * ```
+         * class ItemDataSource extends PositionalDataSource<Item> {
+         *     private int computeCount() {
+         *         // actual count code here
+         *     }
+         *
+         *     private List<Item> loadRangeInternal(int startPosition, int loadCount) {
+         *         // actual load code here
+         *     }
+         *
+         *     @Override
+         *     public void loadInitial(@NonNull LoadInitialParams params,
+         *         @NonNull LoadInitialCallback<Item> callback) {
+         *         int totalCount = computeCount();
+         *         int position = computeInitialLoadPosition(params, totalCount);
+         *         int loadSize = computeInitialLoadSize(params, position, totalCount);
+         *         callback.onResult(loadRangeInternal(position, loadSize), position, totalCount);
+         *     }
+         *
+         *     @Override
+         *     public void loadRange(@NonNull LoadRangeParams params,
+         *     @NonNull LoadRangeCallback<Item> callback) {
+         *         callback.onResult(loadRangeInternal(params.startPosition, params.loadSize));
+         *     }
+         * }
+         * ```
+         *
+         * @param params Params passed to [loadInitial], including page size, and requested
+         *               start / loadSize.
+         * @param initialLoadPosition Value returned by [computeInitialLoadPosition]
+         * @param totalCount Total size of the data set.
+         * @return Number of items to load.
+         *
+         * @see [computeInitialLoadPosition]
+         */
+        @JvmStatic
+        fun computeInitialLoadSize(
+            params: LoadInitialParams,
+            initialLoadPosition: Int,
+            totalCount: Int
+        ) = minOf(totalCount - initialLoadPosition, params.requestedLoadSize)
+    }
+
+    /**
+     * Type produced by [loadInitial] to represent initially loaded data.
+     *
+     * @param V The type of the data loaded.
+     */
+    open class InitialResult<V : Any> : BaseResult<V> {
+        constructor(data: List<V>, position: Int, totalCount: Int) :
+                super(data, null, null, position, totalCount - data.size - position, 0, true) {
+            if (data.isEmpty() && position != 0) {
+                throw IllegalArgumentException(
+                    "Initial result cannot be empty if items are present in data set."
+                )
+            }
+        }
+
+        constructor(data: List<V>, position: Int) : super(data, null, null, 0, 0, position, false) {
+            if (data.isEmpty() && position != 0) {
+                throw IllegalArgumentException(
+                    "Initial result cannot be empty if items are present in data set."
+                )
+            }
+        }
+    }
+
+    /**
+     * Type produced by [loadRange] to represent a page of loaded data.
+     *
+     * @param V The type of the data loaded.
+     */
+    open class RangeResult<V : Any>(data: List<V>) : BaseResult<V>(data, null, null, 0, 0, 0, false)
+}
diff --git a/paging/common/src/main/kotlin/androidx/paging/PageKeyedDataSource.kt b/paging/common/src/main/kotlin/androidx/paging/PageKeyedDataSource.kt
new file mode 100644
index 0000000..ad41ea0
--- /dev/null
+++ b/paging/common/src/main/kotlin/androidx/paging/PageKeyedDataSource.kt
@@ -0,0 +1,322 @@
+/*
+ * 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.
+ */
+
+package androidx.paging
+
+import androidx.arch.core.util.Function
+import androidx.concurrent.futures.ResolvableFuture
+import com.google.common.util.concurrent.ListenableFuture
+
+/**
+ * Incremental data loader for page-keyed content, where requests return keys for next/previous
+ * pages.
+ *
+ * Implement a DataSource using PageKeyedDataSource if you need to use data from page `N - 1` to
+ * load page `N`. This is common, for example, in network APIs that include a next/previous link or
+ * key with each page load.
+ *
+ * The `InMemoryByPageRepository` in the
+ * [PagingWithNetworkSample](https://github.com/googlesamples/android-architecture-components/blob/master/PagingWithNetworkSample/README.md)
+ * shows how to implement a network PageKeyedDataSource using
+ * [Retrofit](https://square.github.io/retrofit/), while
+ * handling swipe-to-refresh, network errors, and retry.
+ *
+ * @param Key Type of data used to query Value types out of the DataSource.
+ * @param Value Type of items being loaded by the DataSource.
+ */
+abstract class PageKeyedDataSource<Key : Any, Value : Any> :
+    ListenablePageKeyedDataSource<Key, Value>() {
+    /**
+     * Holder object for inputs to [loadInitial].
+     *
+     * @param Key Type of data used to query pages.
+     */
+    open class LoadInitialParams<Key : Any>(requestedLoadSize: Int, placeholdersEnabled: Boolean) :
+        ListenablePageKeyedDataSource.LoadInitialParams<Key>(requestedLoadSize, placeholdersEnabled)
+
+    /**
+     * Holder object for inputs to [loadBefore] and [loadAfter].
+     *
+     * @param Key Type of data used to query pages.
+     */
+    open class LoadParams<Key : Any>(key: Key, requestedLoadSize: Int) :
+        ListenablePageKeyedDataSource.LoadParams<Key>(key, requestedLoadSize)
+
+    /**
+     * Callback for [loadInitial] to return data and, optionally, position/count information.
+     *
+     * A callback can be called only once, and will throw if called again.
+     *
+     * If you can compute the number of items in the data set before and after the loaded range,
+     * call the five parameter [onResult] to pass that information. You can skip passing this
+     * information by calling the three parameter [onResult], either if it's difficult to compute,
+     * or if [LoadInitialParams.placeholdersEnabled] is `false`, so the positioning information will
+     * be ignored.
+     *
+     * It is always valid for a DataSource loading method that takes a callback to stash the
+     * callback and call it later. This enables DataSources to be fully asynchronous, and to handle
+     * temporary, recoverable error states (such as a network error that can be retried).
+     *
+     * @param Key Type of data used to query pages.
+     * @param Value Type of items being loaded.
+     */
+    abstract class LoadInitialCallback<Key, Value> {
+        /**
+         * Called to pass initial load state from a DataSource.
+         *
+         * Call this method from your DataSource's `loadInitial` function to return data,
+         * and inform how many placeholders should be shown before and after. If counting is cheap
+         * to compute (for example, if a network load returns the information regardless), it's
+         * recommended to pass data back through this method.
+         *
+         * It is always valid to pass a different amount of data than what is requested. Pass an
+         * empty list if there is no more data to load.
+         *
+         * @param data List of items loaded from the DataSource. If this is empty, the DataSource
+         *             is treated as empty, and no further loads will occur.
+         * @param position Position of the item at the front of the list. If there are `N`
+         *                 items before the items in data that can be loaded from this DataSource,
+         *                 pass `N`.
+         * @param totalCount Total number of items that may be returned from this DataSource.
+         *                   Includes the number in the initial `data` parameter as well as any
+         *                   items that can be loaded in front or behind of `data`.
+         */
+        abstract fun onResult(
+            data: List<Value>,
+            position: Int,
+            totalCount: Int,
+            previousPageKey: Key?,
+            nextPageKey: Key?
+        )
+
+        /**
+         * Called to pass loaded data from a DataSource.
+         *
+         * Call this from [loadInitial] to initialize without counting available data, or supporting
+         * placeholders.
+         *
+         * It is always valid to pass a different amount of data than what is requested. Pass an
+         * empty list if there is no more data to load.
+         *
+         * @param data List of items loaded from the [PageKeyedDataSource].
+         * @param previousPageKey Key for page before the initial load result, or `null` if no more
+         *                        data can be loaded before.
+         * @param nextPageKey Key for page after the initial load result, or `null` if no
+         *                    more data can be loaded after.
+         */
+        abstract fun onResult(data: List<Value>, previousPageKey: Key?, nextPageKey: Key?)
+
+        /**
+         * Called to report an error from a DataSource.
+         *
+         * Call this method to report an error from [loadInitial].
+         *
+         * @param error The error that occurred during loading.
+         */
+        open fun onError(error: Throwable) {
+            // TODO: remove default implementation in 3.0
+            throw IllegalStateException(
+                "You must implement onError if implementing your own load callback"
+            )
+        }
+    }
+
+    /**
+     * Callback for [loadBefore] and [loadAfter] to return data.
+     *
+     * A callback can be called only once, and will throw if called again.
+     *
+     * It is always valid for a DataSource loading method that takes a callback to stash the
+     * callback and call it later. This enables DataSources to be fully asynchronous, and to handle
+     * temporary, recoverable error states (such as a network error that can be retried).
+     *
+     * @param Key Type of data used to query pages.
+     * @param Value Type of items being loaded.
+     */
+    abstract class LoadCallback<Key, Value> {
+        /**
+         * Called to pass loaded data from a [DataSource].
+         *
+         * Call this method from your PageKeyedDataSource's [loadBefore] and [loadAfter] methods to
+         * return data.
+         *
+         * It is always valid to pass a different amount of data than what is requested. Pass an
+         * empty list if there is no more data to load.
+         *
+         * Pass the key for the subsequent page to load to adjacentPageKey. For example, if you've
+         * loaded a page in [loadBefore], pass the key for the previous page, or `null` if the
+         * loaded page is the first. If in [loadAfter], pass the key for the next page, or `null`
+         * if the loaded page is the last.
+         *
+         * @param data List of items loaded from the PageKeyedDataSource.
+         * @param adjacentPageKey Key for subsequent page load (previous page in [loadBefore] / next
+         *                        page in [loadAfter]), or `null` if there are no more pages to load
+         *                        in the current load direction.
+         */
+        abstract fun onResult(data: List<Value>, adjacentPageKey: Key?)
+
+        /**
+         * Called to report an error from a DataSource.
+         *
+         * Call this method to report an error from your PageKeyedDataSource's [loadBefore] and
+         * [loadAfter] methods.
+         *
+         * @param error The error that occurred during loading.
+         */
+        open fun onError(error: Throwable) {
+            // TODO: remove default implementation in 3.0
+            throw IllegalStateException(
+                "You must implement onError if implementing your own load callback"
+            )
+        }
+    }
+
+    final override fun loadInitial(
+        params: ListenablePageKeyedDataSource.LoadInitialParams<Key>
+    ): ListenableFuture<InitialResult<Key, Value>> {
+        val future = ResolvableFuture.create<InitialResult<Key, Value>>()
+        executor.execute {
+            val callback = object : LoadInitialCallback<Key, Value>() {
+                override fun onResult(
+                    data: List<Value>,
+                    position: Int,
+                    totalCount: Int,
+                    previousPageKey: Key?,
+                    nextPageKey: Key?
+                ) {
+                    future.set(
+                        InitialResult(data, position, totalCount, previousPageKey, nextPageKey)
+                    )
+                }
+
+                override fun onResult(data: List<Value>, previousPageKey: Key?, nextPageKey: Key?) {
+                    future.set(InitialResult(data, previousPageKey, nextPageKey))
+                }
+
+                override fun onError(error: Throwable) {
+                    future.setException(error)
+                }
+            }
+            loadInitial(
+                LoadInitialParams(params.requestedLoadSize, params.placeholdersEnabled),
+                callback
+            )
+        }
+        return future
+    }
+
+    private fun getFutureAsCallback(future: ResolvableFuture<Result<Key, Value>>) =
+        object : LoadCallback<Key, Value>() {
+            override fun onResult(data: List<Value>, adjacentPageKey: Key?) {
+                future.set(Result(data, adjacentPageKey))
+            }
+
+            override fun onError(error: Throwable) {
+                future.setException(error)
+            }
+        }
+
+    final override fun loadBefore(
+        params: ListenablePageKeyedDataSource.LoadParams<Key>
+    ): ListenableFuture<Result<Key, Value>> {
+        val future = ResolvableFuture.create<Result<Key, Value>>()
+        executor.execute {
+            loadBefore(
+                LoadParams(params.key, params.requestedLoadSize),
+                getFutureAsCallback(future)
+            )
+        }
+        return future
+    }
+
+    final override fun loadAfter(
+        params: ListenablePageKeyedDataSource.LoadParams<Key>
+    ): ListenableFuture<Result<Key, Value>> {
+        val future = ResolvableFuture.create<Result<Key, Value>>()
+        executor.execute {
+            loadAfter(LoadParams(params.key, params.requestedLoadSize), getFutureAsCallback(future))
+        }
+        return future
+    }
+
+    /**
+     * Load initial data.
+     *
+     * This method is called first to initialize a PagedList with data. If it's possible to count
+     * the items that can be loaded by the DataSource, it's recommended to pass the loaded data to
+     * the callback via the three-parameter [LoadInitialCallback.onResult]. This enables PagedLists
+     * presenting data from this source to display placeholders to represent unloaded items.
+     *
+     * [LoadInitialParams.requestedLoadSize] is a hint, not a requirement, so it may be may be
+     * altered or ignored.
+     *
+     * @param params Parameters for initial load, including requested load size.
+     * @param callback Callback that receives initial load data.
+     */
+    abstract fun loadInitial(
+        params: LoadInitialParams<Key>,
+        callback: LoadInitialCallback<Key, Value>
+    )
+
+    /**
+     * Prepend page with the key specified by [LoadParams.key].
+     *
+     * It's valid to return a different list size than the page size if it's easier, e.g. if your
+     * backend defines page sizes. It is generally preferred to increase the number loaded than
+     * reduce.
+     *
+     * Data may be passed synchronously during the load method, or deferred and called at a later
+     * time. Further loads going down will be blocked until the callback is called.
+     *
+     * If data cannot be loaded (for example, if the request is invalid, or the data would be stale
+     * and inconsistent), it is valid to call [invalidate] to invalidate the data source, and
+     * prevent further loading.
+     *
+     * @param params Parameters for the load, including the key for the new page, and requested load
+     *               size.
+     * @param callback Callback that receives loaded data.
+     */
+    abstract fun loadBefore(params: LoadParams<Key>, callback: LoadCallback<Key, Value>)
+
+    /**
+     * Append page with the key specified by [LoadParams.key].
+     *
+     * It's valid to return a different list size than the page size if it's easier, e.g. if your
+     * backend defines page sizes. It is generally preferred to increase the number loaded than
+     * reduce.
+     *
+     * Data may be passed synchronously during the load method, or deferred and called at a later
+     * time. Further loads going down will be blocked until the callback is called.
+     *
+     * If data cannot be loaded (for example, if the request is invalid, or the data would be stale
+     * and inconsistent), it is valid to call [invalidate] to invalidate the data source, and
+     * prevent further loading.
+     *
+     * @param params Parameters for the load, including the key for the new page, and requested load
+     *               size.
+     * @param callback Callback that receives loaded data.
+     */
+    abstract fun loadAfter(params: LoadParams<Key>, callback: LoadCallback<Key, Value>)
+
+    final override fun <ToValue : Any> mapByPage(
+        function: Function<List<Value>, List<ToValue>>
+    ): PageKeyedDataSource<Key, ToValue> = WrapperPageKeyedDataSource(this, function)
+
+    final override fun <ToValue : Any> map(
+        function: Function<Value, ToValue>
+    ): PageKeyedDataSource<Key, ToValue> =
+        mapByPage(Function { list -> list.map { function.apply(it) } })
+}
diff --git a/paging/common/src/main/kotlin/androidx/paging/PagedList.kt b/paging/common/src/main/kotlin/androidx/paging/PagedList.kt
new file mode 100644
index 0000000..daf0c3b
--- /dev/null
+++ b/paging/common/src/main/kotlin/androidx/paging/PagedList.kt
@@ -0,0 +1,1370 @@
+/*
+ * 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.
+ */
+
+package androidx.paging
+
+import androidx.annotation.AnyThread
+import androidx.annotation.IntRange
+import androidx.annotation.MainThread
+import androidx.annotation.RestrictTo
+import androidx.annotation.VisibleForTesting
+import androidx.annotation.WorkerThread
+import androidx.arch.core.util.Function
+import androidx.paging.PagedList.Callback
+import androidx.paging.PagedList.Config
+import androidx.paging.PagedList.Config.Builder
+import androidx.paging.PagedList.Config.Companion.MAX_SIZE_UNBOUNDED
+import androidx.paging.futures.DirectExecutor
+import androidx.paging.futures.transform
+import com.google.common.util.concurrent.ListenableFuture
+import java.lang.ref.WeakReference
+import java.util.AbstractList
+import java.util.ArrayList
+import java.util.concurrent.ExecutionException
+import java.util.concurrent.Executor
+
+/**
+ * Lazy loading list that pages in immutable content from a [DataSource].
+ *
+ * A PagedList is a [List] which loads its data in chunks (pages) from a [DataSource]. Items can be
+ * accessed with [get], and further loading can be triggered with [loadAround]. To display a
+ * PagedList, see [androidx.paging.PagedListAdapter], which enables the binding of a PagedList to a
+ * [androidx.recyclerview.widget.RecyclerView].
+ *
+ * <h4>Loading Data</h4>
+ *
+ * All data in a PagedList is loaded from its [DataSource]. Creating a PagedList loads the
+ * first chunk of data from the DataSource immediately, and should for this reason be done on a
+ * background thread. The constructed PagedList may then be passed to and used on the UI thread.
+ * This is done to prevent passing a list with no loaded content to the UI thread, which should
+ * generally not be presented to the user.
+ *
+ * A [PagedList] initially presents this first partial load as its content, and expands over time as
+ * content is loaded in. When [loadAround] is called, items will be loaded in near the passed
+ * list index. If placeholder `null`s are present in the list, they will be replaced as
+ * content is loaded. If not, newly loaded items will be inserted at the beginning or end of the
+ * list.
+ *
+ * [PagedList] can present data for an unbounded, infinite scrolling list, or a very large but
+ * countable list. Use [Config] to control how many items a [PagedList] loads, and when.
+ *
+ * If you use [androidx.paging.LivePagedListBuilder] to get a [androidx.lifecycle.LiveData], it will
+ * initialize PagedLists on a background thread for you.
+ *
+ * <h4>Placeholders</h4>
+ *
+ * There are two ways that [PagedList] can represent its not-yet-loaded data - with or without
+ * `null` placeholders.
+ *
+ * With placeholders, the [PagedList] is always the full size of the data set. `get(N)` returns
+ * the `N`th item in the data set, or `null` if its not yet loaded.
+ *
+ * Without `null` placeholders, the [PagedList] is the sublist of data that has already been
+ * loaded. The size of the PagedList is the number of currently loaded items, and `get(N)`
+ * returns the `N`th *loaded* item. This is not necessarily the `N`th item in the
+ * data set.
+ *
+ * Placeholders have several benefits:
+ *
+ *  * They express the full sized list to the presentation layer (often a
+ * [androidx.paging.PagedListAdapter]), and so can support scrollbars (without jumping as pages are
+ * loaded or dropped) and fast-scrolling to any position, loaded or not.
+ *  * They avoid the need for a loading spinner at the end of the loaded list, since the list
+ * is always full sized.
+ *
+ * They also have drawbacks:
+ *
+ *  * Your Adapter needs to account for `null` items. This often means providing default
+ * values in data you bind to a [androidx.recyclerview.widget.RecyclerView.ViewHolder].
+ *  * They don't work well if your item views are of different sizes, as this will prevent
+ * loading items from cross-fading nicely.
+ *  * They require you to count your data set, which can be expensive or impossible, depending
+ * on your [DataSource].
+ *
+ * Placeholders are enabled by default, but can be disabled in two ways. They are disabled if the
+ * [DataSource] does not count its data set in its initial load, or if  `false` is passed to
+ * [Config.Builder.setEnablePlaceholders] when building a [Config].
+ *
+ * <h4>Mutability and Snapshots</h4>
+ *
+ * A [PagedList] is *mutable* while loading, or ready to load from its [DataSource].
+ * As loads succeed, a mutable [PagedList] will be updated via Runnables on the main thread. You can
+ * listen to these updates with a [Callback]. (Note that [androidx.paging.PagedListAdapter] will
+ * listen to these to signal RecyclerView about the updates/changes).
+ *
+ * If a [PagedList] attempts to load from an invalid [DataSource], it will [detach] from the
+ * [DataSource], meaning that it will no longer attempt to load data. It will return true from
+ * [isImmutable], and a new [DataSource] / [PagedList] pair must be created to load further data.
+ *
+ * See [DataSource] and [androidx.paging.LivePagedListBuilder] for how new PagedLists are created to
+ * represent changed data.
+ *
+ * A [PagedList] snapshot is simply an immutable shallow copy of the current state of the
+ * [PagedList] as a `List`. It will reference the same inner items, and contain the same `null`
+ * placeholders, if present.
+ *
+ * @param T The type of the entries in the list.
+ */
+abstract class PagedList<T : Any> : AbstractList<T> {
+    internal companion object {
+        /**
+         * Create a [PagedList] which loads data from the provided data source on a background
+         * thread,posting updates to the main thread.
+         *
+         * @param dataSource DataSource providing data to the PagedList
+         * @param notifyExecutor Thread tat will use and consume data from the PagedList. Generally,
+         *                       this is the UI/main thread.
+         * @param fetchExecutor Data loading will be done via this executor - should be a background
+         *                      thread.
+         * @param boundaryCallback Optional boundary callback to attach to the list.
+         * @param config PagedList Config, which defines how the PagedList will load data.
+         * @param K Key type that indicates to the DataSource what data to load.
+         * @param T Type of items to be held and loaded by the PagedList.
+         *
+         * @return [ListenableFuture] for newly created [PagedList], which will page in data from
+         * the [DataSource] as needed.
+         */
+        @JvmStatic
+        internal fun <K : Any, T : Any> create(
+            dataSource: DataSource<K, T>,
+            notifyExecutor: Executor,
+            fetchExecutor: Executor,
+            initialLoadExecutor: Executor,
+            boundaryCallback: BoundaryCallback<T>?,
+            config: Config,
+            key: K?
+        ): ListenableFuture<PagedList<T>> {
+            dataSource.initExecutor(initialLoadExecutor)
+
+            val lastLoad = when {
+                dataSource.type == DataSource.KeyType.POSITIONAL && key != null -> key as Int
+                else -> ContiguousPagedList.LAST_LOAD_UNSPECIFIED
+            }
+
+            val params = DataSource.Params(
+                DataSource.LoadType.INITIAL,
+                key,
+                config.initialLoadSizeHint,
+                config.enablePlaceholders,
+                config.pageSize
+            )
+            return dataSource.load(params).transform(
+                Function { initialResult ->
+                    dataSource.initExecutor(fetchExecutor)
+                    ContiguousPagedList(
+                        dataSource,
+                        notifyExecutor,
+                        fetchExecutor,
+                        boundaryCallback,
+                        config,
+                        initialResult,
+                        lastLoad
+                    )
+                },
+                DirectExecutor
+            )
+        }
+    }
+
+    /**
+     * Type of load a PagedList can perform.
+     *
+     * You can use a [LoadStateListener] to observe [LoadState] of any [LoadType]. For UI purposes
+     * (swipe refresh, loading spinner, retry button), this is typically done by registering a
+     * Listener with the [androidx.paging.PagedListAdapter] or
+     * [androidx.paging.AsyncPagedListDiffer].
+     *
+     * @see LoadState
+     */
+    enum class LoadType {
+        /**
+         * PagedList content being reloaded, may contain content updates.
+         */
+        REFRESH,
+
+        /**
+         * Load at the start of the PagedList.
+         */
+        START,
+
+        /**
+         * Load at the end of the PagedList.
+         */
+        END
+    }
+
+    /**
+     * State of a PagedList load - associated with a `LoadType`
+     *
+     * You can use a [LoadStateListener] to observe [LoadState] of any [LoadType]. For UI purposes
+     * (swipe refresh, loading spinner, retry button), this is typically done by registering a
+     * Listener with the `PagedListAdapter` or `AsyncPagedListDiffer`.
+     */
+    enum class LoadState {
+        /**
+         * Indicates the PagedList is not currently loading, and no error currently observed.
+         */
+        IDLE,
+
+        /**
+         * Loading is in progress.
+         */
+        LOADING,
+
+        /**
+         * Loading is complete.
+         */
+        DONE,
+
+        /**
+         * Loading hit a non-retryable error.
+         */
+        ERROR,
+
+        /**
+         * Loading hit a retryable error.
+         *
+         * @see .retry
+         */
+        RETRYABLE_ERROR
+    }
+
+    /**
+     * Listener for changes to loading state - whether the refresh, prepend, or append is idle,
+     * loading, or has an error.
+     *
+     * Can be used to observe the [LoadState] of any [LoadType] (REFRESH/START/END). For UI purposes
+     * (swipe refresh, loading spinner, retry button), this is typically done by registering a
+     * Listener with the [PagedListAdapter] or [AsyncPagedListDiffer].
+     *
+     * These calls will be dispatched on the executor defined by [Builder.setNotifyExecutor], which
+     * is generally the main/UI thread.
+     *
+     * @see LoadType
+     *
+     * @see LoadState
+     */
+    interface LoadStateListener {
+        /**
+         * Called when the LoadState has changed - whether the refresh, prepend, or append is idle,
+         * loading, or has an error.
+         *
+         * REFRESH events can be used to drive a
+         * [androidx.swiperefreshlayout.widget.SwipeRefreshLayout], or START/END events can be used
+         * to drive loading spinner items in your `RecyclerView`.
+         *
+         * @param type Type of load - START, END, or REFRESH.
+         * @param state State of load - IDLE, LOADING, DONE, ERROR, or RETRYABLE_ERROR
+         * @param error Error, if in an error state, null otherwise.
+         *
+         * @see [retry]
+         */
+        fun onLoadStateChanged(type: LoadType, state: LoadState, error: Throwable?)
+    }
+
+    /**
+     * Builder class for [PagedList].
+     *
+     * [DataSource], [Config], main thread and background executor must all be provided.
+     *
+     * A [PagedList] queries initial data from its [DataSource] during construction, to avoid empty
+     * PagedLists being presented to the UI when possible. It's preferred to present initial data,
+     * so that the UI doesn't show an empty list, or placeholders for a few frames, just before
+     * showing initial content.
+     *
+     * [androidx.paging.LivePagedListBuilder] does this creation on a background thread
+     * automatically, if you want to receive a `LiveData<PagedList<...>>`.
+     *
+     * @param Key Type of key used to load data from the [DataSource].
+     * @param Value Type of items held and loaded by the [PagedList].
+     */
+    class Builder<Key : Any, Value : Any> {
+        private val dataSource: DataSource<Key, Value>
+        private val config: Config
+        private var notifyExecutor: Executor? = null
+        private var fetchExecutor: Executor? = null
+        private var boundaryCallback: BoundaryCallback<Value>? = null
+        private var initialKey: Key? = null
+
+        /**
+         * Create a PagedList.Builder with the provided [DataSource] and [Config].
+         *
+         * @param dataSource [DataSource] the [PagedList] will load from.
+         * @param config [Config] that defines how the [PagedList] loads data from its [DataSource].
+         */
+        constructor(dataSource: DataSource<Key, Value>, config: Config) {
+            this.dataSource = dataSource
+            this.config = config
+        }
+
+        /**
+         * Create a [PagedList.Builder] with the provided [DataSource] and page size.
+         *
+         * This method is a convenience for:
+         * ```
+         * PagedList.Builder(dataSource,
+         *     new PagedList.Config.Builder().setPageSize(pageSize).build());
+         * ```
+         *
+         * @param dataSource [DataSource] the [PagedList] will load from.
+         * @param pageSize [Config] that defines how the [PagedList] loads data from its
+         *                 [DataSource].
+         */
+        constructor(dataSource: DataSource<Key, Value>, pageSize: Int) : this(
+            dataSource,
+            PagedList.Config.Builder().setPageSize(pageSize).build()
+        )
+
+        /**
+         * The executor defining where page loading updates are dispatched.
+         *
+         * @param notifyExecutor Executor that receives [PagedList] updates, and where [Callback]
+         *                       calls are dispatched. Generally, this is the ui/main thread.
+         * @return this
+         */
+        fun setNotifyExecutor(notifyExecutor: Executor) = apply {
+            this.notifyExecutor = notifyExecutor
+        }
+
+        /**
+         * The executor used to fetch additional pages from the [DataSource].
+         *
+         * Does not affect initial load, which will be done immediately on whichever thread the
+         * [PagedList] is created on.
+         *
+         * @param fetchExecutor [Executor] used to fetch from [DataSources], generally a background
+         *                      thread pool for e.g. I/O or network loading.
+         * @return this
+         */
+        fun setFetchExecutor(fetchExecutor: Executor) = apply {
+            this.fetchExecutor = fetchExecutor
+        }
+
+        /**
+         * The [BoundaryCallback] for out of data events.
+         *
+         * Pass a [BoundaryCallback] to listen to when the [PagedList] runs out of data to load.
+         *
+         * @param boundaryCallback [BoundaryCallback] for listening to out-of-data events.
+         * @return this
+         */
+        fun setBoundaryCallback(boundaryCallback: BoundaryCallback<Value>?) = apply {
+            this.boundaryCallback = boundaryCallback
+        }
+
+        /**
+         * Sets the initial key the [DataSource] should load around as part of initialization.
+         *
+         * @param initialKey Key the [DataSource] should load around as part of initialization.
+         * @return this
+         */
+        fun setInitialKey(initialKey: Key?) = apply {
+            this.initialKey = initialKey
+        }
+
+        /**
+         * Creates a [PagedList] with the given parameters.
+         *
+         * This call will dispatch the [androidx.paging.DataSource]'s loadInitial method immediately
+         * on the current thread, and block the current on the result. This method should always be
+         * called on a worker thread to prevent blocking the main thread.
+         *
+         * It's fine to create a PagedList with an async DataSource on the main thread, such as in
+         * the constructor of a ViewModel. An async network load won't block the initialLoad
+         * function. For a synchronous DataSource such as one created from a Room database, a
+         * `LiveData<PagedList>` can be safely constructed with
+         * [androidx.paging.LivePagedListBuilder] on the main thread, since actual construction work
+         * is deferred, and done on a background thread.
+         *
+         * While build() will always return a [PagedList], it's important to note that the
+         * [PagedList] initial load may fail to acquire data from the [DataSource]. This can happen
+         * for example if the [DataSource] is invalidated during its initial load. If this happens,
+         * the [PagedList] will be immediately [detached][PagedList.isDetached], and you can retry
+         * construction (including setting a new [DataSource]).
+         *
+         * @return The newly constructed [PagedList]
+         */
+        @WorkerThread
+        @Deprecated(
+            "This method has no means of handling errors encountered during initial load, and" +
+                    " blocks on the initial load result. Use {@link #buildAsync()} instead."
+        )
+        fun build(): PagedList<Value> {
+            // TODO: define defaults, once they can be used in module without android dependency
+            if (notifyExecutor == null) {
+                throw IllegalArgumentException("MainThreadExecutor required")
+            }
+            if (fetchExecutor == null) {
+                throw IllegalArgumentException("BackgroundThreadExecutor required")
+            }
+
+            try {
+                return create(DirectExecutor).get()
+            } catch (e: InterruptedException) {
+                throw RuntimeException(e)
+            } catch (e: ExecutionException) {
+                throw RuntimeException(e)
+            }
+        }
+
+        /**
+         * Creates a [PagedList] asynchronously with the given parameters.
+         *
+         * This call will dispatch the [DataSource]'s loadInitial method immediately, and
+         * return a `ListenableFuture<PagedList<T>>` that will resolve (triggering listeners)
+         * once the initial load is completed (success or failure).
+         *
+         * @return The newly constructed PagedList
+         */
+        @Suppress("unused")
+        fun buildAsync(): ListenableFuture<PagedList<Value>> {
+            // TODO: define defaults, once they can be used in module without android dependency
+            if (notifyExecutor == null) {
+                throw IllegalArgumentException("MainThreadExecutor required")
+            }
+            if (fetchExecutor == null) {
+                throw IllegalArgumentException("BackgroundThreadExecutor required")
+            }
+
+            return create(fetchExecutor!!)
+        }
+
+        private fun create(initialFetchExecutor: Executor): ListenableFuture<PagedList<Value>> =
+            create(
+                dataSource,
+                notifyExecutor!!,
+                fetchExecutor!!,
+                initialFetchExecutor,
+                boundaryCallback,
+                config,
+                initialKey
+            )
+    }
+
+    /**
+     * Callback signaling when content is loaded into the list.
+     *
+     * Can be used to listen to items being paged in and out. These calls will be dispatched on
+     * the executor defined by [Builder.setNotifyExecutor], which is generally the main/UI thread.
+     */
+    abstract class Callback {
+        /**
+         * Called when null padding items have been loaded to signal newly available data, or when
+         * data that hasn't been used in a while has been dropped, and swapped back to null.
+         *
+         * @param position Position of first newly loaded items, out of total number of items
+         *                 (including padded nulls).
+         * @param count Number of items loaded.
+         */
+        abstract fun onChanged(position: Int, count: Int)
+
+        /**
+         * Called when new items have been loaded at the end or beginning of the list.
+         *
+         * @param position Position of the first newly loaded item (in practice, either `0` or
+         *                 `size - 1`.
+         * @param count Number of items loaded.
+         */
+        abstract fun onInserted(position: Int, count: Int)
+
+        /**
+         * Called when items have been removed at the end or beginning of the list, and have not
+         * been replaced by padded nulls.
+         *
+         * @param position Position of the first newly loaded item (in practice, either `0` or
+         *                 `size - 1`.
+         * @param count Number of items loaded.
+         */
+        abstract fun onRemoved(position: Int, count: Int)
+    }
+
+    /**
+     * Configures how a [PagedList] loads content from its [DataSource].
+     *
+     * Use [Config.Builder] to construct and define custom loading behavior, such as
+     * [Builder.setPageSize], which defines number of items loaded at a time}.
+     */
+    open class Config internal constructor(
+        /**
+         * Size of each page loaded by the PagedList.
+         */
+        @JvmField
+        val pageSize: Int,
+        /**
+         * Prefetch distance which defines how far ahead to load.
+         *
+         * If this value is set to 50, the paged list will attempt to load 50 items in advance of
+         * data that's already been accessed.
+         *
+         * @see PagedList.loadAround
+         */
+        @JvmField
+        val prefetchDistance: Int,
+        /**
+         * Defines whether the PagedList may display null placeholders, if the DataSource provides
+         * them.
+         */
+        @JvmField
+        val enablePlaceholders: Boolean,
+        /**
+         * Size hint for initial load of PagedList, often larger than a regular page.
+         */
+        @JvmField
+        val initialLoadSizeHint: Int,
+        /**
+         * Defines the maximum number of items that may be loaded into this pagedList before pages
+         * should be dropped.
+         *
+         * [PageKeyedDataSource] does not currently support dropping pages - when loading from a
+         * [PageKeyedDataSource], this value is ignored.
+         *
+         * @see MAX_SIZE_UNBOUNDED
+         * @see Builder.setMaxSize
+         */
+        @JvmField
+        val maxSize: Int
+    ) {
+        /**
+         * Builder class for [Config].
+         *
+         * You must at minimum specify page size with [setPageSize].
+         */
+        class Builder {
+            private var pageSize = -1
+            private var prefetchDistance = -1
+            private var initialLoadSizeHint = -1
+            private var enablePlaceholders = true
+            private var maxSize = MAX_SIZE_UNBOUNDED
+
+            /**
+             * Defines the number of items loaded at once from the [DataSource].
+             *
+             * Should be several times the number of visible items onscreen.
+             *
+             * Configuring your page size depends on how your data is being loaded and used. Smaller
+             * page sizes improve memory usage, latency, and avoid GC churn. Larger pages generally
+             * improve loading throughput, to a point (avoid loading more than 2MB from SQLite at
+             * once, since it incurs extra cost).
+             *
+             * If you're loading data for very large, social-media style cards that take up most of
+             * a screen, and your database isn't a bottleneck, 10-20 may make sense. If you're
+             * displaying dozens of items in a tiled grid, which can present items during a scroll
+             * much more quickly, consider closer to 100.
+             *
+             * @param pageSize Number of items loaded at once from the [DataSource].
+             * @return this
+             */
+            fun setPageSize(@IntRange(from = 1) pageSize: Int) = apply {
+                if (pageSize < 1) {
+                    throw IllegalArgumentException("Page size must be a positive number")
+                }
+                this.pageSize = pageSize
+            }
+
+            /**
+             * Defines how far from the edge of loaded content an access must be to trigger further
+             * loading.
+             *
+             * Should be several times the number of visible items onscreen.
+             *
+             * If not set, defaults to page size.
+             *
+             * A value of 0 indicates that no list items will be loaded until they are specifically
+             * requested. This is generally not recommended, so that users don't observe a
+             * placeholder item (with placeholders) or end of list (without) while scrolling.
+             *
+             * @param prefetchDistance Distance the [PagedList] should prefetch.
+             * @return this
+             */
+            fun setPrefetchDistance(@IntRange(from = 0) prefetchDistance: Int) = apply {
+                this.prefetchDistance = prefetchDistance
+            }
+
+            /**
+             * Pass false to disable null placeholders in [PagedLists] using this [Config].
+             *
+             * If not set, defaults to true.
+             *
+             * A [PagedList] will present null placeholders for not-yet-loaded content if two
+             * conditions are met:
+             *
+             * 1) Its [DataSource] can count all unloaded items (so that the number of nulls to
+             * present is known).
+             *
+             * 2) placeholders are not disabled on the [Config].
+             *
+             * Call `setEnablePlaceholders(false)` to ensure the receiver of the PagedList
+             * (often a [androidx.paging.PagedListAdapter]) doesn't need to account for null items.
+             *
+             * If placeholders are disabled, not-yet-loaded content will not be present in the list.
+             * Paging will still occur, but as items are loaded or removed, they will be signaled
+             * as inserts to the [PagedList.Callback].
+             *
+             * [PagedList.Callback.onChanged] will not be issued as part of loading, though a
+             * [androidx.paging.PagedListAdapter] may still receive change events as a result of
+             * [PagedList] diffing.
+             *
+             * @param enablePlaceholders `false` if null placeholders should be disabled.
+             * @return this
+             */
+            fun setEnablePlaceholders(enablePlaceholders: Boolean) = apply {
+                this.enablePlaceholders = enablePlaceholders
+            }
+
+            /**
+             * Defines how many items to load when first load occurs.
+             *
+             * This value is typically larger than page size, so on first load data there's a large
+             * enough range of content loaded to cover small scrolls.
+             *
+             * When using a [PositionalDataSource], the initial load size will be coerced to an
+             * integer multiple of pageSize, to enable efficient tiling.
+             *
+             * If not set, defaults to three times page size.
+             *
+             * @param initialLoadSizeHint Number of items to load while initializing the
+             *                            [PagedList].
+             * @return this
+             */
+            fun setInitialLoadSizeHint(@IntRange(from = 1) initialLoadSizeHint: Int) = apply {
+                this.initialLoadSizeHint = initialLoadSizeHint
+            }
+
+            /**
+             * Defines how many items to keep loaded at once.
+             *
+             * This can be used to cap the number of items kept in memory by dropping pages. This
+             * value is typically many pages so old pages are cached in case the user scrolls back.
+             *
+             * This value must be at least two times the [prefetchDistance][setPrefetchDistance]
+             * plus the [pageSize][setPageSize]). This constraint prevent loads from being
+             * continuously fetched and discarded due to prefetching.
+             *
+             * The max size specified here best effort, not a guarantee. In practice, if [maxSize]
+             * is many times the page size, the number of items held by the [PagedList] will not
+             * grow above this number. Exceptions are made as necessary to guarantee:
+             *  * Pages are never dropped until there are more than two pages loaded. Note that
+             * a [DataSource] may not be held strictly to [requested pageSize][Config.pageSize], so
+             * two pages may be larger than expected.
+             *  * Pages are never dropped if they are within a prefetch window (defined to be
+             * `pageSize + (2 * prefetchDistance)`) of the most recent load.
+             *
+             * [PageKeyedDataSource] does not currently support dropping pages - when
+             * loading from a [PageKeyedDataSource], this value is ignored.
+             *
+             * If not set, defaults to [MAX_SIZE_UNBOUNDED], which disables page dropping.
+             *
+             * @param maxSize Maximum number of items to keep in memory, or [MAX_SIZE_UNBOUNDED] to
+             *                disable page dropping.
+             * @return this
+             *
+             * @see Config.MAX_SIZE_UNBOUNDED
+             * @see Config.maxSize
+             */
+            fun setMaxSize(@IntRange(from = 2) maxSize: Int) = apply {
+                this.maxSize = maxSize
+            }
+
+            /**
+             * Creates a [Config] with the given parameters.
+             *
+             * @return A new [Config].
+             */
+            fun build(): Config {
+                if (prefetchDistance < 0) {
+                    prefetchDistance = pageSize
+                }
+                if (initialLoadSizeHint < 0) {
+                    initialLoadSizeHint = pageSize * DEFAULT_INITIAL_PAGE_MULTIPLIER
+                }
+                if (!enablePlaceholders && prefetchDistance == 0) {
+                    throw IllegalArgumentException(
+                        "Placeholders and prefetch are the only ways" +
+                                " to trigger loading of more data in the PagedList, so either" +
+                                " placeholders must be enabled, or prefetch distance must be > 0."
+                    )
+                }
+                if (maxSize != MAX_SIZE_UNBOUNDED && maxSize < pageSize + prefetchDistance * 2) {
+                    throw IllegalArgumentException(
+                        "Maximum size must be at least pageSize + 2*prefetchDist" +
+                                ", pageSize=$pageSize, prefetchDist=$prefetchDistance" +
+                                ", maxSize=$maxSize"
+                    )
+                }
+
+                return Config(
+                    pageSize,
+                    prefetchDistance,
+                    enablePlaceholders,
+                    initialLoadSizeHint,
+                    maxSize
+                )
+            }
+
+            internal companion object {
+                internal const val DEFAULT_INITIAL_PAGE_MULTIPLIER = 3
+            }
+        }
+
+        internal companion object {
+            /**
+             * When [maxSize] is set to [MAX_SIZE_UNBOUNDED], the maximum number of items loaded is
+             * unbounded, and pages will never be dropped.
+             */
+            const val MAX_SIZE_UNBOUNDED = Int.MAX_VALUE
+        }
+    }
+
+    /**
+     * Signals when a PagedList has reached the end of available data.
+     *
+     * When local storage is a cache of network data, it's common to set up a streaming pipeline:
+     * Network data is paged into the database, database is paged into UI. Paging from the database
+     * to UI can be done with a `LiveData<PagedList>`, but it's still necessary to know when to
+     * trigger network loads.
+     *
+     * [BoundaryCallback] does this signaling - when a DataSource runs out of data at the end of
+     * the list, [onItemAtEndLoaded] is called, and you can start an async network load that will
+     * write the result directly to the database. Because the database is being observed, the UI
+     * bound to the `LiveData<PagedList>` will update automatically to account for the new items.
+     *
+     * Note that a BoundaryCallback instance shared across multiple PagedLists (e.g. when passed to
+     * [androidx.paging.LivePagedListBuilder.setBoundaryCallback], the callbacks may be issued
+     * multiple times. If for example [onItemAtEndLoaded] triggers a network load, it should avoid
+     * triggering it again while the load is ongoing.
+     *
+     * The database + network Repository in the
+     * [PagingWithNetworkSample](https://github.com/googlesamples/android-architecture-components/blob/master/PagingWithNetworkSample/README.md)
+     * shows how to implement a network BoundaryCallback using
+     * [Retrofit](https://square.github.io/retrofit/), while handling swipe-to-refresh,
+     * network errors, and retry.
+     *
+     * <h4>Requesting Network Data</h4>
+     * [BoundaryCallback] only passes the item at front or end of the list when out of data. This
+     * makes it an easy fit for item-keyed network requests, where you can use the item passed to
+     * the [BoundaryCallback] to request more data from the network. In these cases, the source of
+     * truth for next page to load is coming from local storage, based on what's already loaded.
+     *
+     * If you aren't using an item-keyed network API, you may be using page-keyed, or page-indexed.
+     * If this is the case, the paging library doesn't know about the page key or index used in the
+     * [BoundaryCallback], so you need to track it yourself. You can do this in one of two ways:
+     *
+     * <h5>Local storage Page key</h5>
+     * If you want to perfectly resume your query, even if the app is killed and resumed, you can
+     * store the key on disk. Note that with a positional/page index network API, there's a simple
+     * way to do this, by using the `listSize` as an input to the next load (or
+     * `listSize / NETWORK_PAGE_SIZE`, for page indexing).
+     *
+     * The current list size isn't passed to the BoundaryCallback though. This is because the
+     * PagedList doesn't necessarily know the number of items in local storage. Placeholders may be
+     * disabled, or the DataSource may not count total number of items.
+     *
+     * Instead, for these positional cases, you can query the database for the number of items, and
+     * pass that to the network.
+     * <h5>In-Memory Page key</h5>
+     * Often it doesn't make sense to query the next page from network if the last page you fetched
+     * was loaded many hours or days before. If you keep the key in memory, you can refresh any time
+     * you start paging from a network source.
+     *
+     * Store the next key in memory, inside your BoundaryCallback. When you create a new
+     * BoundaryCallback when creating a new `LiveData`/`Observable` of
+     * `PagedList`, refresh data. For example,
+     * [in the Paging Codelab](https://codelabs.developers.google.com/codelabs/android-paging/index.html#8),
+     * the GitHub network page index is stored in memory.
+     *
+     * @param T Type loaded by the PagedList.
+     */
+    @MainThread
+    abstract class BoundaryCallback<T> {
+        /**
+         * Called when zero items are returned from an initial load of the PagedList's data source.
+         */
+        open fun onZeroItemsLoaded() {}
+
+        /**
+         * Called when the item at the front of the PagedList has been loaded, and access has
+         * occurred within [Config.prefetchDistance] of it.
+         *
+         * No more data will be prepended to the PagedList before this item.
+         *
+         * @param itemAtFront The first item of PagedList
+         */
+        open fun onItemAtFrontLoaded(itemAtFront: T) {}
+
+        /**
+         * Called when the item at the end of the PagedList has been loaded, and access has
+         * occurred within [Config.prefetchDistance] of it.
+         *
+         * No more data will be appended to the [PagedList] after this item.
+         *
+         * @param itemAtEnd The first item of [PagedList]
+         */
+        open fun onItemAtEndLoaded(itemAtEnd: T) {}
+    }
+
+    internal abstract class LoadStateManager {
+        var refresh = LoadState.IDLE
+            private set
+        private var mRefreshError: Throwable? = null
+        var start = LoadState.IDLE
+            private set
+        private var mStartError: Throwable? = null
+        var end = LoadState.IDLE
+            private set
+        private var mEndError: Throwable? = null
+
+        fun setState(type: LoadType, state: LoadState, error: Throwable?) {
+            val expectError = state == LoadState.RETRYABLE_ERROR || state == LoadState.ERROR
+            val hasError = error != null
+            if (expectError != hasError) {
+                throw IllegalArgumentException(
+                    "Error states must be accompanied by a throwable, other states must not"
+                )
+            }
+
+            // deduplicate signals
+            when (type) {
+                LoadType.REFRESH -> {
+                    if (refresh == state && mRefreshError == error) return
+                    refresh = state
+                    mRefreshError = error
+                }
+                LoadType.START -> {
+                    if (start == state && mStartError == error) return
+                    start = state
+                    mStartError = error
+                }
+                LoadType.END -> {
+                    if (end == state && mEndError == error) return
+                    end = state
+                    mEndError = error
+                }
+            }
+            onStateChanged(type, state, error)
+        }
+
+        protected abstract fun onStateChanged(type: LoadType, state: LoadState, error: Throwable?)
+
+        fun dispatchCurrentLoadState(listener: LoadStateListener) {
+            listener.onLoadStateChanged(LoadType.REFRESH, refresh, mRefreshError)
+            listener.onLoadStateChanged(LoadType.START, start, mStartError)
+            listener.onLoadStateChanged(LoadType.END, end, mEndError)
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    constructor(
+        storage: PagedStorage<T>,
+        mainThreadExecutor: Executor,
+        backgroundThreadExecutor: Executor,
+        boundaryCallback: BoundaryCallback<T>?,
+        config: Config
+    ) : super() {
+        this.storage = storage
+        this.mainThreadExecutor = mainThreadExecutor
+        this.backgroundThreadExecutor = backgroundThreadExecutor
+        this.boundaryCallback = boundaryCallback
+        this.config = config
+        this.callbacks = ArrayList()
+        this.listeners = ArrayList()
+        requiredRemainder = this.config.prefetchDistance * 2 + this.config.pageSize
+    }
+
+    internal val storage: PagedStorage<T>
+
+    /**
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    protected fun getStorage() = storage
+
+    internal val mainThreadExecutor: Executor
+    internal val backgroundThreadExecutor: Executor
+    internal val boundaryCallback: BoundaryCallback<T>?
+
+    internal var refreshRetryCallback: Runnable? = null
+
+    /**
+     * Last access location, in total position space (including offset).
+     *
+     * Used by positional data sources to initialize loading near viewport
+     */
+    internal var lastLoad = 0
+    internal var lastItem: T? = null
+
+    internal val requiredRemainder: Int
+
+    /**
+     * Return the Config used to construct this PagedList.
+     *
+     * @return the Config of this PagedList
+     */
+    open val config: Config
+
+    private val callbacks: MutableList<WeakReference<Callback>>
+
+    private val listeners: MutableList<WeakReference<LoadStateListener>>
+
+    // if set to true, boundaryCallback is non-null, and should
+    // be dispatched when nearby load has occurred
+    private var boundaryCallbackBeginDeferred = false
+
+    private var boundaryCallbackEndDeferred = false
+
+    // lowest and highest index accessed by loadAround. Used to
+    // decide when boundaryCallback should be dispatched
+    private var lowestIndexAccessed = Int.MAX_VALUE
+    private var highestIndexAccessed = Int.MIN_VALUE
+
+    /**
+     * Size of the list, including any placeholders (not-yet-loaded null padding).
+     *
+     * To get the number of loaded items, not counting placeholders, use [loadedCount].
+     *
+     * @see loadedCount
+     */
+    override val size
+        get() = storage.size
+
+    @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
+    abstract val isContiguous: Boolean
+
+    /**
+     * The [DataSource] that provides data to this [PagedList].
+     */
+    abstract val dataSource: DataSource<*, T>
+
+    /**
+     * Return the key for the position passed most recently to [loadAround].
+     *
+     * When a PagedList is invalidated, you can pass the key returned by this function to initialize
+     * the next PagedList. This ensures (depending on load times) that the next PagedList that
+     * arrives will have data that overlaps. If you use androidx.paging.LivePagedListBuilder, it
+     * will do this for you.
+     *
+     * @return Key of position most recently passed to [loadAround].
+     */
+    abstract val lastKey: Any?
+
+    /**
+     * True if the [PagedList] has detached the [DataSource] it was loading from, and will no longer
+     * load new data.
+     *
+     * A detached list is [immutable][isImmutable].
+     *
+     * @return True if the data source is detached.
+     */
+    abstract val isDetached: Boolean
+
+    /**
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    abstract fun dispatchCurrentLoadState(listener: LoadStateListener)
+
+    /**
+     * Dispatch updates since the non-empty snapshot was taken.
+     *
+     * @param snapshot Non-empty snapshot.
+     * @param callback [Callback] for updates that have occurred since snapshot.
+     *
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    abstract fun dispatchUpdatesSinceSnapshot(snapshot: PagedList<T>, callback: Callback)
+
+    /**
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    abstract fun loadAroundInternal(index: Int)
+
+    /**
+     * Detach the PagedList from its DataSource, and attempt to load no more data.
+     *
+     * This is called automatically when a DataSource is observed to be invalid, which is a
+     * signal to stop loading. The PagedList will continue to present existing data, but will not
+     * initiate new loads.
+     */
+    abstract fun detach()
+
+    /**
+     * Returns the number of items loaded in the [PagedList].
+     *
+     * Unlike [size] this counts only loaded items, not placeholders.
+     *
+     * If placeholders are [disabled][Config.enablePlaceholders], this method is equivalent to
+     * [size].
+     *
+     * @return Number of items currently loaded, not counting placeholders.
+     *
+     * @see size
+     */
+    open val loadedCount
+        get() = storage.loadedCount
+
+    /**
+     * Returns whether the list is immutable.
+     *
+     * Immutable lists may not become mutable again, and may safely be accessed from any thread.
+     *
+     * In the future, this method may return true when a PagedList has completed loading from its
+     * DataSource. Currently, it is equivalent to [isDetached].
+     *
+     * @return `true` if the [PagedList] is immutable.
+     */
+    open val isImmutable
+        get() = isDetached
+
+    /**
+     * Position offset of the data in the list.
+     *
+     * If data is supplied by a [PositionalDataSource], the item returned from `get(i)` has a
+     * position of `i + getPositionOffset()`.
+     *
+     * If the DataSource is a [ItemKeyedDataSource] or [PageKeyedDataSource], it doesn't use
+     * positions, returns 0.
+     */
+    open val positionOffset: Int
+        get() = storage.positionOffset
+
+    internal open fun setInitialLoadState(loadState: LoadState, error: Throwable?) {}
+
+    /**
+     * Retry any retryable errors associated with this [PagedList].
+     *
+     * If for example a network DataSource append timed out, calling this method will retry the
+     * failed append load. Note that your DataSource will need to pass `true` to `onError()` to
+     * signify the error as retryable.
+     *
+     * You can observe loading state via [addWeakLoadStateListener], though generally this is done
+     * through the [PagedListAdapter][androidx.paging.PagedListAdapter] or
+     * [AsyncPagedListDiffer][androidx.paging.AsyncPagedListDiffer].
+     *
+     * @see addWeakLoadStateListener
+     * @see removeWeakLoadStateListener
+     */
+    open fun retry() {}
+
+    /**
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    fun setRetryCallback(refreshRetryCallback: Runnable?) {
+        this.refreshRetryCallback = refreshRetryCallback
+    }
+
+    internal fun dispatchStateChange(type: LoadType, state: LoadState, error: Throwable?) {
+        for (i in listeners.indices.reversed()) {
+            val currentListener = listeners[i].get()
+            currentListener?.onLoadStateChanged(type, state, error) ?: listeners.removeAt(i)
+        }
+    }
+
+    /**
+     * Get the item in the list of loaded items at the provided index.
+     *
+     * @param index Index in the loaded item list. Must be >= 0, and < [size]
+     * @return The item at the passed index, or `null` if a `null` placeholder is at the specified
+     * position.
+     *
+     * @see size
+     */
+    override fun get(index: Int): T? {
+        val item = storage[index]
+        if (item != null) {
+            lastItem = item
+        }
+        return item
+    }
+
+    /**
+     * Load adjacent items to passed index.
+     *
+     * @param index Index at which to load.
+     */
+    open fun loadAround(index: Int) {
+        if (index < 0 || index >= size) {
+            throw IndexOutOfBoundsException("Index: $index, Size: $size")
+        }
+
+        lastLoad = index + positionOffset
+        loadAroundInternal(index)
+
+        lowestIndexAccessed = minOf(lowestIndexAccessed, index)
+        highestIndexAccessed = maxOf(highestIndexAccessed, index)
+
+        /*
+         * lowestIndexAccessed / highestIndexAccessed have been updated, so check if we need to
+         * dispatch boundary callbacks. Boundary callbacks are deferred until last items are loaded,
+         * and accesses happen near the boundaries.
+         *
+         * Note: we post here, since RecyclerView may want to add items in response, and this
+         * call occurs in PagedListAdapter bind.
+         */
+        tryDispatchBoundaryCallbacks(true)
+    }
+
+    // Creation thread for initial synchronous load, otherwise main thread
+    // Safe to access main thread only state - no other thread has reference during construction
+    @AnyThread
+    internal fun deferBoundaryCallbacks(
+        deferEmpty: Boolean,
+        deferBegin: Boolean,
+        deferEnd: Boolean
+    ) {
+        if (boundaryCallback == null) {
+            throw IllegalStateException("Can't defer BoundaryCallback, no instance")
+        }
+
+        /*
+         * If lowest/highest haven't been initialized, set them to storage size,
+         * since placeholders must already be computed by this point.
+         *
+         * This is just a minor optimization so that BoundaryCallback callbacks are sent immediately
+         * if the initial load size is smaller than the prefetch window (see
+         * TiledPagedListTest#boundaryCallback_immediate())
+         */
+        if (lowestIndexAccessed == Int.MAX_VALUE) {
+            lowestIndexAccessed = storage.size
+        }
+        if (highestIndexAccessed == Int.MIN_VALUE) {
+            highestIndexAccessed = 0
+        }
+
+        if (deferEmpty || deferBegin || deferEnd) {
+            // Post to the main thread, since we may be on creation thread currently
+            mainThreadExecutor.execute {
+                // on is dispatched immediately, since items won't be accessed
+
+                if (deferEmpty) {
+                    boundaryCallback.onZeroItemsLoaded()
+                }
+
+                // for other callbacks, mark deferred, and only dispatch if loadAround
+                // has been called near to the position
+                if (deferBegin) {
+                    boundaryCallbackBeginDeferred = true
+                }
+                if (deferEnd) {
+                    boundaryCallbackEndDeferred = true
+                }
+                tryDispatchBoundaryCallbacks(false)
+            }
+        }
+    }
+
+    /**
+     * Call this when lowest/HighestIndexAccessed are changed, or boundaryCallbackBegin/EndDeferred
+     * is set.
+     */
+    @Suppress("MemberVisibilityCanBePrivate") // synthetic access
+    internal fun tryDispatchBoundaryCallbacks(post: Boolean) {
+        val dispatchBegin =
+            boundaryCallbackBeginDeferred && lowestIndexAccessed <= config.prefetchDistance
+        val dispatchEnd = boundaryCallbackEndDeferred &&
+                highestIndexAccessed >= size - 1 - config.prefetchDistance
+
+        if (!dispatchBegin && !dispatchEnd) {
+            return
+        }
+
+        if (dispatchBegin) {
+            boundaryCallbackBeginDeferred = false
+        }
+        if (dispatchEnd) {
+            boundaryCallbackEndDeferred = false
+        }
+        if (post) {
+            mainThreadExecutor.execute { dispatchBoundaryCallbacks(dispatchBegin, dispatchEnd) }
+        } else {
+            dispatchBoundaryCallbacks(dispatchBegin, dispatchEnd)
+        }
+    }
+
+    @Suppress("MemberVisibilityCanBePrivate") // synthetic access
+    internal fun dispatchBoundaryCallbacks(begin: Boolean, end: Boolean) {
+        // safe to deref boundaryCallback here, since we only defer if boundaryCallback present
+        if (begin) {
+            boundaryCallback!!.onItemAtFrontLoaded(storage.firstLoadedItem!!)
+        }
+        if (end) {
+            boundaryCallback!!.onItemAtEndLoaded(storage.lastLoadedItem!!)
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+    internal fun offsetAccessIndices(offset: Int) {
+        // update last loadAround index
+        lastLoad += offset
+
+        // update access range
+        lowestIndexAccessed += offset
+        highestIndexAccessed += offset
+    }
+
+    /**
+     * Returns an immutable snapshot of the [PagedList] in its current state.
+     *
+     * If this [PagedList] [is immutable][isImmutable] due to its DataSource being invalid, it will
+     * be returned.
+     *
+     * @return Immutable snapshot of PagedList data.
+     */
+    open fun snapshot(): List<T> = when {
+        isImmutable -> this
+        else -> SnapshotPagedList(this)
+    }
+
+    /**
+     * Add a [LoadStateListener] to observe the loading state of the [PagedList].
+     *
+     * @param listener Listener to receive updates.
+     *
+     * @see removeWeakLoadStateListener
+     */
+    open fun addWeakLoadStateListener(listener: LoadStateListener) {
+        // first, clean up any empty weak refs
+        for (i in listeners.indices.reversed()) {
+            val currentListener = listeners[i].get()
+            if (currentListener == null) {
+                listeners.removeAt(i)
+            }
+        }
+
+        // then add the new one
+        listeners.add(WeakReference(listener))
+        dispatchCurrentLoadState(listener)
+    }
+
+    /**
+     * Remove a previously registered [LoadStateListener].
+     *
+     * @param listener Previously registered listener.
+     * @see addWeakLoadStateListener
+     */
+    open fun removeWeakLoadStateListener(listener: LoadStateListener) {
+        for (i in listeners.indices.reversed()) {
+            val currentListener = listeners[i].get()
+            if (currentListener == null || currentListener === listener) {
+                // found Listener, or empty weak ref
+                listeners.removeAt(i)
+            }
+        }
+    }
+
+    /**
+     * Adds a callback, and issues updates since the [previousSnapshot] was created.
+     *
+     * If [previousSnapshot] is passed, the [callback] will also immediately be dispatched any
+     * differences between the previous snapshot, and the current state. For example, if the
+     * previousSnapshot was of 5 nulls, 10 items, 5 nulls, and the current state was 5 nulls,
+     * 12 items, 3 nulls, the callback would immediately receive a call of`onChanged(14, 2)`.
+     *
+     * This allows an observer that's currently presenting a snapshot to catch up to the most recent
+     * version, including any changes that may have been made.
+     *
+     * The callback is internally held as weak reference, so [PagedList] doesn't hold a strong
+     * reference to its observer, such as a [androidx.paging.PagedListAdapter]. If an adapter were
+     * held with a strong reference, it would be necessary to clear its [PagedList] observer before
+     * it could be GC'd.
+     *
+     * @param previousSnapshot Snapshot previously captured from this List, or `null`.
+     * @param callback Callback to dispatch to.
+     *
+     * @see removeWeakCallback
+     */
+    open fun addWeakCallback(previousSnapshot: List<T>?, callback: Callback) {
+        if (previousSnapshot != null && previousSnapshot !== this) {
+            if (previousSnapshot.isEmpty()) {
+                if (!storage.isEmpty()) {
+                    // If snapshot is empty, diff is trivial - just notify number new items.
+                    // Note: occurs in async init, when snapshot taken before init page arrives
+                    callback.onInserted(0, storage.size)
+                }
+            } else {
+                val storageSnapshot = previousSnapshot as PagedList<T>
+                dispatchUpdatesSinceSnapshot(storageSnapshot, callback)
+            }
+        }
+
+        // first, clean up any empty weak refs
+        for (i in callbacks.indices.reversed()) {
+            val currentCallback = callbacks[i].get()
+            if (currentCallback == null) {
+                callbacks.removeAt(i)
+            }
+        }
+
+        // then add the new one
+        callbacks.add(WeakReference(callback))
+    }
+
+    /**
+     * Removes a previously added callback.
+     *
+     * @param callback Callback, previously added.
+     * @see addWeakCallback
+     */
+    open fun removeWeakCallback(callback: Callback) {
+        for (i in callbacks.indices.reversed()) {
+            val currentCallback = callbacks[i].get()
+            if (currentCallback == null || currentCallback === callback) {
+                // found callback, or empty weak ref
+                callbacks.removeAt(i)
+            }
+        }
+    }
+
+    internal fun notifyInserted(position: Int, count: Int) {
+        if (count == 0) return
+
+        for (i in callbacks.indices.reversed()) {
+            val callback = callbacks[i].get()
+            callback?.onInserted(position, count)
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    fun notifyChanged(position: Int, count: Int) {
+        if (count != 0) {
+            for (i in callbacks.indices.reversed()) {
+                callbacks[i].get()?.onChanged(position, count)
+            }
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    fun notifyRemoved(position: Int, count: Int) {
+        if (count != 0) {
+            for (i in callbacks.indices.reversed()) {
+                callbacks[i].get()?.onRemoved(position, count)
+            }
+        }
+    }
+}
diff --git a/paging/common/ktx/src/main/java/androidx/paging/PagedListConfig.kt b/paging/common/src/main/kotlin/androidx/paging/PagedListConfig.kt
similarity index 76%
rename from paging/common/ktx/src/main/java/androidx/paging/PagedListConfig.kt
rename to paging/common/src/main/kotlin/androidx/paging/PagedListConfig.kt
index b1671a8..47676e2 100644
--- a/paging/common/ktx/src/main/java/androidx/paging/PagedListConfig.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/PagedListConfig.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2018 The Android Open Source Project
+ * 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.
@@ -31,15 +31,14 @@
     pageSize: Int,
     prefetchDistance: Int = pageSize,
     enablePlaceholders: Boolean = true,
-    initialLoadSizeHint: Int =
-            pageSize * PagedList.Config.Builder.DEFAULT_INITIAL_PAGE_MULTIPLIER,
+    initialLoadSizeHint: Int = pageSize * PagedList.Config.Builder.DEFAULT_INITIAL_PAGE_MULTIPLIER,
     maxSize: Int = PagedList.Config.MAX_SIZE_UNBOUNDED
 ): PagedList.Config {
     return PagedList.Config.Builder()
-            .setPageSize(pageSize)
-            .setPrefetchDistance(prefetchDistance)
-            .setEnablePlaceholders(enablePlaceholders)
-            .setInitialLoadSizeHint(initialLoadSizeHint)
-            .setMaxSize(maxSize)
-            .build()
+        .setPageSize(pageSize)
+        .setPrefetchDistance(prefetchDistance)
+        .setEnablePlaceholders(enablePlaceholders)
+        .setInitialLoadSizeHint(initialLoadSizeHint)
+        .setMaxSize(maxSize)
+        .build()
 }
diff --git a/paging/common/src/main/kotlin/androidx/paging/PagedStorage.kt b/paging/common/src/main/kotlin/androidx/paging/PagedStorage.kt
new file mode 100644
index 0000000..e9df6d8
--- /dev/null
+++ b/paging/common/src/main/kotlin/androidx/paging/PagedStorage.kt
@@ -0,0 +1,651 @@
+/*
+ * 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.
+ */
+
+package androidx.paging
+
+import androidx.annotation.RestrictTo
+import java.util.AbstractList
+
+/**
+ * Class holding the pages of data backing a [PagedList], presenting sparse loaded data as a List.
+ *
+ * It has two modes of operation: contiguous and non-contiguous (tiled). This class only holds
+ * data, and does not have any notion of the ideas of async loads, or prefetching.
+ *
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+class PagedStorage<T : Any> : AbstractList<T>, Pager.AdjacentProvider<T> {
+    /**
+     * List of pages in storage.
+     *
+     * Two storage modes:
+     *
+     * Contiguous - all content in pages is valid and loaded, but may return `false` from [isTiled].
+     * Safe to access any item in any page.
+     *
+     * Non-contiguous - pages may have nulls or a placeholder page, [isTiled] always returns `true`.
+     * pages may have nulls, or placeholder (empty) pages while content is loading.
+     */
+    private val pages: ArrayList<List<T>?>
+
+    var leadingNullCount: Int = 0
+        private set
+
+    var trailingNullCount: Int = 0
+        private set
+
+    var positionOffset: Int = 0
+        private set
+    /**
+     * Number of loaded items held by [pages]. When tiling, doesn't count unloaded pages in [pages].
+     * If tiling is disabled, same as [storageCount].
+     *
+     * This count is the one used for trimming.
+     */
+    var loadedCount: Int = 0
+        private set
+
+    /**
+     * Number of items represented by [pages]. If tiling is enabled, unloaded items in [pages] may
+     * be `null`, but this value still counts them.
+     */
+    var storageCount: Int = 0
+        private set
+
+    /**
+     *If pageSize > 0, tiling is enabled, 'pages' may have gaps, and leadingPages is set
+     */
+    private var pageSize: Int = 0
+
+    var numberPrepended: Int = 0
+        private set
+    var numberAppended: Int = 0
+        private set
+
+    /**
+     * `true` if all pages are the same size, except for the last, which may be smaller
+     */
+    val isTiled
+        get() = pageSize > 0
+
+    val pageCount
+        get() = pages.size
+
+    val middleOfLoadedRange
+        get() = leadingNullCount + positionOffset + storageCount / 2
+
+    // ------------- Adjacent Provider interface (contiguous-only) ------------------
+
+    override val firstLoadedItem
+        // Safe to access first page's first item here
+        // If contiguous, pages can't be empty, can't hold null Pages, and items can't be empty
+        get() = pages[0]?.first()
+
+    override val lastLoadedItem
+        // Safe to access last page's last item here:
+        // If contiguous, pages can't be empty, can't hold null Pages, and items can't be empty
+        get() = pages.last()?.last()
+
+    override val firstLoadedItemIndex
+        get() = leadingNullCount + positionOffset
+
+    override val lastLoadedItemIndex
+        get() = leadingNullCount + storageCount - 1 + positionOffset
+
+    constructor() {
+        leadingNullCount = 0
+        pages = ArrayList()
+        trailingNullCount = 0
+        positionOffset = 0
+        loadedCount = 0
+        storageCount = 0
+        pageSize = 1
+        numberPrepended = 0
+        numberAppended = 0
+    }
+
+    constructor(leadingNulls: Int, page: List<T>, trailingNulls: Int) : this() {
+        init(leadingNulls, page, trailingNulls, 0)
+    }
+
+    private constructor(other: PagedStorage<T>) {
+        leadingNullCount = other.leadingNullCount
+        pages = ArrayList(other.pages)
+        trailingNullCount = other.trailingNullCount
+        positionOffset = other.positionOffset
+        loadedCount = other.loadedCount
+        storageCount = other.storageCount
+        pageSize = other.pageSize
+        numberPrepended = other.numberPrepended
+        numberAppended = other.numberAppended
+    }
+
+    fun snapshot() = PagedStorage(this)
+
+    private fun init(leadingNulls: Int, page: List<T>, trailingNulls: Int, positionOffset: Int) {
+        leadingNullCount = leadingNulls
+        pages.clear()
+        pages.add(page)
+        trailingNullCount = trailingNulls
+
+        this.positionOffset = positionOffset
+        loadedCount = page.size
+        storageCount = loadedCount
+
+        // initialized as tiled. There may be 3 nulls, 2 items, but we still call this tiled
+        // even if it will break if nulls convert.
+        pageSize = page.size
+
+        numberPrepended = 0
+        numberAppended = 0
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    fun init(
+        leadingNulls: Int,
+        page: List<T>,
+        trailingNulls: Int,
+        positionOffset: Int,
+        callback: Callback
+    ) {
+        init(leadingNulls, page, trailingNulls, positionOffset)
+        callback.onInitialized(size)
+    }
+
+    override fun get(index: Int): T? {
+        // is it definitely outside 'pages'?
+        val localIndex = index - leadingNullCount
+
+        when {
+            index < 0 || index >= size ->
+                throw IndexOutOfBoundsException("Index: $index, Size: $size")
+            localIndex < 0 || localIndex >= storageCount -> return null
+        }
+
+        var localPageIndex: Int
+        var pageInternalIndex: Int
+
+        if (isTiled) {
+            // it's inside pages, and we're tiled. Jump to correct tile.
+            localPageIndex = localIndex / pageSize
+            pageInternalIndex = localIndex % pageSize
+        } else {
+            // it's inside pages, but page sizes aren't regular. Walk to correct tile.
+            // Pages can only be null while tiled, so accessing page count is safe.
+            pageInternalIndex = localIndex
+            val localPageCount = pages.size
+            localPageIndex = 0
+            while (localPageIndex < localPageCount) {
+                val pageSize = pages[localPageIndex]!!.size
+                if (pageSize > pageInternalIndex) {
+                    // stop, found the page
+                    break
+                }
+                pageInternalIndex -= pageSize
+                localPageIndex++
+            }
+        }
+
+        val page = pages[localPageIndex]
+        return when {
+            // can only occur in tiled case, with untouched inner/placeholder pages
+            page == null || page.isEmpty() -> null
+            else -> page[pageInternalIndex]
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    interface Callback {
+        fun onInitialized(count: Int)
+        fun onPagePrepended(leadingNulls: Int, changed: Int, added: Int)
+        fun onPageAppended(endPosition: Int, changed: Int, added: Int)
+        fun onPagePlaceholderInserted(pageIndex: Int)
+        fun onPageInserted(start: Int, count: Int)
+        fun onPagesRemoved(startOfDrops: Int, count: Int)
+        fun onPagesSwappedToPlaceholder(startOfDrops: Int, count: Int)
+    }
+
+    override val size
+        get() = leadingNullCount + storageCount + trailingNullCount
+
+    fun computeLeadingNulls(): Int {
+        var total = leadingNullCount
+        val pageCount = pages.size
+        for (i in 0 until pageCount) {
+            val page = pages[i]
+            if (page != null && page !is PlaceholderList) {
+                break
+            }
+            total += pageSize
+        }
+        return total
+    }
+
+    fun computeTrailingNulls(): Int {
+        var total = trailingNullCount
+        for (i in pages.indices.reversed()) {
+            val page = pages[i]
+            if (page != null && page !is PlaceholderList) {
+                break
+            }
+            total += pageSize
+        }
+        return total
+    }
+
+    // ---------------- Trimming API -------------------
+    // Trimming is always done at the beginning or end of the list, as content is loaded.
+    // In addition to trimming pages in the storage, we also support pre-trimming pages (dropping
+    // them just before they're added) to avoid dispatching an add followed immediately by a trim.
+    //
+    // Note - we avoid trimming down to a single page to reduce chances of dropping page in
+    // viewport, since we don't strictly know the viewport. If trim is aggressively set to size of a
+    // single page, trimming while the user can see a page boundary is dangerous. To be safe, we
+    // just avoid trimming in these cases entirely.
+
+    private fun needsTrim(maxSize: Int, requiredRemaining: Int, localPageIndex: Int): Boolean {
+        val page = pages[localPageIndex]
+        return page == null || (loadedCount > maxSize &&
+                pages.size > 2 &&
+                page !is PlaceholderList &&
+                loadedCount - page.size >= requiredRemaining)
+    }
+
+    fun needsTrimFromFront(maxSize: Int, requiredRemaining: Int) =
+        needsTrim(maxSize, requiredRemaining, 0)
+
+    fun needsTrimFromEnd(maxSize: Int, requiredRemaining: Int) =
+        needsTrim(maxSize, requiredRemaining, pages.size - 1)
+
+    fun shouldPreTrimNewPage(maxSize: Int, requiredRemaining: Int, countToBeAdded: Int) =
+        loadedCount + countToBeAdded > maxSize &&
+                pages.size > 1 &&
+                loadedCount >= requiredRemaining
+
+    internal fun trimFromFront(
+        insertNulls: Boolean,
+        maxSize: Int,
+        requiredRemaining: Int,
+        callback: Callback
+    ): Boolean {
+        var totalRemoved = 0
+        while (needsTrimFromFront(maxSize, requiredRemaining)) {
+            val page = pages.removeAt(0)
+            val removed = page?.size ?: pageSize
+            totalRemoved += removed
+            storageCount -= removed
+            loadedCount -= page?.size ?: 0
+        }
+
+        if (totalRemoved > 0) {
+            if (insertNulls) {
+                // replace removed items with nulls
+                val previousLeadingNulls = leadingNullCount
+                leadingNullCount += totalRemoved
+                callback.onPagesSwappedToPlaceholder(previousLeadingNulls, totalRemoved)
+            } else {
+                // simply remove, and handle offset
+                positionOffset += totalRemoved
+                callback.onPagesRemoved(leadingNullCount, totalRemoved)
+            }
+        }
+        return totalRemoved > 0
+    }
+
+    internal fun trimFromEnd(
+        insertNulls: Boolean,
+        maxSize: Int,
+        requiredRemaining: Int,
+        callback: Callback
+    ): Boolean {
+        var totalRemoved = 0
+        while (needsTrimFromEnd(maxSize, requiredRemaining)) {
+            val page = pages.removeAt(pages.size - 1)
+            val removed = page?.size ?: pageSize
+            totalRemoved += removed
+            storageCount -= removed
+            loadedCount -= page?.size ?: 0
+        }
+
+        if (totalRemoved > 0) {
+            val newEndPosition = leadingNullCount + storageCount
+            if (insertNulls) {
+                // replace removed items with nulls
+                trailingNullCount += totalRemoved
+                callback.onPagesSwappedToPlaceholder(newEndPosition, totalRemoved)
+            } else {
+                // items were just removed, signal
+                callback.onPagesRemoved(newEndPosition, totalRemoved)
+            }
+        }
+        return totalRemoved > 0
+    }
+
+    // ---------------- Contiguous API -------------------
+
+    internal fun prependPage(page: List<T>, callback: Callback) {
+        val count = page.size
+        if (count == 0) {
+            // Nothing returned from source, nothing to do
+            return
+        }
+        if (pageSize > 0 && count != pageSize) {
+            if (pages.size == 1 && count > pageSize) {
+                // prepending to a single item - update current page size to that of 'inner' page
+                pageSize = count
+            } else {
+                // no longer tiled
+                pageSize = -1
+            }
+        }
+
+        pages.add(0, page)
+        loadedCount += count
+        storageCount += count
+
+        val changedCount = minOf(leadingNullCount, count)
+        val addedCount = count - changedCount
+
+        if (changedCount != 0) {
+            leadingNullCount -= changedCount
+        }
+        positionOffset -= addedCount
+        numberPrepended += count
+
+        callback.onPagePrepended(leadingNullCount, changedCount, addedCount)
+    }
+
+    internal fun appendPage(page: List<T>, callback: Callback) {
+        val count = page.size
+        if (count == 0) {
+            // Nothing returned from source, nothing to do
+            return
+        }
+
+        if (pageSize > 0) {
+            // if the previous page was smaller than pageSize,
+            // or if this page is larger than the previous, disable tiling
+            if (pages[pages.size - 1]!!.size != pageSize || count > pageSize) {
+                pageSize = -1
+            }
+        }
+
+        pages.add(page)
+        loadedCount += count
+        storageCount += count
+
+        val changedCount = minOf(trailingNullCount, count)
+        val addedCount = count - changedCount
+
+        if (changedCount != 0) {
+            trailingNullCount -= changedCount
+        }
+        numberAppended += count
+        callback.onPageAppended(
+            leadingNullCount + storageCount - count,
+            changedCount, addedCount
+        )
+    }
+
+    override fun onPageResultResolution(
+        type: PagedList.LoadType,
+        result: DataSource.BaseResult<T>
+    ) {
+        // ignored
+    }
+
+    // ------------------ Non-Contiguous API (tiling required) ----------------------
+
+    /**
+     * Return true if the page at the passed position would be the first (if trimFromFront) or last
+     * page that's currently loading.
+     */
+    fun pageWouldBeBoundary(positionOfPage: Int, trimFromFront: Boolean): Boolean {
+        when {
+            pageSize < 1 || pages.size < 2 ->
+                throw IllegalStateException("Trimming attempt before sufficient load")
+            // position represent page in leading nulls
+            positionOfPage < leadingNullCount -> return trimFromFront
+            // position represent page in trailing nulls
+            positionOfPage >= leadingNullCount + storageCount -> return !trimFromFront
+        }
+
+        val localPageIndex = (positionOfPage - leadingNullCount) / pageSize
+
+        // walk outside in, return false if we find non-placeholder page before localPageIndex
+        if (trimFromFront) {
+            for (i in 0 until localPageIndex) {
+                if (pages[i] != null) {
+                    return false
+                }
+            }
+        } else {
+            for (i in pages.size - 1 downTo localPageIndex + 1) {
+                if (pages[i] != null) {
+                    return false
+                }
+            }
+        }
+
+        // didn't find another page, so this one would be a boundary
+        return true
+    }
+
+    internal fun initAndSplit(
+        leadingNulls: Int,
+        multiPageList: List<T>,
+        trailingNulls: Int,
+        positionOffset: Int,
+        pageSize: Int,
+        callback: Callback
+    ) {
+        val pageCount = (multiPageList.size + (pageSize - 1)) / pageSize
+        for (i in 0 until pageCount) {
+            val beginInclusive = i * pageSize
+            val endExclusive = minOf(multiPageList.size, (i + 1) * pageSize)
+
+            val sublist = multiPageList.subList(beginInclusive, endExclusive)
+
+            if (i == 0) {
+                // Trailing nulls for first page includes other pages in multiPageList
+                val initialTrailingNulls = trailingNulls + multiPageList.size - sublist.size
+                init(leadingNulls, sublist, initialTrailingNulls, positionOffset)
+            } else {
+                val insertPosition = leadingNulls + beginInclusive
+                insertPage(insertPosition, sublist, null)
+            }
+        }
+        callback.onInitialized(size)
+    }
+
+    internal fun tryInsertPageAndTrim(
+        position: Int,
+        page: List<T>,
+        lastLoad: Int,
+        maxSize: Int,
+        requiredRemaining: Int,
+        callback: Callback
+    ) {
+        val trim = maxSize != PagedList.Config.MAX_SIZE_UNBOUNDED
+        val trimFromFront = lastLoad > middleOfLoadedRange
+
+        val pageInserted = (!trim ||
+                !shouldPreTrimNewPage(maxSize, requiredRemaining, page.size) ||
+                !pageWouldBeBoundary(position, trimFromFront))
+
+        if (pageInserted) {
+            insertPage(position, page, callback)
+        } else {
+            // trim would have us drop the page we just loaded - swap it to null
+            val localPageIndex = (position - leadingNullCount) / pageSize
+            pages.set(localPageIndex, null)
+
+            // note: we also remove it, so we don't have to guess how large a 'null' page is later
+            storageCount -= page.size
+            if (trimFromFront) {
+                pages.removeAt(0)
+                leadingNullCount += page.size
+            } else {
+                pages.removeAt(pages.size - 1)
+                trailingNullCount += page.size
+            }
+        }
+
+        if (trim) {
+            if (trimFromFront) {
+                trimFromFront(true, maxSize, requiredRemaining, callback)
+            } else {
+                trimFromEnd(true, maxSize, requiredRemaining, callback)
+            }
+        }
+    }
+
+    internal fun insertPage(position: Int, page: List<T>, callback: Callback?) {
+        val newPageSize = page.size
+        if (newPageSize != pageSize) {
+            // differing page size is OK in 2 cases, when the page is being added:
+            // 1) to the end (in which case, ignore new smaller size)
+            // 2) only the last page has been added so far (in which case, adopt new bigger size)
+
+            val size = size
+            val addingLastPage = position == size - size % pageSize && newPageSize < pageSize
+            val onlyEndPagePresent = (trailingNullCount == 0 && pages.size == 1 &&
+                    newPageSize > pageSize)
+
+            // OK only if existing single page, and it's the last one
+            if (!onlyEndPagePresent && !addingLastPage) {
+                throw IllegalArgumentException("page introduces incorrect tiling")
+            }
+            if (onlyEndPagePresent) {
+                pageSize = newPageSize
+            }
+        }
+
+        val pageIndex = position / pageSize
+
+        allocatePageRange(pageIndex, pageIndex)
+
+        val localPageIndex = pageIndex - leadingNullCount / pageSize
+
+        val oldPage = pages[localPageIndex]
+        if (oldPage != null && oldPage !is PlaceholderList) {
+            throw IllegalArgumentException(
+                "Invalid position $position: data already loaded"
+            )
+        }
+        pages[localPageIndex] = page
+        loadedCount += newPageSize
+        callback?.onPageInserted(position, newPageSize)
+    }
+
+    @Suppress("MemberVisibilityCanBePrivate")
+    fun allocatePageRange(minimumPage: Int, maximumPage: Int) {
+        var leadingNullPages = leadingNullCount / pageSize
+
+        if (minimumPage < leadingNullPages) {
+            for (i in 0 until leadingNullPages - minimumPage) {
+                pages.add(0, null)
+            }
+            val newStorageAllocated = (leadingNullPages - minimumPage) * pageSize
+            storageCount += newStorageAllocated
+            leadingNullCount -= newStorageAllocated
+
+            leadingNullPages = minimumPage
+        }
+        if (maximumPage >= leadingNullPages + pages.size) {
+            val newStorageAllocated = minOf(
+                trailingNullCount,
+                (maximumPage + 1 - (leadingNullPages + pages.size)) * pageSize
+            )
+            for (i in pages.size..maximumPage - leadingNullPages) {
+                pages.add(pages.size, null)
+            }
+            storageCount += newStorageAllocated
+            trailingNullCount -= newStorageAllocated
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    fun allocatePlaceholders(index: Int, prefetchDistance: Int, pageSize: Int, callback: Callback) {
+        if (pageSize != this.pageSize) {
+            if (pageSize < this.pageSize) {
+                throw IllegalArgumentException("Page size cannot be reduced")
+            }
+            if (pages.size != 1 || trailingNullCount != 0) {
+                // not in single, last page allocated case - can't change page size
+                throw IllegalArgumentException(
+                    "Page size can change only if last page is only one present"
+                )
+            }
+            this.pageSize = pageSize
+        }
+
+        val maxPageCount = (size + this.pageSize - 1) / this.pageSize
+        val minimumPage = maxOf((index - prefetchDistance) / this.pageSize, 0)
+        val maximumPage = minOf((index + prefetchDistance) / this.pageSize, maxPageCount - 1)
+
+        allocatePageRange(minimumPage, maximumPage)
+        val leadingNullPages = leadingNullCount / this.pageSize
+        for (pageIndex in minimumPage..maximumPage) {
+            val localPageIndex = pageIndex - leadingNullPages
+            if (pages[localPageIndex] == null) {
+                pages[localPageIndex] = placeholderList
+                callback.onPagePlaceholderInserted(pageIndex)
+            }
+        }
+    }
+
+    fun hasPage(pageSize: Int, index: Int): Boolean {
+        // NOTE: we pass pageSize here to avoid in case pageSize not fully initialized (when last
+        // page only one loaded).
+        val leadingNullPages = leadingNullCount / pageSize
+
+        if (index < leadingNullPages || index >= leadingNullPages + pages.size) {
+            return false
+        }
+
+        val page = pages[index - leadingNullPages]
+
+        return page != null && page !is PlaceholderList
+    }
+
+    override fun toString(): String {
+        var ret = "leading $leadingNullCount, storage $storageCount, trailing $trailingNullCount"
+        if (pages.isNotEmpty()) {
+            ret += " ${pages.joinToString(" ")}"
+        }
+        return ret
+    }
+
+    /**
+     * Lists instances are compared (with instance equality) to [placeholderList] to check if an
+     * item in that position is already loading. We use a singleton placeholder list that is
+     * distinct from `Collections.emptyList()` for safety.
+     */
+    private class PlaceholderList<T> : ArrayList<T>()
+
+    private val placeholderList = PlaceholderList<T>()
+}
diff --git a/paging/common/src/main/kotlin/androidx/paging/Pager.kt b/paging/common/src/main/kotlin/androidx/paging/Pager.kt
new file mode 100644
index 0000000..3387f9f
--- /dev/null
+++ b/paging/common/src/main/kotlin/androidx/paging/Pager.kt
@@ -0,0 +1,281 @@
+/*
+ * 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.
+ */
+
+package androidx.paging
+
+import androidx.paging.DataSource.BaseResult
+import androidx.paging.PagedList.LoadState
+import androidx.paging.PagedList.LoadType
+import androidx.paging.futures.FutureCallback
+import androidx.paging.futures.addCallback
+import com.google.common.util.concurrent.ListenableFuture
+import java.util.concurrent.Executor
+import java.util.concurrent.atomic.AtomicBoolean
+
+internal class Pager<K : Any, V : Any>(
+    val config: PagedList.Config,
+    val source: DataSource<K, V>,
+    val notifyExecutor: Executor,
+    private val fetchExecutor: Executor,
+    val pageConsumer: PageConsumer<V>,
+    adjacentProvider: AdjacentProvider<V>?,
+    result: BaseResult<V>
+) {
+    private val totalCount: Int
+    private val adjacentProvider: AdjacentProvider<V>
+    private var prevKey: K? = null
+    private var nextKey: K? = null
+    private val detached = AtomicBoolean(false)
+
+    var loadStateManager = object : PagedList.LoadStateManager() {
+        override fun onStateChanged(type: LoadType, state: LoadState, error: Throwable?) =
+            pageConsumer.onStateChanged(type, state, error)
+    }
+
+    val isDetached
+        get() = detached.get()
+
+    init {
+        this.adjacentProvider = adjacentProvider ?: SimpleAdjacentProvider()
+        @Suppress("UNCHECKED_CAST")
+        prevKey = result.prevKey as K?
+        @Suppress("UNCHECKED_CAST")
+        nextKey = result.nextKey as K?
+        this.adjacentProvider.onPageResultResolution(LoadType.REFRESH, result)
+        totalCount = result.totalCount()
+
+        // TODO: move this validation to tiled paging impl, once that's added back
+        if (source.type === DataSource.KeyType.POSITIONAL && config.enablePlaceholders) {
+            result.validateForInitialTiling(config.pageSize)
+        }
+    }
+
+    private fun listenTo(type: LoadType, future: ListenableFuture<out BaseResult<V>>) {
+        // First listen on the BG thread if the DataSource is invalid, since it can be expensive
+        future.addListener(Runnable {
+            // if invalid, drop result on the floor
+            if (source.isInvalid) {
+                detach()
+                return@Runnable
+            }
+
+            // Source has been verified to be valid after producing data, so sent data to UI
+            future.addCallback(
+                object : FutureCallback<BaseResult<V>> {
+                    override fun onSuccess(value: BaseResult<V>) = onLoadSuccess(type, value)
+                    override fun onError(throwable: Throwable) = onLoadError(type, throwable)
+                },
+                notifyExecutor
+            )
+        }, fetchExecutor)
+    }
+
+    internal interface PageConsumer<V : Any> {
+        /**
+         * @return `true` if we need to fetch more
+         */
+        fun onPageResult(type: LoadType, pageResult: BaseResult<V>): Boolean
+
+        fun onStateChanged(type: LoadType, state: LoadState, error: Throwable?)
+    }
+
+    internal interface AdjacentProvider<V : Any> {
+        val firstLoadedItem: V?
+        val lastLoadedItem: V?
+        val firstLoadedItemIndex: Int
+        val lastLoadedItemIndex: Int
+
+        /**
+         * Notify the [AdjacentProvider] of new loaded data, to update first/last item/index.
+         *
+         * NOTE: this data may not be committed (e.g. it may be dropped due to max size). Up to the
+         * implementation of the AdjacentProvider to handle this (generally by ignoring this call if
+         * dropping is supported).
+         */
+        fun onPageResultResolution(type: LoadType, result: BaseResult<V>)
+    }
+
+    fun onLoadSuccess(type: LoadType, value: BaseResult<V>) {
+        if (isDetached) return // abort!
+
+        adjacentProvider.onPageResultResolution(type, value)
+
+        if (pageConsumer.onPageResult(type, value)) {
+            when (type) {
+                LoadType.START -> {
+                    @Suppress("UNCHECKED_CAST")
+                    prevKey = value.prevKey as K?
+                    schedulePrepend()
+                }
+                LoadType.END -> {
+                    @Suppress("UNCHECKED_CAST")
+                    nextKey = value.nextKey as K?
+                    scheduleAppend()
+                }
+                else -> throw IllegalStateException("Can only fetch more during append/prepend")
+            }
+        } else {
+            val state = if (value.data.isEmpty()) LoadState.DONE else LoadState.IDLE
+            loadStateManager.setState(type, state, null)
+        }
+    }
+
+    fun onLoadError(type: LoadType, throwable: Throwable) {
+        if (isDetached) return // abort!
+
+        // TODO: handle nesting
+        val state = when {
+            source.isRetryableError(throwable) -> LoadState.RETRYABLE_ERROR
+            else -> LoadState.ERROR
+        }
+        loadStateManager.setState(type, state, throwable)
+    }
+
+    fun trySchedulePrepend() {
+        if (loadStateManager.start == LoadState.IDLE) schedulePrepend()
+    }
+
+    fun tryScheduleAppend() {
+        if (loadStateManager.end == LoadState.IDLE) scheduleAppend()
+    }
+
+    private fun canPrepend() = when (totalCount) {
+        // don't know count / position from initial load, so be conservative, return true
+        BaseResult.TOTAL_COUNT_UNKNOWN -> true
+        // position is known, do we have space left?
+        else -> adjacentProvider.firstLoadedItemIndex > 0
+    }
+
+    private fun canAppend() = when (totalCount) {
+        // don't know count / position from initial load, so be conservative, return true
+        BaseResult.TOTAL_COUNT_UNKNOWN -> true
+        // count is known, do we have space left?
+        else -> adjacentProvider.lastLoadedItemIndex < totalCount - 1
+    }
+
+    private fun schedulePrepend() {
+        if (!canPrepend()) {
+            onLoadSuccess(LoadType.START, BaseResult.empty())
+            return
+        }
+
+        val key = when (source.type) {
+            DataSource.KeyType.POSITIONAL ->
+                @Suppress("UNCHECKED_CAST")
+                (adjacentProvider.firstLoadedItemIndex - 1) as K
+            DataSource.KeyType.PAGE_KEYED -> prevKey
+            DataSource.KeyType.ITEM_KEYED -> (source as ListenableItemKeyedDataSource).getKey(
+                adjacentProvider.firstLoadedItem!!
+            )
+        }
+
+        loadStateManager.setState(LoadType.START, LoadState.LOADING, null)
+        listenTo(
+            LoadType.START,
+            source.load(
+                DataSource.Params(
+                    DataSource.LoadType.START,
+                    key,
+                    config.initialLoadSizeHint,
+                    config.enablePlaceholders,
+                    config.pageSize
+                )
+            )
+        )
+    }
+
+    private fun scheduleAppend() {
+        if (!canAppend()) {
+            onLoadSuccess(LoadType.END, BaseResult.empty())
+            return
+        }
+
+        val key = when (source.type) {
+            DataSource.KeyType.POSITIONAL ->
+                @Suppress("UNCHECKED_CAST")
+                (adjacentProvider.lastLoadedItemIndex + 1) as K
+            DataSource.KeyType.PAGE_KEYED -> nextKey
+            DataSource.KeyType.ITEM_KEYED -> (source as ListenableItemKeyedDataSource).getKey(
+                adjacentProvider.lastLoadedItem!!
+            )
+        }
+
+        loadStateManager.setState(LoadType.END, LoadState.LOADING, null)
+        listenTo(
+            LoadType.END,
+            source.load(
+                DataSource.Params(
+                    DataSource.LoadType.END,
+                    key,
+                    config.initialLoadSizeHint,
+                    config.enablePlaceholders,
+                    config.pageSize
+                )
+            )
+        )
+    }
+
+    fun retry() {
+        if (loadStateManager.start == LoadState.RETRYABLE_ERROR) schedulePrepend()
+        if (loadStateManager.end == LoadState.RETRYABLE_ERROR) scheduleAppend()
+    }
+
+    fun detach() = detached.set(true)
+
+    internal class SimpleAdjacentProvider<V : Any> : AdjacentProvider<V> {
+        override var firstLoadedItemIndex: Int = 0
+            private set
+        override var lastLoadedItemIndex: Int = 0
+            private set
+        override var firstLoadedItem: V? = null
+            private set
+        override var lastLoadedItem: V? = null
+            private set
+
+        private var counted: Boolean = false
+        private var leadingUnloadedCount: Int = 0
+        private var trailingUnloadedCount: Int = 0
+
+        override fun onPageResultResolution(type: LoadType, result: BaseResult<V>) {
+            if (result.data.isEmpty()) return
+
+            if (type == LoadType.START) {
+                firstLoadedItemIndex -= result.data.size
+                firstLoadedItem = result.data[0]
+                if (counted) {
+                    leadingUnloadedCount -= result.data.size
+                }
+            } else if (type == LoadType.END) {
+                lastLoadedItemIndex += result.data.size
+                lastLoadedItem = result.data.last()
+                if (counted) {
+                    trailingUnloadedCount -= result.data.size
+                }
+            } else {
+                firstLoadedItemIndex = result.leadingNulls + result.offset
+                lastLoadedItemIndex = firstLoadedItemIndex + result.data.size - 1
+                firstLoadedItem = result.data[0]
+                lastLoadedItem = result.data.last()
+
+                if (result.counted) {
+                    counted = true
+                    leadingUnloadedCount = result.leadingNulls
+                    trailingUnloadedCount = result.trailingNulls
+                }
+            }
+        }
+    }
+}
diff --git a/paging/common/src/main/kotlin/androidx/paging/PositionalDataSource.kt b/paging/common/src/main/kotlin/androidx/paging/PositionalDataSource.kt
new file mode 100644
index 0000000..50bb351
--- /dev/null
+++ b/paging/common/src/main/kotlin/androidx/paging/PositionalDataSource.kt
@@ -0,0 +1,389 @@
+/*
+ * 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.
+ */
+
+package androidx.paging
+
+import androidx.annotation.RestrictTo
+import androidx.annotation.WorkerThread
+import androidx.arch.core.util.Function
+import androidx.concurrent.futures.ResolvableFuture
+import androidx.paging.PositionalDataSource.LoadInitialCallback
+import com.google.common.util.concurrent.ListenableFuture
+
+/**
+ * Position-based data loader for a fixed-size, countable data set, supporting fixed-size loads at
+ * arbitrary page positions.
+ *
+ * Extend PositionalDataSource if you can load pages of a requested size at arbitrary positions,
+ * and provide a fixed item count. If your data source can't support loading arbitrary requested
+ * page sizes (e.g. when network page size constraints are only known at runtime), either use
+ * [PageKeyedDataSource] or [ItemKeyedDataSource], or pass the initial result with the two parameter
+ * [LoadInitialCallback.onResult].
+ *
+ * Room can generate a Factory of PositionalDataSources for you:
+ * ```
+ * @Dao
+ * interface UserDao {
+ *     @Query("SELECT * FROM user ORDER BY age DESC")
+ *     public abstract DataSource.Factory<Integer, User> loadUsersByAgeDesc();
+ * }
+ * ```
+ *
+ * @param T Type of items being loaded by the [PositionalDataSource].
+ *
+ * @see [ListenablePositionalDataSource]
+ */
+abstract class PositionalDataSource<T : Any> : ListenablePositionalDataSource<T>() {
+    /**
+     * Holder object for inputs to [loadInitial].
+     */
+    open class LoadInitialParams(
+        requestedStartPosition: Int,
+        requestedLoadSize: Int,
+        pageSize: Int,
+        placeholdersEnabled: Boolean
+    ) : ListenablePositionalDataSource.LoadInitialParams(
+        requestedStartPosition,
+        requestedLoadSize,
+        pageSize,
+        placeholdersEnabled
+    )
+
+    /**
+     * Holder object for inputs to [loadRange].
+     */
+    open class LoadRangeParams(startPosition: Int, loadSize: Int) :
+        ListenablePositionalDataSource.LoadRangeParams(startPosition, loadSize)
+
+    /**
+     * Callback for [loadInitial] to return data, position, and count.
+     *
+     * A callback should be called only once, and may throw if called again.
+     *
+     * It is always valid for a DataSource loading method that takes a callback to stash the
+     * callback and call it later. This enables DataSources to be fully asynchronous, and to handle
+     * temporary, recoverable error states (such as a network error that can be retried).
+     *
+     * @param T Type of items being loaded.
+     */
+    abstract class LoadInitialCallback<T> {
+        /**
+         * Called to pass initial load state from a DataSource.
+         *
+         * Call this method from [loadInitial] function to return data, and inform how many
+         * placeholders should be shown before and after. If counting is cheap compute (for example,
+         * if a network load returns the information regardless), it's recommended to pass the total
+         * size to the totalCount parameter. If placeholders are not requested (when
+         * [LoadInitialParams.placeholdersEnabled] is false), you can instead call [onResult].
+         *
+         * @param data List of items loaded from the [DataSource]. If this is empty, the
+         *             [DataSource] is treated as empty, and no further loads will occur.
+         * @param position Position of the item at the front of the list. If there are N items
+         *                 before the items in data that can be loaded from this DataSource, pass N.
+         * @param totalCount Total number of items that may be returned from this DataSource.
+         *                   Includes the number in the initial [data] parameter as well as any
+         *                   items that can be loaded in front or behind of [data].
+         */
+        abstract fun onResult(data: List<T>, position: Int, totalCount: Int)
+
+        /**
+         * Called to pass initial load state from a DataSource without total count, when
+         * placeholders aren't requested.
+         *
+         * **Note:** This method can only be called when placeholders are disabled (i.e.,
+         * [LoadInitialParams.placeholdersEnabled] is `false`).
+         *
+         * Call this method from [loadInitial] function to return data, if position is known but
+         * total size is not. If placeholders are requested, call the three parameter variant:
+         * [onResult].
+         *
+         * @param data List of items loaded from the [DataSource]. If this is empty, the
+         *             [DataSource] is treated as empty, and no further loads will occur.
+         * @param position Position of the item at the front of the list. If there are N items
+         *                 before the items in data that can be provided by this [DataSource], pass
+         *                 N.
+         */
+        abstract fun onResult(data: List<T>, position: Int)
+
+        /**
+         * Called to report an error from a DataSource.
+         *
+         * Call this method to report an error from [loadInitial].
+         *
+         * @param error The error that occurred during loading.
+         */
+        open fun onError(error: Throwable) {
+            // TODO: remove default implementation in 3.0
+            throw IllegalStateException(
+                "You must implement onError if implementing your own load callback"
+            )
+        }
+    }
+
+    /**
+     * Callback for PositionalDataSource [loadRange] to return data.
+     *
+     * A callback should be called only once, and may throw if called again.
+     *
+     * It is always valid for a [DataSource] loading method that takes a callback to stash the
+     * callback and call it later. This enables DataSources to be fully asynchronous, and to handle
+     * temporary, recoverable error states (such as a network error that can be retried).
+     *
+     * @param T Type of items being loaded.
+     */
+    abstract class LoadRangeCallback<T> {
+        /**
+         * Called to pass loaded data from [loadRange].
+         *
+         * @param data List of items loaded from the [DataSource]. Must be same size as requested,
+         *             unless at end of list.
+         */
+        abstract fun onResult(data: List<T>)
+
+        /**
+         * Called to report an error from a [DataSource].
+         *
+         * Call this method to report an error from [loadRange].
+         *
+         * @param error The error that occurred during loading.
+         */
+        open fun onError(error: Throwable) {
+            // TODO: remove default implementation in 3.0
+            throw IllegalStateException(
+                "You must implement onError if implementing your own load callback"
+            )
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    companion object {
+        /**
+         * Helper for computing an initial position in [loadInitial] when total data set size can be
+         * computed ahead of loading.
+         *
+         * The value computed by this function will do bounds checking, page alignment, and
+         * positioning based on initial load size requested.
+         *
+         * Example usage in a [PositionalDataSource] subclass:
+         * ```
+         * class ItemDataSource extends PositionalDataSource<Item> {
+         *     private int computeCount() {
+         *         // actual count code here
+         *     }
+         *
+         *     private List<Item> loadRangeInternal(int startPosition, int loadCount) {
+         *         // actual load code here
+         *     }
+         *
+         *     @Override
+         *     public void loadInitial(@NonNull LoadInitialParams params,
+         *         @NonNull LoadInitialCallback<Item> callback) {
+         *         int totalCount = computeCount();
+         *         int position = computeInitialLoadPosition(params, totalCount);
+         *         int loadSize = computeInitialLoadSize(params, position, totalCount);
+         *         callback.onResult(loadRangeInternal(position, loadSize), position, totalCount);
+         *     }
+         *
+         *     @Override
+         *     public void loadRange(@NonNull LoadRangeParams params,
+         *         @NonNull LoadRangeCallback<Item> callback) {
+         *         callback.onResult(loadRangeInternal(params.startPosition, params.loadSize));
+         *     }
+         * }
+         * ```
+         *
+         * @param params Params passed to [loadInitial], including page size, and requested start /
+         *               loadSize.
+         * @param totalCount Total size of the data set.
+         * @return Position to start loading at.
+         *
+         * @see [computeInitialLoadSize]
+         */
+        @JvmStatic
+        fun computeInitialLoadPosition(params: LoadInitialParams, totalCount: Int): Int =
+            ListenablePositionalDataSource.computeInitialLoadPosition(params, totalCount)
+
+        /**
+         * Helper for computing an initial load size in [loadInitial] when total data set size can
+         * be computed ahead of loading.
+         *
+         * This function takes the requested load size, and bounds checks it against the value
+         * returned by [computeInitialLoadPosition].
+         *
+         * Example usage in a [PositionalDataSource] subclass:
+         * ```
+         * class ItemDataSource extends PositionalDataSource<Item> {
+         *     private int computeCount() {
+         *         // actual count code here
+         *     }
+         *
+         *     private List<Item> loadRangeInternal(int startPosition, int loadCount) {
+         *         // actual load code here
+         *     }
+         *
+         *     @Override
+         *     public void loadInitial(@NonNull LoadInitialParams params,
+         *         @NonNull LoadInitialCallback<Item> callback) {
+         *         int totalCount = computeCount();
+         *         int position = computeInitialLoadPosition(params, totalCount);
+         *         int loadSize = computeInitialLoadSize(params, position, totalCount);
+         *         callback.onResult(loadRangeInternal(position, loadSize), position, totalCount);
+         *     }
+         *
+         *     @Override
+         *     public void loadRange(@NonNull LoadRangeParams params,
+         *         @NonNull LoadRangeCallback<Item> callback) {
+         *         callback.onResult(loadRangeInternal(params.startPosition, params.loadSize));
+         *     }
+         * }
+         * ```
+         *
+         * @param params Params passed to [loadInitial], including page size, and requested start /
+         *               loadSize.
+         * @param initialLoadPosition Value returned by [computeInitialLoadPosition]
+         * @param totalCount Total size of the data set.
+         * @return Number of items to load.
+         *
+         * @see [computeInitialLoadPosition]
+         */
+        @JvmStatic
+        fun computeInitialLoadSize(
+            params: LoadInitialParams,
+            initialLoadPosition: Int,
+            totalCount: Int
+        ) = ListenablePositionalDataSource.computeInitialLoadSize(
+            params,
+            initialLoadPosition,
+            totalCount
+        )
+    }
+
+    final override fun loadInitial(
+        params: ListenablePositionalDataSource.LoadInitialParams
+    ): ListenableFuture<InitialResult<T>> {
+        val future = ResolvableFuture.create<InitialResult<T>>()
+        executor.execute {
+            val newParams = LoadInitialParams(
+                params.requestedStartPosition,
+                params.requestedLoadSize,
+                params.pageSize,
+                params.placeholdersEnabled
+            )
+            val callback = object : LoadInitialCallback<T>() {
+                override fun onResult(data: List<T>, position: Int, totalCount: Int) {
+                    if (isInvalid) {
+                        // NOTE: this isInvalid check works around
+                        // https://issuetracker.google.com/issues/124511903
+                        future.set(InitialResult(emptyList(), 0, 0))
+                    } else {
+                        setFuture(newParams, InitialResult(data, position, totalCount))
+                    }
+                }
+
+                override fun onResult(data: List<T>, position: Int) {
+                    if (isInvalid) {
+                        // NOTE: this isInvalid check works around
+                        // https://issuetracker.google.com/issues/124511903
+                        future.set(InitialResult(emptyList(), 0))
+                    } else {
+                        setFuture(newParams, InitialResult(data, position))
+                    }
+                }
+
+                private fun setFuture(
+                    params: ListenablePositionalDataSource.LoadInitialParams,
+                    result: InitialResult<T>
+                ) {
+                    if (params.placeholdersEnabled) {
+                        result.validateForInitialTiling(params.pageSize)
+                    }
+                    future.set(result)
+                }
+
+                override fun onError(error: Throwable) {
+                    future.setException(error)
+                }
+            }
+            loadInitial(newParams, callback)
+        }
+        return future
+    }
+
+    final override fun loadRange(
+        params: ListenablePositionalDataSource.LoadRangeParams
+    ): ListenableFuture<RangeResult<T>> {
+        val future = ResolvableFuture.create<RangeResult<T>>()
+        executor.execute {
+            val callback = object : LoadRangeCallback<T>() {
+                override fun onResult(data: List<T>) {
+                    when {
+                        isInvalid -> future.set(RangeResult(emptyList()))
+                        else -> future.set(RangeResult(data))
+                    }
+                }
+
+                override fun onError(error: Throwable) {
+                    future.setException(error)
+                }
+            }
+            loadRange(LoadRangeParams(params.startPosition, params.loadSize), callback)
+        }
+        return future
+    }
+
+    /**
+     * Load initial list data.
+     *
+     * This method is called to load the initial page(s) from the [DataSource].
+     *
+     * Result list must be a multiple of pageSize to enable efficient tiling.
+     *
+     * @param params Parameters for initial load, including requested start position, load size, and
+     *               page size.
+     * @param callback Callback that receives initial load data, including position and total data
+     *                 set size.
+     */
+    @WorkerThread
+    abstract fun loadInitial(params: LoadInitialParams, callback: LoadInitialCallback<T>)
+
+    /**
+     * Called to load a range of data from the DataSource.
+     *
+     * This method is called to load additional pages from the DataSource after the
+     * [LoadInitialCallback] passed to dispatchLoadInitial has initialized a [PagedList].
+     *
+     * Unlike [loadInitial], this method must return the number of items requested, at the position
+     * requested.
+     *
+     * @param params Parameters for load, including start position and load size.
+     * @param callback Callback that receives loaded data.
+     */
+    @WorkerThread
+    abstract fun loadRange(params: LoadRangeParams, callback: LoadRangeCallback<T>)
+
+    @Suppress("RedundantVisibilityModifier") // Metalava doesn't inherit visibility properly.
+    internal override val isContiguous = false
+
+    final override fun <V : Any> mapByPage(
+        function: Function<List<T>, List<V>>
+    ): PositionalDataSource<V> = WrapperPositionalDataSource(this, function)
+
+    final override fun <V : Any> map(function: Function<T, V>): PositionalDataSource<V> =
+        mapByPage(Function { list -> list.map { function.apply(it) } })
+}
diff --git a/paging/common/src/main/kotlin/androidx/paging/SnapshotPagedList.kt b/paging/common/src/main/kotlin/androidx/paging/SnapshotPagedList.kt
new file mode 100644
index 0000000..7217d82
--- /dev/null
+++ b/paging/common/src/main/kotlin/androidx/paging/SnapshotPagedList.kt
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+package androidx.paging
+
+internal class SnapshotPagedList<T : Any>(private val pagedList: PagedList<T>) : PagedList<T>(
+    pagedList.storage.snapshot(),
+    pagedList.mainThreadExecutor,
+    pagedList.backgroundThreadExecutor,
+    null,
+    pagedList.config
+) {
+    init {
+        lastLoad = pagedList.lastLoad
+    }
+
+    override val isContiguous
+        get() = pagedList.isContiguous
+
+    override val dataSource: DataSource<*, T> = pagedList.dataSource
+    override val isImmutable = true
+
+    override val lastKey
+        get() = pagedList.lastKey
+
+    override val isDetached = true
+
+    override fun detach() {}
+    override fun dispatchUpdatesSinceSnapshot(snapshot: PagedList<T>, callback: Callback) {}
+    override fun dispatchCurrentLoadState(listener: LoadStateListener) {}
+    override fun loadAroundInternal(index: Int) {}
+}
diff --git a/paging/common/src/main/kotlin/androidx/paging/WrapperDataSource.kt b/paging/common/src/main/kotlin/androidx/paging/WrapperDataSource.kt
new file mode 100644
index 0000000..fa5b145
--- /dev/null
+++ b/paging/common/src/main/kotlin/androidx/paging/WrapperDataSource.kt
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ */
+
+package androidx.paging
+
+import androidx.arch.core.util.Function
+import androidx.paging.futures.DirectExecutor
+import androidx.paging.futures.transform
+import com.google.common.util.concurrent.ListenableFuture
+import java.util.IdentityHashMap
+
+/**
+ * @param Key DataSource key type, same for original and wrapped.
+ * @param ValueFrom Value type of original DataSource.
+ * @param ValueTo Value type of new DataSource.
+ */
+internal open class WrapperDataSource<Key : Any, ValueFrom : Any, ValueTo : Any>(
+    private val source: DataSource<Key, ValueFrom>,
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    val listFunction: Function<List<ValueFrom>, List<ValueTo>>
+) : DataSource<Key, ValueTo>(source.type) {
+    private val keyMap = when (source.type) {
+        KeyType.ITEM_KEYED -> IdentityHashMap<ValueTo, Key>()
+        else -> null
+    }
+
+    override fun addInvalidatedCallback(onInvalidatedCallback: InvalidatedCallback) =
+        source.addInvalidatedCallback(onInvalidatedCallback)
+
+    override fun removeInvalidatedCallback(onInvalidatedCallback: InvalidatedCallback) =
+        source.removeInvalidatedCallback(onInvalidatedCallback)
+
+    override fun invalidate() = source.invalidate()
+
+    override val isInvalid
+        get() = source.isInvalid
+
+    override fun getKeyInternal(item: ValueTo): Key = when {
+        keyMap != null -> synchronized(keyMap) {
+            return keyMap[item]!!
+        }
+        // positional / page-keyed
+        else -> throw IllegalStateException("Cannot get key by item in non-item keyed DataSource")
+    }
+
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    fun stashKeysIfNeeded(source: List<ValueFrom>, dest: List<ValueTo>) {
+        if (keyMap != null) {
+            synchronized(keyMap) {
+                for (i in dest.indices) {
+                    keyMap[dest[i]] =
+                        (this.source as ListenableItemKeyedDataSource).getKey(source[i])
+                }
+            }
+        }
+    }
+
+    override fun load(params: Params<Key>): ListenableFuture<out BaseResult<ValueTo>> =
+        source.load(params).transform(
+            Function { input ->
+                val result = BaseResult.convert(input, listFunction)
+                stashKeysIfNeeded(input.data, result.data)
+                result
+            },
+            DirectExecutor
+        )
+}
diff --git a/paging/common/src/main/kotlin/androidx/paging/WrapperItemKeyedDataSource.kt b/paging/common/src/main/kotlin/androidx/paging/WrapperItemKeyedDataSource.kt
new file mode 100644
index 0000000..65047b9
--- /dev/null
+++ b/paging/common/src/main/kotlin/androidx/paging/WrapperItemKeyedDataSource.kt
@@ -0,0 +1,99 @@
+/*
+ * 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.
+ */
+
+package androidx.paging
+
+import androidx.arch.core.util.Function
+import java.util.IdentityHashMap
+
+internal class WrapperItemKeyedDataSource<K : Any, A : Any, B : Any>(
+    private val source: ItemKeyedDataSource<K, A>,
+    private val listFunction: Function<List<A>, List<B>>
+) : ItemKeyedDataSource<K, B>() {
+
+    private val keyMap = IdentityHashMap<B, K>()
+
+    override fun addInvalidatedCallback(onInvalidatedCallback: InvalidatedCallback) {
+        source.addInvalidatedCallback(onInvalidatedCallback)
+    }
+
+    override fun removeInvalidatedCallback(onInvalidatedCallback: InvalidatedCallback) {
+        source.removeInvalidatedCallback(onInvalidatedCallback)
+    }
+
+    override fun invalidate() {
+        source.invalidate()
+    }
+
+    override val isInvalid
+        get() = source.isInvalid
+
+    fun convertWithStashedKeys(source: List<A>): List<B> {
+        val dest = convert(listFunction, source)
+        synchronized(keyMap) {
+            // synchronize on keyMap, since multiple loads may occur simultaneously.
+            // Note: manually sync avoids locking per-item (e.g. Collections.synchronizedMap)
+            for (i in dest.indices) {
+                keyMap[dest[i]] = this.source.getKey(source[i])
+            }
+        }
+        return dest
+    }
+
+    override fun loadInitial(params: LoadInitialParams<K>, callback: LoadInitialCallback<B>) {
+        source.loadInitial(params, object : ItemKeyedDataSource.LoadInitialCallback<A>() {
+            override fun onResult(data: List<A>, position: Int, totalCount: Int) {
+                callback.onResult(convertWithStashedKeys(data), position, totalCount)
+            }
+
+            override fun onResult(data: List<A>) {
+                callback.onResult(convertWithStashedKeys(data))
+            }
+
+            override fun onError(error: Throwable) {
+                callback.onError(error)
+            }
+        })
+    }
+
+    override fun loadAfter(params: LoadParams<K>, callback: LoadCallback<B>) {
+        source.loadAfter(params, object : ItemKeyedDataSource.LoadCallback<A>() {
+            override fun onResult(data: List<A>) {
+                callback.onResult(convertWithStashedKeys(data))
+            }
+
+            override fun onError(error: Throwable) {
+                callback.onError(error)
+            }
+        })
+    }
+
+    override fun loadBefore(params: LoadParams<K>, callback: LoadCallback<B>) {
+        source.loadBefore(params, object : ItemKeyedDataSource.LoadCallback<A>() {
+            override fun onResult(data: List<A>) {
+                callback.onResult(convertWithStashedKeys(data))
+            }
+
+            override fun onError(error: Throwable) {
+                callback.onError(error)
+            }
+        })
+    }
+
+    override fun getKey(item: B): K = synchronized(keyMap) {
+        return keyMap[item]!!
+    }
+}
diff --git a/paging/common/src/main/kotlin/androidx/paging/WrapperPageKeyedDataSource.kt b/paging/common/src/main/kotlin/androidx/paging/WrapperPageKeyedDataSource.kt
new file mode 100644
index 0000000..c49b480
--- /dev/null
+++ b/paging/common/src/main/kotlin/androidx/paging/WrapperPageKeyedDataSource.kt
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+
+package androidx.paging
+
+import androidx.arch.core.util.Function
+
+internal class WrapperPageKeyedDataSource<K : Any, A : Any, B : Any>(
+    private val source: PageKeyedDataSource<K, A>,
+    private val listFunction: Function<List<A>, List<B>>
+) : PageKeyedDataSource<K, B>() {
+    override val isInvalid
+        get() = source.isInvalid
+
+    override fun addInvalidatedCallback(onInvalidatedCallback: InvalidatedCallback) =
+        source.addInvalidatedCallback(onInvalidatedCallback)
+
+    override fun removeInvalidatedCallback(onInvalidatedCallback: InvalidatedCallback) =
+        source.removeInvalidatedCallback(onInvalidatedCallback)
+
+    override fun invalidate() = source.invalidate()
+
+    override fun loadInitial(params: LoadInitialParams<K>, callback: LoadInitialCallback<K, B>) {
+        source.loadInitial(params, object : PageKeyedDataSource.LoadInitialCallback<K, A>() {
+            override fun onResult(
+                data: List<A>,
+                position: Int,
+                totalCount: Int,
+                previousPageKey: K?,
+                nextPageKey: K?
+            ) {
+                val convertedData = convert(listFunction, data)
+                callback.onResult(convertedData, position, totalCount, previousPageKey, nextPageKey)
+            }
+
+            override fun onResult(data: List<A>, previousPageKey: K?, nextPageKey: K?) {
+                val convertedData = convert(listFunction, data)
+                callback.onResult(convertedData, previousPageKey, nextPageKey)
+            }
+
+            override fun onError(error: Throwable) = callback.onError(error)
+        })
+    }
+
+    override fun loadBefore(params: LoadParams<K>, callback: LoadCallback<K, B>) {
+        source.loadBefore(params, object : PageKeyedDataSource.LoadCallback<K, A>() {
+            override fun onResult(data: List<A>, adjacentPageKey: K?) =
+                callback.onResult(convert(listFunction, data), adjacentPageKey)
+
+            override fun onError(error: Throwable) = callback.onError(error)
+        })
+    }
+
+    override fun loadAfter(params: LoadParams<K>, callback: LoadCallback<K, B>) {
+        source.loadAfter(params, object : PageKeyedDataSource.LoadCallback<K, A>() {
+            override fun onResult(data: List<A>, adjacentPageKey: K?) =
+                callback.onResult(convert(listFunction, data), adjacentPageKey)
+
+            override fun onError(error: Throwable) = callback.onError(error)
+        })
+    }
+}
diff --git a/paging/common/src/main/kotlin/androidx/paging/WrapperPositionalDataSource.kt b/paging/common/src/main/kotlin/androidx/paging/WrapperPositionalDataSource.kt
new file mode 100644
index 0000000..683d670
--- /dev/null
+++ b/paging/common/src/main/kotlin/androidx/paging/WrapperPositionalDataSource.kt
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+package androidx.paging
+
+import androidx.arch.core.util.Function
+
+internal class WrapperPositionalDataSource<A : Any, B : Any>(
+    private val source: PositionalDataSource<A>,
+    val listFunction: Function<List<A>, List<B>>
+) : PositionalDataSource<B>() {
+    override val isInvalid
+        get() = source.isInvalid
+
+    override fun addInvalidatedCallback(onInvalidatedCallback: InvalidatedCallback) =
+        source.addInvalidatedCallback(onInvalidatedCallback)
+
+    override fun removeInvalidatedCallback(onInvalidatedCallback: InvalidatedCallback) =
+        source.removeInvalidatedCallback(onInvalidatedCallback)
+
+    override fun invalidate() = source.invalidate()
+
+    override fun loadInitial(params: LoadInitialParams, callback: LoadInitialCallback<B>) {
+        source.loadInitial(params, object : LoadInitialCallback<A>() {
+            override fun onResult(data: List<A>, position: Int, totalCount: Int) =
+                callback.onResult(convert(listFunction, data), position, totalCount)
+
+            override fun onResult(data: List<A>, position: Int) =
+                callback.onResult(convert(listFunction, data), position)
+
+            override fun onError(error: Throwable) = callback.onError(error)
+        })
+    }
+
+    override fun loadRange(params: LoadRangeParams, callback: LoadRangeCallback<B>) {
+        source.loadRange(params, object : LoadRangeCallback<A>() {
+            override fun onResult(data: List<A>) = callback.onResult(convert(listFunction, data))
+
+            override fun onError(error: Throwable) = callback.onError(error)
+        })
+    }
+}
diff --git a/paging/common/src/main/kotlin/androidx/paging/futures/DirectExecutor.kt b/paging/common/src/main/kotlin/androidx/paging/futures/DirectExecutor.kt
new file mode 100644
index 0000000..2d1da12
--- /dev/null
+++ b/paging/common/src/main/kotlin/androidx/paging/futures/DirectExecutor.kt
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+package androidx.paging.futures
+
+import androidx.annotation.RestrictTo
+
+import java.util.concurrent.Executor
+
+/**
+ * TODO: Make this internal and remove @RestrictTo once common-ktx is merged in.
+ *
+ * An [Executor] that runs each task in the thread that invokes [execute][Executor.execute]
+ *
+ * This instance is equivalent to:
+ *```
+ * final class DirectExecutor implements Executor {
+ *     public void execute(Runnable r) {
+ *         r.run();
+ *     }
+ * }
+ *```
+ *
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+object DirectExecutor : Executor {
+    override fun execute(runnable: Runnable) = runnable.run()
+}
diff --git a/paging/common/src/main/kotlin/androidx/paging/futures/FutureCallback.kt b/paging/common/src/main/kotlin/androidx/paging/futures/FutureCallback.kt
new file mode 100644
index 0000000..899a5ec
--- /dev/null
+++ b/paging/common/src/main/kotlin/androidx/paging/futures/FutureCallback.kt
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+package androidx.paging.futures
+
+import androidx.annotation.RestrictTo
+
+import com.google.common.util.concurrent.ListenableFuture
+
+import java.util.concurrent.ExecutionException
+import java.util.concurrent.Future
+
+/**
+ * A callback for accepting the results of a [Future] computation asynchronously.
+ *
+ * To attach to a [ListenableFuture] use [addCallback].
+ * @param V Type of the Future result.
+ *
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+interface FutureCallback<V> {
+    /**
+     * Invoked with the result of the `Future` computation when it is successful.
+     */
+    fun onSuccess(value: V)
+
+    /**
+     * Invoked when a `Future` computation fails or is canceled.
+     *
+     * If the future's [get][Future.get] method throws an [ExecutionException], then the cause is
+     * passed to this method. Any other thrown object is passed unaltered.
+     */
+    fun onError(throwable: Throwable)
+}
diff --git a/paging/common/src/main/kotlin/androidx/paging/futures/Futures.kt b/paging/common/src/main/kotlin/androidx/paging/futures/Futures.kt
new file mode 100644
index 0000000..a44dddd
--- /dev/null
+++ b/paging/common/src/main/kotlin/androidx/paging/futures/Futures.kt
@@ -0,0 +1,143 @@
+/*
+ * 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.
+ */
+
+// TODO: Remove once paging-runtime is converted to .kt.
+@file:JvmName("Futures")
+
+package androidx.paging.futures
+
+import androidx.annotation.RestrictTo
+import androidx.arch.core.util.Function
+import androidx.concurrent.futures.ResolvableFuture
+
+import com.google.common.util.concurrent.ListenableFuture
+
+import java.util.concurrent.CancellationException
+import java.util.concurrent.ExecutionException
+import java.util.concurrent.Executor
+
+/**
+ * Registers separate success and failure callbacks to be run when the `Future`'s computation is
+ * complete or, if the computation is already complete, immediately.
+ *
+ * The callback is run on `executor`. There is no guaranteed ordering of execution of callbacks,
+ * but any callback added through this method is guaranteed to be called once the computation is
+ * complete.
+ *
+ * Example:
+ * ```
+ * ListenableFuture<QueryResult> future = ...;
+ * Executor e = ...
+ * addCallback(future, new FutureCallback<QueryResult>() {
+ *     public void onSuccess(QueryResult result) {
+ *         storeInCache(result);
+ *     }
+ *     public void onFailure(Throwable t) {
+ *         reportError(t);
+ *     }
+ * }, e);
+ * ```
+ *
+ * When selecting an executor, note that `directExecutor` is dangerous in some cases. See the
+ * discussion in the [ListenableFuture.addListener] documentation. All its warnings about
+ * heavyweight listeners are also applicable to heavyweight callbacks passed to this method.
+ *
+ * For a more general interface to attach a completion listener to a `Future`, see
+ * [ListenableFuture.addListener].
+ *
+ * @param callback The callback to invoke when `future` is completed.
+ * @param executor The executor to run `callback` when the future completes.
+ *
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+fun <V> ListenableFuture<out V>.addCallback(callback: FutureCallback<in V>, executor: Executor) {
+    addListener(Runnable {
+        val value: V
+        try {
+            value = get()
+        } catch (e: ExecutionException) {
+            callback.onError(e.cause ?: e)
+            return@Runnable
+        } catch (e: Throwable) {
+            callback.onError(e)
+            return@Runnable
+        }
+
+        callback.onSuccess(value)
+    }, executor)
+}
+
+/**
+ * Returns a new `Future` whose result is derived from the result of the given `Future`. If
+ * `input` fails, the returned `Future` fails with the same exception (and the function is not
+ * invoked). Example usage:
+ *
+ * ```
+ * ListenableFuture<QueryResult> queryFuture = ...;
+ * ListenableFuture<List<Row>> rowsFuture =
+ *     transform(queryFuture, QueryResult::getRows, executor);
+ * ```
+ *
+ * When selecting an executor, note that `directExecutor` is dangerous in some cases. See the
+ * discussion in the [ListenableFuture.addListener] documentation. All its warnings about
+ * heavyweight listeners are also applicable to heavyweight functions passed to this method.
+ *
+ * The returned `Future` attempts to keep its cancellation state in sync with that of the input
+ * future. That is, if the returned `Future` is cancelled, it will attempt to cancel the input,
+ * and if the input is cancelled, the returned `Future` will receive a callback in which it will
+ * attempt to cancel itself.
+ *
+ * An example use of this method is to convert a serializable object returned from an RPC into a
+ * POJO.
+ *
+ * @param function A Function to transform the results of the provided future to the results of
+ *                 the returned future.
+ * @param executor Executor to run the function in.
+ * @return A future that holds result of the transformation.
+ *
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+fun <I, O> ListenableFuture<out I>.transform(
+    function: Function<in I, out O>,
+    executor: Executor
+): ListenableFuture<O> {
+    val out = ResolvableFuture.create<O>()
+
+    // Add success/error callback.
+    addCallback(object : FutureCallback<I> {
+        override fun onSuccess(value: I) {
+            out.set(function.apply(value))
+        }
+
+        override fun onError(throwable: Throwable) {
+            out.setException(throwable)
+        }
+    }, executor)
+
+    // Propagate output future's cancellation to input future.
+    out.addCallback(object : FutureCallback<O> {
+        override fun onSuccess(value: O) {}
+
+        override fun onError(throwable: Throwable) {
+            if (throwable is CancellationException) {
+                cancel(false)
+            }
+        }
+    }, executor)
+    return out
+}
diff --git a/paging/common/src/test/java/androidx/paging/TiledDataSourceTest.kt b/paging/common/src/test/java/androidx/paging/TiledDataSourceTest.kt
deleted file mode 100644
index 2cae585..0000000
--- a/paging/common/src/test/java/androidx/paging/TiledDataSourceTest.kt
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright 2017 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.paging.futures.DirectExecutor
-import org.junit.Assert.assertEquals
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-
-@Suppress("DEPRECATION")
-@RunWith(JUnit4::class)
-class TiledDataSourceTest {
-
-    fun TiledDataSource<String>.loadInitial(
-        startPosition: Int,
-        count: Int,
-        pageSize: Int
-    ): List<String> {
-        initExecutor(DirectExecutor.INSTANCE)
-        return loadInitial(PositionalDataSource.LoadInitialParams(
-            startPosition, count, pageSize, true)).get().data
-    }
-
-    @Test
-    fun loadInitialEmpty() {
-        class EmptyDataSource : TiledDataSource<String>() {
-            override fun countItems(): Int {
-                return 0
-            }
-
-            override fun loadRange(startPosition: Int, count: Int): List<String> {
-                return emptyList()
-            }
-        }
-
-        assertEquals(emptyList<String>(), EmptyDataSource().loadInitial(0, 1, 5))
-    }
-
-    @Test
-    fun loadInitialTooLong() {
-        val list = List(26) { "" + 'a' + it }
-        class AlphabetDataSource : TiledDataSource<String>() {
-            override fun countItems(): Int {
-                return list.size
-            }
-
-            override fun loadRange(startPosition: Int, count: Int): List<String> {
-                return list.subList(startPosition, startPosition + count)
-            }
-        }
-        // baseline behavior
-        assertEquals(list, AlphabetDataSource().loadInitial(0, 26, 10))
-        assertEquals(list, AlphabetDataSource().loadInitial(50, 26, 10))
-    }
-}
diff --git a/paging/common/src/test/java/androidx/paging/ContiguousPagedListTest.kt b/paging/common/src/test/kotlin/androidx/paging/ContiguousPagedListTest.kt
similarity index 90%
rename from paging/common/src/test/java/androidx/paging/ContiguousPagedListTest.kt
rename to paging/common/src/test/kotlin/androidx/paging/ContiguousPagedListTest.kt
index c2a6f82..9ce5682 100644
--- a/paging/common/src/test/java/androidx/paging/ContiguousPagedListTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/ContiguousPagedListTest.kt
@@ -21,22 +21,22 @@
 import androidx.paging.PagedList.LoadState.RETRYABLE_ERROR
 import androidx.paging.futures.DirectExecutor
 import androidx.testutils.TestExecutor
+import com.nhaarman.mockitokotlin2.mock
+import com.nhaarman.mockitokotlin2.reset
+import com.nhaarman.mockitokotlin2.verify
+import com.nhaarman.mockitokotlin2.verifyNoMoreInteractions
+import com.nhaarman.mockitokotlin2.verifyZeroInteractions
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertSame
 import org.junit.Assert.assertTrue
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.Parameterized
-import org.mockito.Mockito.mock
-import org.mockito.Mockito.reset
-import org.mockito.Mockito.verify
-import org.mockito.Mockito.verifyNoMoreInteractions
-import org.mockito.Mockito.verifyZeroInteractions
 
 @RunWith(Parameterized::class)
 class ContiguousPagedListTest(private val placeholdersEnabled: Boolean) {
-    private val mMainThread = TestExecutor()
-    private val mBackgroundThread = TestExecutor()
+    private val mainThread = TestExecutor()
+    private val backgroundThread = TestExecutor()
 
     private class Item(position: Int) {
         val pos: Int = position
@@ -59,7 +59,7 @@
             callback: LoadInitialCallback<Item>
         ) {
             val initPos = params.requestedInitialKey ?: 0
-            val start = Math.max(initPos - params.requestedLoadSize / 2, 0)
+            val start = maxOf(initPos - params.requestedLoadSize / 2, 0)
 
             val result = getClampedRange(start, start + params.requestedLoadSize)
             if (result == null) {
@@ -94,13 +94,13 @@
         override fun getKey(item: Item): Int = item.pos
 
         private fun getClampedRange(startInc: Int, endExc: Int): List<Item>? {
-            val matching = errorIndices.filter { it in startInc..(endExc - 1) }
+            val matching = errorIndices.filter { it in startInc until endExc }
             if (matching.isNotEmpty()) {
                 // found indices with errors enqueued - fail to load them
                 errorIndices.removeAll(matching)
                 return null
             }
-            return listData.subList(Math.max(0, startInc), Math.min(listData.size, endExc))
+            return listData.subList(maxOf(0, startInc), minOf(listData.size, endExc))
         }
 
         fun enqueueErrorForIndex(index: Int) {
@@ -124,14 +124,20 @@
         return data
     }
 
-    private fun <E> PagedList<E>.addLoadStateCapture(desiredType: PagedList.LoadType):
+    private fun <E : Any> PagedList<E>.addLoadStateCapture(desiredType: PagedList.LoadType):
             MutableList<PagedList.LoadState> {
         val list = mutableListOf<PagedList.LoadState>()
-        this.addWeakLoadStateListener { type, state, _ ->
-            if (type == desiredType) {
-                list.add(state)
+        this.addWeakLoadStateListener(object : PagedList.LoadStateListener {
+            override fun onLoadStateChanged(
+                type: PagedList.LoadType,
+                state: PagedList.LoadState,
+                error: Throwable?
+            ) {
+                if (type == desiredType) {
+                    list.add(state)
+                }
             }
-        }
+        })
         return list
     }
 
@@ -162,7 +168,7 @@
     }
 
     private fun verifyRange(start: Int, count: Int, actual: PagedList<Item>) {
-        verifyRange(start, count, actual.mStorage)
+        verifyRange(start, count, actual.storage)
         assertEquals(count, actual.loadedCount)
     }
 
@@ -178,9 +184,9 @@
     ): ContiguousPagedList<Int, Item> {
         val ret = PagedList.create(
             dataSource,
-            mMainThread,
-            mBackgroundThread,
-            DirectExecutor.INSTANCE,
+            mainThread,
+            backgroundThread,
+            DirectExecutor,
             boundaryCallback,
             PagedList.Config.Builder()
                 .setPageSize(pageSize)
@@ -260,7 +266,7 @@
     @Test
     fun append() {
         val pagedList = createCountedPagedList(0)
-        val callback = mock(PagedList.Callback::class.java)
+        val callback = mock<PagedList.Callback>()
         pagedList.addWeakCallback(null, callback)
         verifyRange(0, 40, pagedList)
         verifyZeroInteractions(callback)
@@ -276,7 +282,7 @@
     @Test
     fun prepend() {
         val pagedList = createCountedPagedList(80)
-        val callback = mock(PagedList.Callback::class.java)
+        val callback = mock<PagedList.Callback>()
         pagedList.addWeakCallback(null, callback)
         verifyRange(60, 40, pagedList)
         verifyZeroInteractions(callback)
@@ -292,7 +298,7 @@
     @Test
     fun outwards() {
         val pagedList = createCountedPagedList(40)
-        val callback = mock(PagedList.Callback::class.java)
+        val callback = mock<PagedList.Callback>()
         pagedList.addWeakCallback(null, callback)
         verifyRange(20, 40, pagedList)
         verifyZeroInteractions(callback)
@@ -379,7 +385,7 @@
             prefetchDistance = 1,
             maxSize = 70
         )
-        val callback = mock(PagedList.Callback::class.java)
+        val callback = mock<PagedList.Callback>()
         pagedList.addWeakCallback(null, callback)
         verifyRange(0, 20, pagedList)
         verifyZeroInteractions(callback)
@@ -416,7 +422,7 @@
             prefetchDistance = 1,
             maxSize = 70
         )
-        val callback = mock(PagedList.Callback::class.java)
+        val callback = mock<PagedList.Callback>()
         pagedList.addWeakCallback(null, callback)
         verifyRange(80, 20, pagedList)
         verifyZeroInteractions(callback)
@@ -462,18 +468,18 @@
         drain()
         verifyRange(1, 3, pagedList)
 
-        val callback = mock(PagedList.Callback::class.java)
+        val callback = mock<PagedList.Callback>()
         pagedList.addWeakCallback(null, callback)
 
         // start a load at the beginning...
         pagedList.loadAround(if (placeholdersEnabled) 1 else 0)
 
-        mBackgroundThread.executeAll()
+        backgroundThread.executeAll()
 
         // but before page received, access near end of list
         pagedList.loadAround(if (placeholdersEnabled) 3 else 2)
         verifyZeroInteractions(callback)
-        mMainThread.executeAll()
+        mainThread.executeAll()
         // and the load at the beginning is dropped without signaling callback
         verifyNoMoreInteractions(callback)
         verifyRange(1, 3, pagedList)
@@ -504,18 +510,18 @@
         pagedList.loadAround(if (placeholdersEnabled) 2 else 0)
         drain()
 
-        val callback = mock(PagedList.Callback::class.java)
+        val callback = mock<PagedList.Callback>()
         pagedList.addWeakCallback(null, callback)
 
         // start a load at the end...
         pagedList.loadAround(if (placeholdersEnabled) 3 else 2)
 
-        mBackgroundThread.executeAll()
+        backgroundThread.executeAll()
 
         // but before page received, access near front of list
         pagedList.loadAround(if (placeholdersEnabled) 1 else 0)
         verifyZeroInteractions(callback)
-        mMainThread.executeAll()
+        mainThread.executeAll()
         // and the load at the end is dropped without signaling callback
         verifyNoMoreInteractions(callback)
         verifyRange(1, 3, pagedList)
@@ -542,7 +548,7 @@
 
         // trigger load
         pagedList.loadAround(35)
-        mMainThread.executeAll()
+        mainThread.executeAll()
         assertEquals(listOf(LOADING), states.getAllAndClear())
         verifyRange(0, 40, pagedList)
 
@@ -555,7 +561,7 @@
 
         // trigger load which will error
         pagedList.loadAround(55)
-        mMainThread.executeAll()
+        mainThread.executeAll()
         assertEquals(listOf(LOADING), states.getAllAndClear())
         verifyRange(0, 60, pagedList)
 
@@ -566,7 +572,7 @@
 
         // retry
         pagedList.retry()
-        mMainThread.executeAll()
+        mainThread.executeAll()
         assertEquals(listOf(LOADING), states.getAllAndClear())
 
         // load finishes
@@ -644,11 +650,17 @@
         // have an error, move loading range, error goes away
         val pagedList = createCountedPagedList(0)
         val states = mutableListOf<PagedList.LoadState>()
-        pagedList.addWeakLoadStateListener { type, state, _ ->
-            if (type == PagedList.LoadType.END) {
-                states.add(state)
+        pagedList.addWeakLoadStateListener(object : PagedList.LoadStateListener {
+            override fun onLoadStateChanged(
+                type: PagedList.LoadType,
+                state: PagedList.LoadState,
+                error: Throwable?
+            ) {
+                if (type == PagedList.LoadType.END) {
+                    states.add(state)
+                }
             }
-        }
+        })
 
         pagedList.dataSource.enqueueErrorForIndex(45)
         pagedList.loadAround(35)
@@ -661,9 +673,12 @@
     fun distantPrefetch() {
         val pagedList = createCountedPagedList(
             0,
-            initLoadSize = 10, pageSize = 10, prefetchDistance = 30
+            initLoadSize = 10,
+            pageSize = 10,
+            prefetchDistance = 30
         )
-        val callback = mock(PagedList.Callback::class.java)
+
+        val callback = mock<PagedList.Callback>()
         pagedList.addWeakCallback(null, callback)
         verifyRange(0, 10, pagedList)
         verifyZeroInteractions(callback)
@@ -700,7 +715,7 @@
         verifyRange(0, 60, snapshot)
 
         // and verify the snapshot hasn't received them
-        val callback = mock(PagedList.Callback::class.java)
+        val callback = mock<PagedList.Callback>()
         pagedList.addWeakCallback(snapshot, callback)
         verifyCallback(callback, 60)
         verifyNoMoreInteractions(callback)
@@ -724,7 +739,7 @@
         verifyRange(20, 80, pagedList)
         verifyRange(40, 60, snapshot)
 
-        val callback = mock(PagedList.Callback::class.java)
+        val callback = mock<PagedList.Callback>()
         pagedList.addWeakCallback(snapshot, callback)
         verifyCallback(callback, 40, 0)
         verifyNoMoreInteractions(callback)
@@ -739,7 +754,7 @@
             dataSource = ListDataSource(ITEMS)
         )
         // With positional DataSource, last load is param passed
-        assertEquals(4, pagedList.mLastLoad)
+        assertEquals(4, pagedList.lastLoad)
         verifyRange(0, 20, pagedList)
     }
 
@@ -750,14 +765,14 @@
             initLoadSize = 20
         )
         // last load is middle of initial load
-        assertEquals(10, pagedList.mLastLoad)
+        assertEquals(10, pagedList.lastLoad)
         verifyRange(0, 20, pagedList)
     }
 
     @Test
     fun addWeakCallbackEmpty() {
         val pagedList = createCountedPagedList(0)
-        val callback = mock(PagedList.Callback::class.java)
+        val callback = mock<PagedList.Callback>()
         verifyRange(0, 40, pagedList)
 
         // capture empty snapshot
@@ -782,8 +797,7 @@
     @Test
     fun boundaryCallback_empty() {
         @Suppress("UNCHECKED_CAST")
-        val boundaryCallback =
-            mock(PagedList.BoundaryCallback::class.java) as PagedList.BoundaryCallback<Item>
+        val boundaryCallback = mock<PagedList.BoundaryCallback<Item>>()
         val pagedList = createCountedPagedList(
             0,
             listData = ArrayList(), boundaryCallback = boundaryCallback
@@ -803,8 +817,7 @@
     fun boundaryCallback_singleInitialLoad() {
         val shortList = ITEMS.subList(0, 4)
         @Suppress("UNCHECKED_CAST")
-        val boundaryCallback =
-            mock(PagedList.BoundaryCallback::class.java) as PagedList.BoundaryCallback<Item>
+        val boundaryCallback = mock<PagedList.BoundaryCallback<Item>>()
         val pagedList = createCountedPagedList(
             0, listData = shortList,
             initLoadSize = shortList.size, boundaryCallback = boundaryCallback
@@ -826,8 +839,7 @@
     @Test
     fun boundaryCallback_delayed() {
         @Suppress("UNCHECKED_CAST")
-        val boundaryCallback =
-            mock(PagedList.BoundaryCallback::class.java) as PagedList.BoundaryCallback<Item>
+        val boundaryCallback = mock<PagedList.BoundaryCallback<Item>>()
         val pagedList = createCountedPagedList(
             90,
             initLoadSize = 20, prefetchDistance = 5, boundaryCallback = boundaryCallback
@@ -872,8 +884,8 @@
     private fun drain() {
         var executed: Boolean
         do {
-            executed = mBackgroundThread.executeAll()
-            executed = mMainThread.executeAll() || executed
+            executed = backgroundThread.executeAll()
+            executed = mainThread.executeAll() || executed
         } while (executed)
     }
 
diff --git a/paging/common/src/test/java/androidx/paging/FailExecutor.kt b/paging/common/src/test/kotlin/androidx/paging/FailExecutor.kt
similarity index 100%
rename from paging/common/src/test/java/androidx/paging/FailExecutor.kt
rename to paging/common/src/test/kotlin/androidx/paging/FailExecutor.kt
diff --git a/paging/common/src/test/java/androidx/paging/ItemKeyedDataSourceTest.kt b/paging/common/src/test/kotlin/androidx/paging/ItemKeyedDataSourceTest.kt
similarity index 86%
rename from paging/common/src/test/java/androidx/paging/ItemKeyedDataSourceTest.kt
rename to paging/common/src/test/kotlin/androidx/paging/ItemKeyedDataSourceTest.kt
index fb72c74..819d9db 100644
--- a/paging/common/src/test/java/androidx/paging/ItemKeyedDataSourceTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/ItemKeyedDataSourceTest.kt
@@ -16,7 +16,10 @@
 
 package androidx.paging
 
+import androidx.arch.core.util.Function
 import androidx.paging.futures.DirectExecutor
+import com.nhaarman.mockitokotlin2.capture
+import com.nhaarman.mockitokotlin2.mock
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertTrue
 import org.junit.Assert.fail
@@ -24,7 +27,6 @@
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
 import org.mockito.ArgumentCaptor
-import org.mockito.Mockito.mock
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.verifyNoMoreInteractions
 
@@ -39,9 +41,10 @@
         initialLoadSize: Int,
         enablePlaceholders: Boolean
     ): DataSource.BaseResult<Item> {
-        dataSource.initExecutor(DirectExecutor.INSTANCE)
+        dataSource.initExecutor(DirectExecutor)
         return dataSource.loadInitial(
-            ItemKeyedDataSource.LoadInitialParams(key, initialLoadSize, enablePlaceholders)).get()
+            ItemKeyedDataSource.LoadInitialParams(key, initialLoadSize, enablePlaceholders)
+        ).get()
     }
 
     @Test
@@ -178,15 +181,15 @@
     fun loadBefore() {
         val dataSource = ItemDataSource()
         @Suppress("UNCHECKED_CAST")
-        val callback = mock(ItemKeyedDataSource.LoadCallback::class.java)
-                as ItemKeyedDataSource.LoadCallback<Item>
+        val callback = mock<ItemKeyedDataSource.LoadCallback<Item>>()
 
         dataSource.loadBefore(
-                ItemKeyedDataSource.LoadParams(dataSource.getKey(ITEMS_BY_NAME_ID[5]), 5), callback)
+            ItemKeyedDataSource.LoadParams(dataSource.getKey(ITEMS_BY_NAME_ID[5]), 5), callback
+        )
 
         @Suppress("UNCHECKED_CAST")
         val argument = ArgumentCaptor.forClass(List::class.java) as ArgumentCaptor<List<Item>>
-        verify(callback).onResult(argument.capture())
+        verify(callback).onResult(capture(argument))
         verifyNoMoreInteractions(callback)
 
         val observed = argument.value
@@ -219,9 +222,9 @@
                 return
             }
 
-            val key = params.requestedInitialKey ?: Key("", Integer.MAX_VALUE)
-            val start = Math.max(0, findFirstIndexAfter(key) - params.requestedLoadSize / 2)
-            val endExclusive = Math.min(start + params.requestedLoadSize, items.size)
+            val key = params.requestedInitialKey ?: Key("", Int.MAX_VALUE)
+            val start = maxOf(0, findFirstIndexAfter(key) - params.requestedLoadSize / 2)
+            val endExclusive = minOf(start + params.requestedLoadSize, items.size)
 
             if (params.placeholdersEnabled && counted) {
                 callback.onResult(items.subList(start, endExclusive), start, items.size)
@@ -238,7 +241,7 @@
             }
 
             val start = findFirstIndexAfter(params.key)
-            val endExclusive = Math.min(start + params.requestedLoadSize, items.size)
+            val endExclusive = minOf(start + params.requestedLoadSize, items.size)
 
             callback.onResult(items.subList(start, endExclusive))
         }
@@ -251,8 +254,8 @@
             }
 
             val firstIndexBefore = findFirstIndexBefore(params.key)
-            val endExclusive = Math.max(0, firstIndexBefore + 1)
-            val start = Math.max(0, firstIndexBefore - params.requestedLoadSize + 1)
+            val endExclusive = maxOf(0, firstIndexBefore + 1)
+            val start = maxOf(0, firstIndexBefore - params.requestedLoadSize + 1)
 
             callback.onResult(items.subList(start, endExclusive))
         }
@@ -307,14 +310,16 @@
             }
         }
 
-        PagedList.create(dataSource, FailExecutor(),
-            DirectExecutor.INSTANCE,
-            DirectExecutor.INSTANCE,
+        PagedList.create(
+            dataSource, FailExecutor(),
+            DirectExecutor,
+            DirectExecutor,
             null,
             PagedList.Config.Builder()
                 .setPageSize(10)
                 .build(),
-            "").get()
+            ""
+        ).get()
     }
 
     @Test
@@ -354,8 +359,9 @@
         it.onResult(emptyList(), 0, 2)
     }
 
-    private abstract class WrapperDataSource<K, A, B>(private val source: ItemKeyedDataSource<K, A>)
-            : ItemKeyedDataSource<K, B>() {
+    private abstract class WrapperDataSource<K : Any, A : Any, B : Any>(
+        private val source: ItemKeyedDataSource<K, A>
+    ) : ItemKeyedDataSource<K, B>() {
         override fun addInvalidatedCallback(onInvalidatedCallback: InvalidatedCallback) {
             source.addInvalidatedCallback(onInvalidatedCallback)
         }
@@ -368,9 +374,8 @@
             source.invalidate()
         }
 
-        override fun isInvalid(): Boolean {
-            return source.isInvalid
-        }
+        override val isInvalid
+            get() = source.isInvalid
 
         override fun loadInitial(params: LoadInitialParams<K>, callback: LoadInitialCallback<B>) {
             source.loadInitial(params, object : LoadInitialCallback<A>() {
@@ -378,7 +383,7 @@
                     callback.onResult(convert(data), position, totalCount)
                 }
 
-                override fun onResult(data: MutableList<A>) {
+                override fun onResult(data: List<A>) {
                     callback.onResult(convert(data))
                 }
 
@@ -390,7 +395,7 @@
 
         override fun loadAfter(params: LoadParams<K>, callback: LoadCallback<B>) {
             source.loadAfter(params, object : LoadCallback<A>() {
-                override fun onResult(data: MutableList<A>) {
+                override fun onResult(data: List<A>) {
                     callback.onResult(convert(data))
                 }
 
@@ -402,7 +407,7 @@
 
         override fun loadBefore(params: LoadParams<K>, callback: LoadCallback<B>) {
             source.loadBefore(params, object : LoadCallback<A>() {
-                override fun onResult(data: MutableList<A>) {
+                override fun onResult(data: List<A>) {
                     callback.onResult(convert(data))
                 }
 
@@ -417,8 +422,8 @@
 
     private data class DecoratedItem(val item: Item)
 
-    private class DecoratedWrapperDataSource(private val source: ItemKeyedDataSource<Key, Item>)
-            : WrapperDataSource<Key, Item, DecoratedItem>(source) {
+    private class DecoratedWrapperDataSource(private val source: ItemKeyedDataSource<Key, Item>) :
+        WrapperDataSource<Key, Item, DecoratedItem>(source) {
         override fun convert(source: List<Item>): List<DecoratedItem> {
             return source.map { DecoratedItem(it) }
         }
@@ -438,14 +443,15 @@
 
         // load initial - success
         @Suppress("UNCHECKED_CAST")
-        val loadInitialCallback = mock(ItemKeyedDataSource.LoadInitialCallback::class.java)
-                as ItemKeyedDataSource.LoadInitialCallback<DecoratedItem>
+        val loadInitialCallback = mock<ItemKeyedDataSource.LoadInitialCallback<DecoratedItem>>()
         val initKey = orig.getKey(ITEMS_BY_NAME_ID.first())
         val initParams = ItemKeyedDataSource.LoadInitialParams(initKey, 10, false)
-        wrapper.loadInitial(initParams,
-                loadInitialCallback)
+        wrapper.loadInitial(
+            initParams,
+            loadInitialCallback
+        )
         verify(loadInitialCallback).onResult(
-                ITEMS_BY_NAME_ID.subList(0, 10).map { DecoratedItem(it) })
+            ITEMS_BY_NAME_ID.subList(0, 10).map { DecoratedItem(it) })
         //     error
         orig.enqueueError()
         wrapper.loadInitial(initParams, loadInitialCallback)
@@ -454,8 +460,8 @@
 
         val key = orig.getKey(ITEMS_BY_NAME_ID[20])
         @Suppress("UNCHECKED_CAST")
-        var loadCallback = mock(ItemKeyedDataSource.LoadCallback::class.java)
-                as ItemKeyedDataSource.LoadCallback<DecoratedItem>
+        var loadCallback = mock<ItemKeyedDataSource.LoadCallback<DecoratedItem>>()
+
         // load after
         wrapper.loadAfter(ItemKeyedDataSource.LoadParams(key, 10), loadCallback)
         verify(loadCallback).onResult(ITEMS_BY_NAME_ID.subList(21, 31).map { DecoratedItem(it) })
@@ -467,8 +473,7 @@
 
         // load before
         @Suppress("UNCHECKED_CAST")
-        loadCallback = mock(ItemKeyedDataSource.LoadCallback::class.java)
-                as ItemKeyedDataSource.LoadCallback<DecoratedItem>
+        loadCallback = mock()
         wrapper.loadBefore(ItemKeyedDataSource.LoadParams(key, 10), loadCallback)
         verify(loadCallback).onResult(ITEMS_BY_NAME_ID.subList(10, 20).map { DecoratedItem(it) })
         // load before - error
@@ -489,18 +494,18 @@
 
     @Test
     fun testListConverterWrappedDataSource() = verifyWrappedDataSource { dataSource ->
-        dataSource.mapByPage { page -> page.map { DecoratedItem(it) } }
+        dataSource.mapByPage(Function { page -> page.map { DecoratedItem(it) } })
     }
 
     @Test
     fun testItemConverterWrappedDataSource() = verifyWrappedDataSource { dataSource ->
-        dataSource.map { DecoratedItem(it) }
+        dataSource.map(Function { DecoratedItem(it) })
     }
 
     @Test
     fun testInvalidateToWrapper() {
         val orig = ItemDataSource()
-        val wrapper = orig.map { DecoratedItem(it) }
+        val wrapper = orig.map<DecoratedItem>(Function { DecoratedItem(it) })
 
         orig.invalidate()
         assertTrue(wrapper.isInvalid)
@@ -509,7 +514,7 @@
     @Test
     fun testInvalidateFromWrapper() {
         val orig = ItemDataSource()
-        val wrapper = orig.map { DecoratedItem(it) }
+        val wrapper = orig.map<DecoratedItem>(Function { DecoratedItem(it) })
 
         wrapper.invalidate()
         assertTrue(orig.isInvalid)
@@ -521,10 +526,12 @@
 
         private val ITEMS_BY_NAME_ID = List(100) {
             val names = Array(10) { index -> "f" + ('a' + index) }
-            Item(names[it % 10],
-                    it,
-                    Math.random() * 1000,
-                    (Math.random() * 200).toInt().toString() + " fake st.")
+            Item(
+                names[it % 10],
+                it,
+                Math.random() * 1000,
+                (Math.random() * 200).toInt().toString() + " fake st."
+            )
         }.sortedWith(ITEM_COMPARATOR)
 
         private val EXCEPTION = Exception()
diff --git a/paging/common/src/test/java/androidx/paging/PageKeyedDataSourceTest.kt b/paging/common/src/test/kotlin/androidx/paging/PageKeyedDataSourceTest.kt
similarity index 85%
rename from paging/common/src/test/java/androidx/paging/PageKeyedDataSourceTest.kt
rename to paging/common/src/test/kotlin/androidx/paging/PageKeyedDataSourceTest.kt
index 6122172..63d3b28 100644
--- a/paging/common/src/test/java/androidx/paging/PageKeyedDataSourceTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/PageKeyedDataSourceTest.kt
@@ -16,6 +16,7 @@
 
 package androidx.paging
 
+import androidx.arch.core.util.Function
 import androidx.paging.futures.DirectExecutor
 import androidx.testutils.TestExecutor
 import org.junit.Assert.assertEquals
@@ -32,15 +33,15 @@
 
 @RunWith(JUnit4::class)
 class PageKeyedDataSourceTest {
-    private val mMainThread = TestExecutor()
-    private val mBackgroundThread = TestExecutor()
+    private val mainThread = TestExecutor()
+    private val backgroundThread = TestExecutor()
 
     internal data class Item(val name: String)
 
     internal data class Page(val prev: String?, val data: List<Item>, val next: String?)
 
-    internal class ItemDataSource(val data: Map<String, Page> = PAGE_MAP)
-            : PageKeyedDataSource<String, Item>() {
+    internal class ItemDataSource(val data: Map<String, Page> = PAGE_MAP) :
+        PageKeyedDataSource<String, Item>() {
         private var error = false
 
         private fun getPage(key: String): Page = data[key]!!
@@ -91,14 +92,14 @@
         // validate paging entire ItemDataSource results in full, correctly ordered data
         val pagedListFuture = PagedList.create(
             ItemDataSource(),
-            mMainThread,
-            mBackgroundThread,
-            mBackgroundThread,
+            mainThread,
+            backgroundThread,
+            backgroundThread,
             null,
             PagedList.Config.Builder().setPageSize(100).build(),
             null
         )
-        mBackgroundThread.executeAll()
+        backgroundThread.executeAll()
         val pagedList = pagedListFuture.get()
 
         // validate initial load
@@ -118,7 +119,7 @@
     private fun performLoadInitial(
         invalidateDataSource: Boolean = false,
         callbackInvoker:
-                (callback: PageKeyedDataSource.LoadInitialCallback<String, String>) -> Unit
+            (callback: PageKeyedDataSource.LoadInitialCallback<String, String>) -> Unit
     ) {
         val dataSource = object : PageKeyedDataSource<String, String>() {
             override fun loadInitial(
@@ -147,14 +148,17 @@
             }
         }
 
-        PagedList.create(dataSource, FailExecutor(),
-            DirectExecutor.INSTANCE,
-            DirectExecutor.INSTANCE,
+        PagedList.create(
+            dataSource,
+            FailExecutor(),
+            DirectExecutor,
+            DirectExecutor,
             null,
             PagedList.Config.Builder()
                 .setPageSize(10)
                 .build(),
-            "").get()
+            ""
+        ).get()
     }
 
     @Test
@@ -196,7 +200,7 @@
 
     @Test
     fun pageDroppingNotSupported() {
-        assertFalse(ItemDataSource().supportsPageDropping())
+        assertFalse(ItemDataSource().supportsPageDropping)
     }
 
     @Test
@@ -228,10 +232,11 @@
 
         @Suppress("UNCHECKED_CAST")
         val boundaryCallback =
-                mock(PagedList.BoundaryCallback::class.java) as PagedList.BoundaryCallback<String>
+            mock(PagedList.BoundaryCallback::class.java) as PagedList.BoundaryCallback<String>
         val executor = TestExecutor()
 
-        val pagedList = PagedList.create(dataSource,
+        val pagedList = PagedList.create(
+            dataSource,
             executor,
             executor,
             executor,
@@ -239,7 +244,8 @@
             PagedList.Config.Builder()
                 .setPageSize(10)
                 .build(),
-            "").apply { executor.executeAll() }.get()
+            ""
+        ).apply { executor.executeAll() }.get()
 
         pagedList.loadAround(0)
 
@@ -281,10 +287,11 @@
 
         @Suppress("UNCHECKED_CAST")
         val boundaryCallback =
-                mock(PagedList.BoundaryCallback::class.java) as PagedList.BoundaryCallback<String>
+            mock(PagedList.BoundaryCallback::class.java) as PagedList.BoundaryCallback<String>
         val executor = TestExecutor()
 
-        val pagedList = PagedList.create(dataSource,
+        val pagedList = PagedList.create(
+            dataSource,
             executor,
             executor,
             executor,
@@ -292,7 +299,8 @@
             PagedList.Config.Builder()
                 .setPageSize(10)
                 .build(),
-            "").apply { executor.executeAll() }.get()
+            ""
+        ).apply { executor.executeAll() }.get()
 
         pagedList.loadAround(0)
 
@@ -306,8 +314,9 @@
         verifyNoMoreInteractions(boundaryCallback)
     }
 
-    private abstract class WrapperDataSource<K, A, B>(private val source: PageKeyedDataSource<K, A>)
-            : PageKeyedDataSource<K, B>() {
+    private abstract class WrapperDataSource<K : Any, A : Any, B : Any>(
+        private val source: PageKeyedDataSource<K, A>
+    ) : PageKeyedDataSource<K, B>() {
         override fun addInvalidatedCallback(onInvalidatedCallback: InvalidatedCallback) {
             source.addInvalidatedCallback(onInvalidatedCallback)
         }
@@ -320,9 +329,8 @@
             source.invalidate()
         }
 
-        override fun isInvalid(): Boolean {
-            return source.isInvalid
-        }
+        override val isInvalid
+            get() = source.isInvalid
 
         override fun loadInitial(
             params: LoadInitialParams<K>,
@@ -336,11 +344,16 @@
                     previousPageKey: K?,
                     nextPageKey: K?
                 ) {
-                    callback.onResult(convert(data), position, totalCount,
-                            previousPageKey, nextPageKey)
+                    callback.onResult(
+                        convert(data),
+                        position,
+                        totalCount,
+                        previousPageKey,
+                        nextPageKey
+                    )
                 }
 
-                override fun onResult(data: MutableList<A>, previousPageKey: K?, nextPageKey: K?) {
+                override fun onResult(data: List<A>, previousPageKey: K?, nextPageKey: K?) {
                     callback.onResult(convert(data), previousPageKey, nextPageKey)
                 }
 
@@ -377,8 +390,8 @@
         protected abstract fun convert(source: List<A>): List<B>
     }
 
-    private class StringWrapperDataSource<K, V>(source: PageKeyedDataSource<K, V>)
-            : WrapperDataSource<K, V, String>(source) {
+    private class StringWrapperDataSource<K : Any, V : Any>(source: PageKeyedDataSource<K, V>) :
+        WrapperDataSource<K, V, String>(source) {
         override fun convert(source: List<V>): List<String> {
             return source.map { it.toString() }
         }
@@ -398,9 +411,11 @@
 
         val initParams = PageKeyedDataSource.LoadInitialParams<String>(4, true)
         wrapper.loadInitial(initParams, loadInitialCallback)
-        val expectedInitial = PAGE_MAP[INIT_KEY]!!
-        verify(loadInitialCallback).onResult(expectedInitial.data.map { it.toString() },
-                expectedInitial.prev, expectedInitial.next)
+        val expectedInitial = PAGE_MAP.getValue(INIT_KEY)
+        verify(loadInitialCallback).onResult(
+            expectedInitial.data.map { it.toString() },
+            expectedInitial.prev, expectedInitial.next
+        )
         verifyNoMoreInteractions(loadInitialCallback)
 
         @Suppress("UNCHECKED_CAST")
@@ -421,8 +436,10 @@
         loadCallback = mock(PageKeyedDataSource.LoadCallback::class.java)
                 as PageKeyedDataSource.LoadCallback<String, String>
         wrapper.loadBefore(PageKeyedDataSource.LoadParams(expectedAfter.prev!!, 4), loadCallback)
-        verify(loadCallback).onResult(expectedInitial.data.map { it.toString() },
-                expectedInitial.prev)
+        verify(loadCallback).onResult(
+            expectedInitial.data.map { it.toString() },
+            expectedInitial.prev
+        )
         verifyNoMoreInteractions(loadCallback)
         // load before - error
         orig.enqueueError()
@@ -442,18 +459,18 @@
 
     @Test
     fun testListConverterWrappedDataSource() = verifyWrappedDataSource { dataSource ->
-        dataSource.mapByPage { page -> page.map { it.toString() } }
+        dataSource.mapByPage(Function { page -> page.map { it.toString() } })
     }
 
     @Test
     fun testItemConverterWrappedDataSource() = verifyWrappedDataSource { dataSource ->
-        dataSource.map { it.toString() }
+        dataSource.map(Function { it.toString() })
     }
 
     @Test
     fun testInvalidateToWrapper() {
         val orig = ItemDataSource()
-        val wrapper = orig.map { it.toString() }
+        val wrapper = orig.map<String>(Function { it.toString() })
 
         orig.invalidate()
         assertTrue(wrapper.isInvalid)
@@ -462,7 +479,7 @@
     @Test
     fun testInvalidateFromWrapper() {
         val orig = ItemDataSource()
-        val wrapper = orig.map { it.toString() }
+        val wrapper = orig.map<String>(Function { it.toString() })
 
         wrapper.invalidate()
         assertTrue(orig.isInvalid)
@@ -470,7 +487,7 @@
 
     companion object {
         // first load is 2nd page to ensure we test prepend as well as append behavior
-        private val INIT_KEY: String = "key 2"
+        private const val INIT_KEY: String = "key 2"
         private val PAGE_MAP: Map<String, Page>
         private val ITEM_LIST: List<Item>
         private val EXCEPTION = Exception()
@@ -496,8 +513,8 @@
     private fun drain() {
         var executed: Boolean
         do {
-            executed = mBackgroundThread.executeAll()
-            executed = mMainThread.executeAll() || executed
+            executed = backgroundThread.executeAll()
+            executed = mainThread.executeAll() || executed
         } while (executed)
     }
 }
diff --git a/paging/common/src/test/java/androidx/paging/PagedListConfigBuilderTest.kt b/paging/common/src/test/kotlin/androidx/paging/PagedListConfigBuilderTest.kt
similarity index 100%
rename from paging/common/src/test/java/androidx/paging/PagedListConfigBuilderTest.kt
rename to paging/common/src/test/kotlin/androidx/paging/PagedListConfigBuilderTest.kt
diff --git a/paging/common/ktx/src/test/java/PagedListConfigTest.kt b/paging/common/src/test/kotlin/androidx/paging/PagedListConfigTest.kt
similarity index 100%
rename from paging/common/ktx/src/test/java/PagedListConfigTest.kt
rename to paging/common/src/test/kotlin/androidx/paging/PagedListConfigTest.kt
diff --git a/paging/common/src/test/java/androidx/paging/PagedListTest.kt b/paging/common/src/test/kotlin/androidx/paging/PagedListTest.kt
similarity index 100%
rename from paging/common/src/test/java/androidx/paging/PagedListTest.kt
rename to paging/common/src/test/kotlin/androidx/paging/PagedListTest.kt
diff --git a/paging/common/src/test/java/androidx/paging/PagedStorageTest.kt b/paging/common/src/test/kotlin/androidx/paging/PagedStorageTest.kt
similarity index 100%
rename from paging/common/src/test/java/androidx/paging/PagedStorageTest.kt
rename to paging/common/src/test/kotlin/androidx/paging/PagedStorageTest.kt
diff --git a/paging/common/src/test/java/androidx/paging/PagerTest.kt b/paging/common/src/test/kotlin/androidx/paging/PagerTest.kt
similarity index 91%
rename from paging/common/src/test/java/androidx/paging/PagerTest.kt
rename to paging/common/src/test/kotlin/androidx/paging/PagerTest.kt
index 4e6dc4f..f4c3a26 100644
--- a/paging/common/src/test/java/androidx/paging/PagerTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/PagerTest.kt
@@ -62,8 +62,8 @@
             executor.execute {
 
                 val position = params.startPosition
-                val end = Math.min(position + params.loadSize, data.size)
-                future.set(RangeResult(position, end))
+                val end = minOf(position + params.loadSize, data.size)
+                future.set(rangeResult(position, end))
             }
 
             return future
@@ -72,10 +72,8 @@
 
     val data = List(9) { "$it" }
 
-    private fun RangeResult(start: Int, end: Int):
-            ListenablePositionalDataSource.RangeResult<String> {
-        return ListenablePositionalDataSource.RangeResult(data.subList(start, end))
-    }
+    private fun rangeResult(start: Int, end: Int) =
+        ListenablePositionalDataSource.RangeResult(data.subList(start, end))
 
     private data class Result(
         val type: PagedList.LoadType,
@@ -125,8 +123,8 @@
     private fun createPager(consumer: MockConsumer, start: Int = 0, end: Int = 10) = Pager(
         PagedList.Config(2, 2, true, 10, PagedList.Config.MAX_SIZE_UNBOUNDED),
         ImmediateListDataSource(data),
-        DirectExecutor.INSTANCE,
-        DirectExecutor.INSTANCE,
+        DirectExecutor,
+        DirectExecutor,
         consumer,
         null,
         ListenablePositionalDataSource.InitialResult(data.subList(start, end), start, data.size)
@@ -152,7 +150,7 @@
         testExecutor.executeAll()
 
         assertEquals(
-            listOf(Result(END, RangeResult(6, 8))),
+            listOf(Result(END, rangeResult(6, 8))),
             consumer.takeResults()
         )
         assertEquals(
@@ -178,7 +176,7 @@
         testExecutor.executeAll()
 
         assertEquals(
-            listOf(Result(START, RangeResult(2, 4))),
+            listOf(Result(START, rangeResult(2, 4))),
             consumer.takeResults()
         )
         assertEquals(
@@ -199,8 +197,8 @@
 
         assertEquals(
             listOf(
-                Result(END, RangeResult(6, 8)),
-                Result(END, RangeResult(8, 9))
+                Result(END, rangeResult(6, 8)),
+                Result(END, rangeResult(8, 9))
             ), consumer.takeResults()
         )
         assertEquals(
@@ -225,8 +223,8 @@
 
         assertEquals(
             listOf(
-                Result(START, RangeResult(2, 4)),
-                Result(START, RangeResult(0, 2))
+                Result(START, rangeResult(2, 4)),
+                Result(START, rangeResult(0, 2))
             ), consumer.takeResults()
         )
         assertEquals(
diff --git a/paging/common/src/test/java/androidx/paging/PositionalDataSourceTest.kt b/paging/common/src/test/kotlin/androidx/paging/PositionalDataSourceTest.kt
similarity index 80%
rename from paging/common/src/test/java/androidx/paging/PositionalDataSourceTest.kt
rename to paging/common/src/test/kotlin/androidx/paging/PositionalDataSourceTest.kt
index 08cd3fe..6b7868a 100644
--- a/paging/common/src/test/java/androidx/paging/PositionalDataSourceTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/PositionalDataSourceTest.kt
@@ -16,6 +16,7 @@
 
 package androidx.paging
 
+import androidx.arch.core.util.Function
 import androidx.paging.futures.DirectExecutor
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertTrue
@@ -26,6 +27,7 @@
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.verifyNoMoreInteractions
+import java.util.concurrent.Executor
 
 @RunWith(JUnit4::class)
 class PositionalDataSourceTest {
@@ -43,54 +45,69 @@
 
     @Test
     fun computeInitialLoadPositionZero() {
-        assertEquals(0, computeInitialLoadPos(
+        assertEquals(
+            0, computeInitialLoadPos(
                 requestedStartPosition = 0,
                 requestedLoadSize = 30,
                 pageSize = 10,
-                totalCount = 100))
+                totalCount = 100
+            )
+        )
     }
 
     @Test
     fun computeInitialLoadPositionRequestedPositionIncluded() {
-        assertEquals(10, computeInitialLoadPos(
+        assertEquals(
+            10, computeInitialLoadPos(
                 requestedStartPosition = 10,
                 requestedLoadSize = 10,
                 pageSize = 10,
-                totalCount = 100))
+                totalCount = 100
+            )
+        )
     }
 
     @Test
     fun computeInitialLoadPositionRound() {
-        assertEquals(10, computeInitialLoadPos(
+        assertEquals(
+            10, computeInitialLoadPos(
                 requestedStartPosition = 13,
                 requestedLoadSize = 30,
                 pageSize = 10,
-                totalCount = 100))
+                totalCount = 100
+            )
+        )
     }
 
     @Test
     fun computeInitialLoadPositionEndAdjusted() {
-        assertEquals(70, computeInitialLoadPos(
+        assertEquals(
+            70, computeInitialLoadPos(
                 requestedStartPosition = 99,
                 requestedLoadSize = 30,
                 pageSize = 10,
-                totalCount = 100))
+                totalCount = 100
+            )
+        )
     }
 
     @Test
     fun computeInitialLoadPositionEndAdjustedAndAligned() {
-        assertEquals(70, computeInitialLoadPos(
+        assertEquals(
+            70, computeInitialLoadPos(
                 requestedStartPosition = 99,
                 requestedLoadSize = 35,
                 pageSize = 10,
-                totalCount = 100))
+                totalCount = 100
+            )
+        )
     }
 
     private fun validatePositionOffset(enablePlaceholders: Boolean) {
         val config = PagedList.Config.Builder()
-                .setPageSize(10)
-                .setEnablePlaceholders(enablePlaceholders)
-                .build()
+            .setPageSize(10)
+            .setEnablePlaceholders(enablePlaceholders)
+            .build()
         val success = mutableListOf(false)
         val dataSource = object : PositionalDataSource<String>() {
             override fun loadInitial(
@@ -116,10 +133,10 @@
 
         @Suppress("DEPRECATION")
         PagedList.Builder(dataSource, config)
-                .setFetchExecutor { it.run() }
-                .setNotifyExecutor { it.run() }
-                .setInitialKey(36)
-                .build()
+            .setFetchExecutor(Executor { it.run() })
+            .setNotifyExecutor(Executor { it.run() })
+            .setInitialKey(36)
+            .build()
         assertTrue(success[0])
     }
 
@@ -156,14 +173,19 @@
         }
 
         val config = PagedList.Config.Builder()
-                .setPageSize(10)
-                .setEnablePlaceholders(enablePlaceholders)
-                .build()
+            .setPageSize(10)
+            .setEnablePlaceholders(enablePlaceholders)
+            .build()
 
-        dataSource.initExecutor(DirectExecutor.INSTANCE)
+        dataSource.initExecutor(DirectExecutor)
 
-        dataSource.loadInitial(PositionalDataSource.LoadInitialParams(
-            0, config.initialLoadSizeHint, config.pageSize, config.enablePlaceholders)).get()
+        val params = PositionalDataSource.LoadInitialParams(
+            0,
+            config.initialLoadSizeHint,
+            config.pageSize,
+            config.enablePlaceholders
+        )
+        dataSource.loadInitial(params).get()
     }
 
     @Test
@@ -239,8 +261,9 @@
         it.onResult(emptyList(), 0, 1)
     }
 
-    private abstract class WrapperDataSource<in A, B>(private val source: PositionalDataSource<A>)
-            : PositionalDataSource<B>() {
+    private abstract class WrapperDataSource<in A : Any, B : Any>(
+        private val source: PositionalDataSource<A>
+    ) : PositionalDataSource<B>() {
         override fun addInvalidatedCallback(onInvalidatedCallback: InvalidatedCallback) {
             source.addInvalidatedCallback(onInvalidatedCallback)
         }
@@ -249,13 +272,10 @@
             source.removeInvalidatedCallback(onInvalidatedCallback)
         }
 
-        override fun invalidate() {
-            source.invalidate()
-        }
+        override fun invalidate() = source.invalidate()
 
-        override fun isInvalid(): Boolean {
-            return source.isInvalid
-        }
+        override val isInvalid
+            get() = source.isInvalid
 
         override fun loadInitial(params: LoadInitialParams, callback: LoadInitialCallback<B>) {
             source.loadInitial(params, object : LoadInitialCallback<A>() {
@@ -288,20 +308,17 @@
         protected abstract fun convert(source: List<A>): List<B>
     }
 
-    private class StringWrapperDataSource<in A>(source: PositionalDataSource<A>)
-            : WrapperDataSource<A, String>(source) {
+    private class StringWrapperDataSource<in A : Any>(source: PositionalDataSource<A>) :
+        WrapperDataSource<A, String>(source) {
         override fun convert(source: List<A>): List<String> {
             return source.map { it.toString() }
         }
     }
 
-    class ListDataSource<T>(val list: List<T>) : PositionalDataSource<T>() {
+    class ListDataSource<T : Any>(val list: List<T>) : PositionalDataSource<T>() {
         private var error = false
 
-        override fun loadInitial(
-            params: PositionalDataSource.LoadInitialParams,
-            callback: PositionalDataSource.LoadInitialCallback<T>
-        ) {
+        override fun loadInitial(params: LoadInitialParams, callback: LoadInitialCallback<T>) {
             if (error) {
                 callback.onError(ERROR)
                 error = false
@@ -309,8 +326,8 @@
             }
             val totalCount = list.size
 
-            val position = PositionalDataSource.computeInitialLoadPosition(params, totalCount)
-            val loadSize = PositionalDataSource.computeInitialLoadSize(params, position, totalCount)
+            val position = computeInitialLoadPosition(params, totalCount)
+            val loadSize = computeInitialLoadSize(params, position, totalCount)
 
             // for simplicity, we could return everything immediately,
             // but we tile here since it's expected behavior
@@ -318,20 +335,14 @@
             callback.onResult(sublist, position, totalCount)
         }
 
-        override fun loadRange(
-            params: PositionalDataSource.LoadRangeParams,
-            callback: PositionalDataSource.LoadRangeCallback<T>
-        ) {
+        override fun loadRange(params: LoadRangeParams, callback: LoadRangeCallback<T>) {
             if (error) {
                 callback.onError(ERROR)
                 error = false
                 return
             }
             callback.onResult(
-                list.subList(
-                    params.startPosition,
-                    params.startPosition + params.loadSize
-                )
+                list.subList(params.startPosition, params.startPosition + params.loadSize)
             )
         }
 
@@ -372,11 +383,11 @@
         verifyNoMoreInteractions(loadRangeCallback)
 
         // check invalidation behavior
-        val invalCallback = mock(DataSource.InvalidatedCallback::class.java)
-        wrapper.addInvalidatedCallback(invalCallback)
+        val invalidCallback = mock(DataSource.InvalidatedCallback::class.java)
+        wrapper.addInvalidatedCallback(invalidCallback)
         orig.invalidate()
-        verify(invalCallback).onInvalidated()
-        verifyNoMoreInteractions(invalCallback)
+        verify(invalidCallback).onInvalidated()
+        verifyNoMoreInteractions(invalidCallback)
 
         // verify invalidation
         orig.invalidate()
@@ -390,26 +401,18 @@
 
     @Test
     fun testListConverterWrappedDataSource() = verifyWrappedDataSource { dataSource ->
-        dataSource.mapByPage { page -> page.map { it.toString() } }
+        dataSource.mapByPage(Function { page -> page.map { it.toString() } })
     }
 
     @Test
     fun testItemConverterWrappedDataSource() = verifyWrappedDataSource { dataSource ->
-        dataSource.map { it.toString() }
-    }
-
-    @Test
-    fun testGetKey() {
-        val source = ListDataSource(listOf("a", "b"))
-        assertEquals(null, source.getKey("a"))
-        assertEquals(1, source.getKey(1, "a"))
-        assertEquals(1, source.getKey(1, null))
+        dataSource.map(Function { it.toString() })
     }
 
     @Test
     fun testInvalidateToWrapper() {
         val orig = ListDataSource(listOf(0, 1, 2))
-        val wrapper = orig.map { it.toString() }
+        val wrapper = orig.map<String>(Function { it.toString() })
 
         orig.invalidate()
         assertTrue(wrapper.isInvalid)
@@ -418,7 +421,7 @@
     @Test
     fun testInvalidateFromWrapper() {
         val orig = ListDataSource(listOf(0, 1, 2))
-        val wrapper = orig.map { it.toString() }
+        val wrapper = orig.map<String>(Function { it.toString() })
 
         wrapper.invalidate()
         assertTrue(orig.isInvalid)
diff --git a/paging/common/src/test/java/androidx/paging/futures/FuturesTest.kt b/paging/common/src/test/kotlin/androidx/paging/futures/FuturesTest.kt
similarity index 89%
rename from paging/common/src/test/java/androidx/paging/futures/FuturesTest.kt
rename to paging/common/src/test/kotlin/androidx/paging/futures/FuturesTest.kt
index 9301bd0..d740578 100644
--- a/paging/common/src/test/java/androidx/paging/futures/FuturesTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/futures/FuturesTest.kt
@@ -16,8 +16,8 @@
 
 package androidx.paging.futures
 
+import androidx.arch.core.util.Function
 import androidx.concurrent.futures.ResolvableFuture
-import com.google.common.util.concurrent.Futures
 import com.google.common.util.concurrent.ListenableFuture
 import com.google.common.util.concurrent.SettableFuture
 import org.junit.Assert.assertEquals
@@ -35,19 +35,19 @@
         if (type == guava) {
             val future = SettableFuture.create<String>()
 
-            val wrapper = Futures.transform(
-                future,
-                com.google.common.base.Function<String, String> { it -> it },
-                DirectExecutor.INSTANCE)
+            val wrapper = future.transform(
+                Function<String, String> { it },
+                DirectExecutor
+            )
 
             tester(future, wrapper)
         } else {
             val future = ResolvableFuture.create<String>()
 
-            val wrapper = androidx.paging.futures.Futures.transform(
-                future,
-                androidx.arch.core.util.Function<String, String> { it -> it },
-                DirectExecutor.INSTANCE)
+            val wrapper = future.transform(
+                Function<String, String> { it },
+                DirectExecutor
+            )
             tester(future, wrapper)
         }
     }
diff --git a/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/custom/ItemDataSource.kt b/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/custom/ItemDataSource.kt
index e10301a..cc831a3 100644
--- a/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/custom/ItemDataSource.kt
+++ b/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/custom/ItemDataSource.kt
@@ -60,8 +60,8 @@
     }
 
     override fun loadInitial(params: LoadInitialParams, callback: LoadInitialCallback<Item>) {
-        val position = PositionalDataSource.computeInitialLoadPosition(params, COUNT)
-        val loadSize = PositionalDataSource.computeInitialLoadSize(params, position, COUNT)
+        val position = computeInitialLoadPosition(params, COUNT)
+        val loadSize = computeInitialLoadSize(params, position, COUNT)
         val data = loadRangeInternal(position, loadSize)
         if (data == null) {
             callback.onError(RetryableItemError())
diff --git a/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/custom/PagedListSampleActivity.kt b/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/custom/PagedListSampleActivity.kt
index 474ac25..677e2f8 100644
--- a/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/custom/PagedListSampleActivity.kt
+++ b/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/custom/PagedListSampleActivity.kt
@@ -69,34 +69,40 @@
             adapter.currentList?.retry()
         }
 
-        adapter.addLoadStateListener { type, state, _ ->
-            val button = when (type) {
-                PagedList.LoadType.REFRESH -> buttonRefresh
-                PagedList.LoadType.START -> buttonStart
-                PagedList.LoadType.END -> buttonEnd
-            }
-            when (state) {
-                PagedList.LoadState.IDLE -> {
-                    button.text = "Idle"
-                    button.isEnabled = type == PagedList.LoadType.REFRESH
+        adapter.addLoadStateListener(object : PagedList.LoadStateListener {
+            override fun onLoadStateChanged(
+                type: PagedList.LoadType,
+                state: PagedList.LoadState,
+                error: Throwable?
+            ) {
+                val button = when (type) {
+                    PagedList.LoadType.REFRESH -> buttonRefresh
+                    PagedList.LoadType.START -> buttonStart
+                    PagedList.LoadType.END -> buttonEnd
                 }
-                PagedList.LoadState.LOADING -> {
-                    button.text = "Loading"
-                    button.isEnabled = false
-                }
-                PagedList.LoadState.DONE -> {
-                    button.text = "Done"
-                    button.isEnabled = false
-                }
-                PagedList.LoadState.ERROR -> {
-                    button.text = "Error"
-                    button.isEnabled = false
-                }
-                PagedList.LoadState.RETRYABLE_ERROR -> {
-                    button.text = "Error"
-                    button.isEnabled = true
+                when (state) {
+                    PagedList.LoadState.IDLE -> {
+                        button.text = "Idle"
+                        button.isEnabled = type == PagedList.LoadType.REFRESH
+                    }
+                    PagedList.LoadState.LOADING -> {
+                        button.text = "Loading"
+                        button.isEnabled = false
+                    }
+                    PagedList.LoadState.DONE -> {
+                        button.text = "Done"
+                        button.isEnabled = false
+                    }
+                    PagedList.LoadState.ERROR -> {
+                        button.text = "Error"
+                        button.isEnabled = false
+                    }
+                    PagedList.LoadState.RETRYABLE_ERROR -> {
+                        button.text = "Error"
+                        button.isEnabled = true
+                    }
                 }
             }
-        }
+        })
     }
 }
diff --git a/paging/runtime/ktx/src/main/java/androidx/paging/LivePagedList.kt b/paging/runtime/ktx/src/main/java/androidx/paging/LivePagedList.kt
index 07e0bac..52343f4 100644
--- a/paging/runtime/ktx/src/main/java/androidx/paging/LivePagedList.kt
+++ b/paging/runtime/ktx/src/main/java/androidx/paging/LivePagedList.kt
@@ -34,17 +34,17 @@
  *
  * @see LivePagedListBuilder
  */
-fun <Key, Value> DataSource.Factory<Key, Value>.toLiveData(
+fun <Key : Any, Value : Any> DataSource.Factory<Key, Value>.toLiveData(
     config: PagedList.Config,
     initialLoadKey: Key? = null,
     boundaryCallback: PagedList.BoundaryCallback<Value>? = null,
     fetchExecutor: Executor = ArchTaskExecutor.getIOThreadExecutor()
 ): LiveData<PagedList<Value>> {
     return LivePagedListBuilder(this, config)
-            .setInitialLoadKey(initialLoadKey)
-            .setBoundaryCallback(boundaryCallback)
-            .setFetchExecutor(fetchExecutor)
-            .build()
+        .setInitialLoadKey(initialLoadKey)
+        .setBoundaryCallback(boundaryCallback)
+        .setFetchExecutor(fetchExecutor)
+        .build()
 }
 
 /**
@@ -61,15 +61,15 @@
  *
  * @see LivePagedListBuilder
  */
-fun <Key, Value> DataSource.Factory<Key, Value>.toLiveData(
+fun <Key : Any, Value : Any> DataSource.Factory<Key, Value>.toLiveData(
     pageSize: Int,
     initialLoadKey: Key? = null,
     boundaryCallback: PagedList.BoundaryCallback<Value>? = null,
     fetchExecutor: Executor = ArchTaskExecutor.getIOThreadExecutor()
 ): LiveData<PagedList<Value>> {
     return LivePagedListBuilder(this, Config(pageSize))
-            .setInitialLoadKey(initialLoadKey)
-            .setBoundaryCallback(boundaryCallback)
-            .setFetchExecutor(fetchExecutor)
-            .build()
+        .setInitialLoadKey(initialLoadKey)
+        .setBoundaryCallback(boundaryCallback)
+        .setFetchExecutor(fetchExecutor)
+        .build()
 }
diff --git a/paging/runtime/src/androidTest/java/androidx/paging/AsyncPagedListDifferTest.kt b/paging/runtime/src/androidTest/java/androidx/paging/AsyncPagedListDifferTest.kt
index 156334e..416934f 100644
--- a/paging/runtime/src/androidTest/java/androidx/paging/AsyncPagedListDifferTest.kt
+++ b/paging/runtime/src/androidTest/java/androidx/paging/AsyncPagedListDifferTest.kt
@@ -59,13 +59,13 @@
         return differ
     }
 
-    private fun <V> createPagedListFromListAndPos(
+    private fun <V : Any> createPagedListFromListAndPos(
         config: PagedList.Config,
         data: List<V>,
         initialKey: Int
     ): PagedList<V> {
         @Suppress("DEPRECATION")
-        return PagedList.Builder<Int, V>(ListDataSource(data), config)
+        return PagedList.Builder(ListDataSource(data), config)
             .setInitialKey(initialKey)
             .setNotifyExecutor(mMainThread)
             .setFetchExecutor(mPageLoadingThread)
diff --git a/paging/runtime/src/androidTest/java/androidx/paging/LivePagedListBuilderTest.kt b/paging/runtime/src/androidTest/java/androidx/paging/LivePagedListBuilderTest.kt
index 28aa2ba..56ec88c 100644
--- a/paging/runtime/src/androidTest/java/androidx/paging/LivePagedListBuilderTest.kt
+++ b/paging/runtime/src/androidTest/java/androidx/paging/LivePagedListBuilderTest.kt
@@ -181,9 +181,15 @@
         assertNotNull(initPagedList!!)
         assertTrue(initPagedList is InitialPagedList<*, *>)
 
-        val loadStateListener = PagedList.LoadStateListener { type, state, error ->
-            if (type == REFRESH) {
-                loadStates.add(LoadState(type, state, error))
+        val loadStateListener = object : PagedList.LoadStateListener {
+            override fun onLoadStateChanged(
+                type: PagedList.LoadType,
+                state: PagedList.LoadState,
+                error: Throwable?
+            ) {
+                if (type == REFRESH) {
+                    loadStates.add(LoadState(type, state, error))
+                }
             }
         }
         initPagedList.addWeakLoadStateListener(loadStateListener)
diff --git a/paging/runtime/src/androidTest/java/androidx/paging/StringPagedList.kt b/paging/runtime/src/androidTest/java/androidx/paging/StringPagedList.kt
index 2e85715..36e69c0 100644
--- a/paging/runtime/src/androidTest/java/androidx/paging/StringPagedList.kt
+++ b/paging/runtime/src/androidTest/java/androidx/paging/StringPagedList.kt
@@ -23,7 +23,7 @@
     trailingNulls: Int,
     vararg items: String
 ) : PagedList<String>(
-    PagedStorage<String>(),
+    PagedStorage(),
     TestExecutor(),
     TestExecutor(),
     null,
@@ -33,8 +33,7 @@
     var detached = false
 
     init {
-        @Suppress("UNCHECKED_CAST")
-        val keyedStorage = mStorage as PagedStorage<String>
+        val keyedStorage = getStorage()
         keyedStorage.init(
             leadingNulls,
             list,
@@ -44,23 +43,20 @@
         )
     }
 
-    internal override fun isContiguous(): Boolean = true
+    override val isContiguous = true
 
-    override fun getLastKey(): Any? = null
+    override val lastKey: Any? = null
 
-    override fun isDetached(): Boolean = detached
+    override val isDetached
+        get() = detached
 
     override fun detach() {
         detached = true
     }
 
-    override fun dispatchUpdatesSinceSnapshot(
-        storageSnapshot: PagedList<String>,
-        callback: Callback
-    ) {
-    }
+    override fun dispatchUpdatesSinceSnapshot(snapshot: PagedList<String>, callback: Callback) {}
 
-    override fun dispatchCurrentLoadState(listener: LoadStateListener?) {}
+    override fun dispatchCurrentLoadState(listener: LoadStateListener) {}
 
     override fun loadAroundInternal(index: Int) {}
 
@@ -74,15 +70,10 @@
 
     override fun onPageInserted(start: Int, count: Int) {}
 
-    override fun getDataSource(): DataSource<*, String> {
-        return ListDataSource<String>(list)
-    }
+    override val dataSource = ListDataSource(list)
 
-    override fun onPagesRemoved(startOfDrops: Int, count: Int) {
-        notifyRemoved(startOfDrops, count)
-    }
+    override fun onPagesRemoved(startOfDrops: Int, count: Int) = notifyRemoved(startOfDrops, count)
 
-    override fun onPagesSwappedToPlaceholder(startOfDrops: Int, count: Int) {
+    override fun onPagesSwappedToPlaceholder(startOfDrops: Int, count: Int) =
         notifyChanged(startOfDrops, count)
-    }
 }
diff --git a/paging/runtime/src/main/java/androidx/paging/AsyncPagedListDiffer.java b/paging/runtime/src/main/java/androidx/paging/AsyncPagedListDiffer.java
index 0a02746..2c2e6f2 100644
--- a/paging/runtime/src/main/java/androidx/paging/AsyncPagedListDiffer.java
+++ b/paging/runtime/src/main/java/androidx/paging/AsyncPagedListDiffer.java
@@ -31,7 +31,7 @@
 import java.util.concurrent.Executor;
 
 /**
- * Helper object for mapping a {@link PagedList} into a
+ * Helper object for mapping a {@link androidx.paging.PagedList} into a
  * {@link androidx.recyclerview.widget.RecyclerView.Adapter RecyclerView.Adapter}.
  * <p>
  * For simplicity, the {@link PagedListAdapter} wrapper class can often be used instead of the
@@ -39,9 +39,9 @@
  * base class to support paging isn't convenient.
  * <p>
  * When consuming a {@link LiveData} of PagedList, you can observe updates and dispatch them
- * directly to {@link #submitList(PagedList)}. The AsyncPagedListDiffer then can present this
- * updating data set simply for an adapter. It listens to PagedList loading callbacks, and uses
- * DiffUtil on a background thread to compute updates as new PagedLists are received.
+ * directly to {@link #submitList(androidx.paging.PagedList)}. The AsyncPagedListDiffer then can
+ * present this updating data set simply for an adapter. It listens to PagedList loading callbacks,
+ * and uses DiffUtil on a background thread to compute updates as new PagedLists are received.
  * <p>
  * It provides a simple list-like API with {@link #getItem(int)} and {@link #getItemCount()} for an
  * adapter to acquire and present data objects.
@@ -135,7 +135,7 @@
          * Called after the current PagedList has been updated.
          *
          * @param previousList The previous list, may be null.
-         * @param currentList The new current list, may be null.
+         * @param currentList  The new current list, may be null.
          */
         void onCurrentListChanged(
                 @Nullable PagedList<T> previousList, @Nullable PagedList<T> currentList);
@@ -152,7 +152,9 @@
     @SuppressWarnings("WeakerAccess") /* synthetic access */
     int mMaxScheduledGeneration;
 
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    // Intentional higher visibility for synthetic access.
+    // LoadStateManager is marked internal, but we really intend it to be package-private.
+    @SuppressWarnings({"WeakerAccess", "KotlinInternalInJava"})
     final PagedList.LoadStateManager mLoadStateManager = new PagedList.LoadStateManager() {
         @Override
         protected void onStateChanged(@NonNull PagedList.LoadType type,
@@ -164,7 +166,7 @@
         }
     };
     @SuppressWarnings("WeakerAccess") // synthetic access
-    PagedList.LoadStateListener mLoadStateListener = new PagedList.LoadStateListener() {
+            PagedList.LoadStateListener mLoadStateListener = new PagedList.LoadStateListener() {
         @Override
         public void onLoadStateChanged(@NonNull PagedList.LoadType type,
                 @NonNull PagedList.LoadState state, @Nullable Throwable error) {
@@ -180,9 +182,9 @@
      * Convenience for {@code AsyncPagedListDiffer(new AdapterListUpdateCallback(adapter),
      * new AsyncDifferConfig.Builder<T>(diffCallback).build();}
      *
-     * @param adapter Adapter that will receive update signals.
+     * @param adapter      Adapter that will receive update signals.
      * @param diffCallback The {@link DiffUtil.ItemCallback DiffUtil.ItemCallback} instance to
-     * compare items in the list.
+     *                     compare items in the list.
      */
     @SuppressWarnings("WeakerAccess")
     public AsyncPagedListDiffer(@NonNull RecyclerView.Adapter adapter,
@@ -281,7 +283,7 @@
      * may not be executed. If PagedList B is submitted immediately after PagedList A, and is
      * committed directly, the callback associated with PagedList A will not be run.
      *
-     * @param pagedList The new PagedList.
+     * @param pagedList      The new PagedList.
      * @param commitCallback Optional runnable that is executed when the PagedList is committed, if
      *                       it is committed.
      */
@@ -359,18 +361,19 @@
         mConfig.getBackgroundThreadExecutor().execute(new Runnable() {
             @Override
             public void run() {
-                final DiffUtil.DiffResult result;
-                result = PagedStorageDiffHelper.computeDiff(
-                        oldSnapshot.mStorage,
-                        newSnapshot.mStorage,
+                //noinspection KotlinInternalInJava
+                final DiffUtil.DiffResult result = PagedStorageDiffHelper.computeDiff(
+                        oldSnapshot.getStorage$paging_common(),
+                        newSnapshot.getStorage$paging_common(),
                         mConfig.getDiffCallback());
 
                 mMainThreadExecutor.execute(new Runnable() {
                     @Override
                     public void run() {
                         if (mMaxScheduledGeneration == runGeneration) {
+                            //noinspection KotlinInternalInJava
                             latchPagedList(pagedList, newSnapshot, result,
-                                    oldSnapshot.mLastLoad, commitCallback);
+                                    oldSnapshot.getLastLoad$paging_common(), commitCallback);
                         }
                     }
                 });
@@ -395,8 +398,10 @@
         mSnapshot = null;
 
         // dispatch update callback after updating mPagedList/mSnapshot
+        //noinspection KotlinInternalInJava
         PagedStorageDiffHelper.dispatchDiff(mUpdateCallback,
-                previousSnapshot.mStorage, newList.mStorage, diffResult);
+                previousSnapshot.getStorage$paging_common(), newList.getStorage$paging_common(),
+                diffResult);
 
         newList.addWeakCallback(diffSnapshot, mPagedListCallback);
 
@@ -407,8 +412,10 @@
             // Note: we don't take into account loads between new list snapshot and new list, but
             // this is only a problem in rare cases when placeholders are disabled, and a load
             // starts (for some reason) and finishes before diff completes.
-            int newPosition = PagedStorageDiffHelper.transformAnchorIndex(
-                    diffResult, previousSnapshot.mStorage, diffSnapshot.mStorage, lastAccessIndex);
+            @SuppressWarnings("KotlinInternalInJava") int newPosition =
+                    PagedStorageDiffHelper.transformAnchorIndex(
+                            diffResult, previousSnapshot.getStorage$paging_common(),
+                            diffSnapshot.getStorage$paging_common(), lastAccessIndex);
 
             // Trigger load in new list at this position, clamped to list bounds.
             // This is a load, not just an update of last load position, since the new list may be
@@ -436,7 +443,6 @@
      * Add a PagedListListener to receive updates when the current PagedList changes.
      *
      * @param listener Listener to receive updates.
-     *
      * @see #getCurrentList()
      * @see #removePagedListListener(PagedListListener)
      */
@@ -462,7 +468,6 @@
      * current REFRESH, START, and END states.
      *
      * @param listener Listener to receive updates.
-     *
      * @see #removeLoadStateListListener(PagedList.LoadStateListener)
      */
     public void addLoadStateListener(@NonNull PagedList.LoadStateListener listener) {
diff --git a/paging/runtime/src/main/java/androidx/paging/LivePagedList.java b/paging/runtime/src/main/java/androidx/paging/LivePagedList.java
index a4b04ca..dfdfbef 100644
--- a/paging/runtime/src/main/java/androidx/paging/LivePagedList.java
+++ b/paging/runtime/src/main/java/androidx/paging/LivePagedList.java
@@ -98,9 +98,11 @@
         mCurrentData.getDataSource().removeInvalidatedCallback(mCallback);
         dataSource.addInvalidatedCallback(mCallback);
 
-        mCurrentData.setInitialLoadState(PagedList.LoadState.LOADING, null);
+        //noinspection KotlinInternalInJava
+        mCurrentData.setInitialLoadState$paging_common(PagedList.LoadState.LOADING, null);
 
-        return PagedList.create(
+        //noinspection KotlinInternalInJava
+        return PagedList.create$paging_common(
                 dataSource,
                 mNotifyExecutor,
                 mFetchExecutor,
@@ -116,7 +118,8 @@
                 .isRetryableError(throwable)
                 ? PagedList.LoadState.RETRYABLE_ERROR
                 : PagedList.LoadState.ERROR;
-        mCurrentData.setInitialLoadState(loadState, throwable);
+        //noinspection KotlinInternalInJava
+        mCurrentData.setInitialLoadState$paging_common(loadState, throwable);
     }
 
     @Override
diff --git a/paging/runtime/src/main/java/androidx/paging/LivePagedListBuilder.java b/paging/runtime/src/main/java/androidx/paging/LivePagedListBuilder.java
index d156143..9fbcdb0 100644
--- a/paging/runtime/src/main/java/androidx/paging/LivePagedListBuilder.java
+++ b/paging/runtime/src/main/java/androidx/paging/LivePagedListBuilder.java
@@ -27,14 +27,14 @@
 import java.util.concurrent.Executor;
 
 /**
- * Builder for {@code LiveData<PagedList>}, given a {@link DataSource.Factory} and a
- * {@link PagedList.Config}.
+ * Builder for {@code LiveData<PagedList>}, given a {@link androidx.paging.DataSource.Factory} and a
+ * {@link androidx.paging.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).
  *
  * @param <Key> Type of input valued used to load data from the DataSource. Must be integer if
- *             you're using PositionalDataSource.
+ *              you're using PositionalDataSource.
  * @param <Value> Item type being presented.
  */
 public final class LivePagedListBuilder<Key, Value> {
@@ -99,8 +99,8 @@
     }
 
     /**
-     * Sets a {@link PagedList.BoundaryCallback} on each PagedList created, typically used to load
-     * additional data from network when paging from local storage.
+     * Sets a {@link androidx.paging.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
diff --git a/paging/runtime/src/main/java/androidx/paging/PagedListAdapter.java b/paging/runtime/src/main/java/androidx/paging/PagedListAdapter.java
index 5b28d8a..9877ffb 100644
--- a/paging/runtime/src/main/java/androidx/paging/PagedListAdapter.java
+++ b/paging/runtime/src/main/java/androidx/paging/PagedListAdapter.java
@@ -27,7 +27,7 @@
 
 /**
  * {@link RecyclerView.Adapter RecyclerView.Adapter} base class for presenting paged data from
- * {@link PagedList}s in a {@link RecyclerView}.
+ * {@link androidx.paging.PagedList}s in a {@link RecyclerView}.
  * <p>
  * This class is a convenience wrapper around {@link AsyncPagedListDiffer} that implements common
  * default behavior for item counting, and listening to PagedList update callbacks.
diff --git a/paging/rxjava2/ktx/src/androidTest/java/androidx/paging/RxPagedListTest.kt b/paging/rxjava2/ktx/src/androidTest/java/androidx/paging/RxPagedListTest.kt
index 2a07c7a..3fa1a22 100644
--- a/paging/rxjava2/ktx/src/androidTest/java/androidx/paging/RxPagedListTest.kt
+++ b/paging/rxjava2/ktx/src/androidTest/java/androidx/paging/RxPagedListTest.kt
@@ -70,7 +70,7 @@
                 params: LoadInitialParams,
                 callback: LoadInitialCallback<String>
             ) {
-                callback.onResult(listOf<String>(), 0, 0)
+                callback.onResult(listOf(), 0, 0)
             }
 
             override fun loadRange(params: LoadRangeParams, callback: LoadRangeCallback<String>) {
diff --git a/paging/rxjava2/ktx/src/main/java/androidx/paging/RxPagedList.kt b/paging/rxjava2/ktx/src/main/java/androidx/paging/RxPagedList.kt
index 29378f3..539fda5 100644
--- a/paging/rxjava2/ktx/src/main/java/androidx/paging/RxPagedList.kt
+++ b/paging/rxjava2/ktx/src/main/java/androidx/paging/RxPagedList.kt
@@ -21,7 +21,7 @@
 import io.reactivex.Observable
 import io.reactivex.Scheduler
 
-private fun <Key, Value> createRxPagedListBuilder(
+private fun <Key : Any, Value : Any> createRxPagedListBuilder(
     dataSourceFactory: DataSource.Factory<Key, Value>,
     config: PagedList.Config,
     initialLoadKey: Key?,
@@ -30,8 +30,8 @@
     notifyScheduler: Scheduler?
 ): RxPagedListBuilder<Key, Value> {
     val builder = RxPagedListBuilder(dataSourceFactory, config)
-            .setInitialLoadKey(initialLoadKey)
-            .setBoundaryCallback(boundaryCallback)
+        .setInitialLoadKey(initialLoadKey)
+        .setBoundaryCallback(boundaryCallback)
     if (fetchScheduler != null) builder.setFetchScheduler(fetchScheduler)
     if (notifyScheduler != null) builder.setNotifyScheduler(notifyScheduler)
     return builder
@@ -56,7 +56,7 @@
  * @see RxPagedListBuilder
  * @see toFlowable
  */
-fun <Key, Value> DataSource.Factory<Key, Value>.toObservable(
+fun <Key : Any, Value : Any> DataSource.Factory<Key, Value>.toObservable(
     config: PagedList.Config,
     initialLoadKey: Key? = null,
     boundaryCallback: PagedList.BoundaryCallback<Value>? = null,
@@ -64,12 +64,13 @@
     notifyScheduler: Scheduler? = null
 ): Observable<PagedList<Value>> {
     return createRxPagedListBuilder(
-            dataSourceFactory = this,
-            config = config,
-            initialLoadKey = initialLoadKey,
-            boundaryCallback = boundaryCallback,
-            fetchScheduler = fetchScheduler,
-            notifyScheduler = notifyScheduler).buildObservable()
+        dataSourceFactory = this,
+        config = config,
+        initialLoadKey = initialLoadKey,
+        boundaryCallback = boundaryCallback,
+        fetchScheduler = fetchScheduler,
+        notifyScheduler = notifyScheduler
+    ).buildObservable()
 }
 
 /**
@@ -91,7 +92,7 @@
  * @see RxPagedListBuilder
  * @see toFlowable
  */
-fun <Key, Value> DataSource.Factory<Key, Value>.toObservable(
+fun <Key : Any, Value : Any> DataSource.Factory<Key, Value>.toObservable(
     pageSize: Int,
     initialLoadKey: Key? = null,
     boundaryCallback: PagedList.BoundaryCallback<Value>? = null,
@@ -99,12 +100,13 @@
     notifyScheduler: Scheduler? = null
 ): Observable<PagedList<Value>> {
     return createRxPagedListBuilder(
-            dataSourceFactory = this,
-            config = Config(pageSize),
-            initialLoadKey = initialLoadKey,
-            boundaryCallback = boundaryCallback,
-            fetchScheduler = fetchScheduler,
-            notifyScheduler = notifyScheduler).buildObservable()
+        dataSourceFactory = this,
+        config = Config(pageSize),
+        initialLoadKey = initialLoadKey,
+        boundaryCallback = boundaryCallback,
+        fetchScheduler = fetchScheduler,
+        notifyScheduler = notifyScheduler
+    ).buildObservable()
 }
 
 /**
@@ -127,7 +129,7 @@
  * @see RxPagedListBuilder
  * @see toObservable
  */
-fun <Key, Value> DataSource.Factory<Key, Value>.toFlowable(
+fun <Key : Any, Value : Any> DataSource.Factory<Key, Value>.toFlowable(
     config: PagedList.Config,
     initialLoadKey: Key? = null,
     boundaryCallback: PagedList.BoundaryCallback<Value>? = null,
@@ -136,12 +138,13 @@
     backpressureStrategy: BackpressureStrategy = BackpressureStrategy.LATEST
 ): Flowable<PagedList<Value>> {
     return createRxPagedListBuilder(
-            dataSourceFactory = this,
-            config = config,
-            initialLoadKey = initialLoadKey,
-            boundaryCallback = boundaryCallback,
-            fetchScheduler = fetchScheduler,
-            notifyScheduler = notifyScheduler).buildFlowable(backpressureStrategy)
+        dataSourceFactory = this,
+        config = config,
+        initialLoadKey = initialLoadKey,
+        boundaryCallback = boundaryCallback,
+        fetchScheduler = fetchScheduler,
+        notifyScheduler = notifyScheduler
+    ).buildFlowable(backpressureStrategy)
 }
 
 /**
@@ -164,7 +167,7 @@
  * @see RxPagedListBuilder
  * @see toObservable
  */
-fun <Key, Value> DataSource.Factory<Key, Value>.toFlowable(
+fun <Key : Any, Value : Any> DataSource.Factory<Key, Value>.toFlowable(
     pageSize: Int,
     initialLoadKey: Key? = null,
     boundaryCallback: PagedList.BoundaryCallback<Value>? = null,
@@ -173,10 +176,11 @@
     backpressureStrategy: BackpressureStrategy = BackpressureStrategy.LATEST
 ): Flowable<PagedList<Value>> {
     return createRxPagedListBuilder(
-            dataSourceFactory = this,
-            config = Config(pageSize),
-            initialLoadKey = initialLoadKey,
-            boundaryCallback = boundaryCallback,
-            fetchScheduler = fetchScheduler,
-            notifyScheduler = notifyScheduler).buildFlowable(backpressureStrategy)
+        dataSourceFactory = this,
+        config = Config(pageSize),
+        initialLoadKey = initialLoadKey,
+        boundaryCallback = boundaryCallback,
+        fetchScheduler = fetchScheduler,
+        notifyScheduler = notifyScheduler
+    ).buildFlowable(backpressureStrategy)
 }
diff --git a/paging/rxjava2/src/main/java/androidx/paging/RxPagedListBuilder.java b/paging/rxjava2/src/main/java/androidx/paging/RxPagedListBuilder.java
index a070466..271a56d 100644
--- a/paging/rxjava2/src/main/java/androidx/paging/RxPagedListBuilder.java
+++ b/paging/rxjava2/src/main/java/androidx/paging/RxPagedListBuilder.java
@@ -33,7 +33,7 @@
 
 /**
  * Builder for {@code Observable<PagedList>} or {@code Flowable<PagedList>}, given a
- * {@link DataSource.Factory} and a {@link PagedList.Config}.
+ * {@link androidx.paging.DataSource.Factory} and a {@link androidx.paging.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).
@@ -90,8 +90,10 @@
      * @param pageSize          Size of pages to load.
      */
     @SuppressWarnings("unused")
-    public RxPagedListBuilder(@NonNull DataSource.Factory<Key, Value> dataSourceFactory,
-            int pageSize) {
+    public RxPagedListBuilder(
+            @NonNull DataSource.Factory<Key, Value> dataSourceFactory,
+            int pageSize
+    ) {
         this(dataSourceFactory, new PagedList.Config.Builder().setPageSize(pageSize).build());
     }
 
@@ -112,8 +114,8 @@
     }
 
     /**
-     * Sets a {@link PagedList.BoundaryCallback} on each PagedList created, typically used to load
-     * additional data from network when paging from local storage.
+     * Sets a {@link androidx.paging.PagedList.BoundaryCallback} on each PagedList created,
+     * typically used to load additional data from network when paging from local storage.
      *
      * 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
@@ -149,8 +151,8 @@
      * receiving PagedLists will also receive the internal updates to the PagedList.
      *
      * @param scheduler Scheduler that receives PagedList updates, and where
-     *                  {@link PagedList.Callback} calls are dispatched. Generally, this is the
-     *                  UI/main thread.
+     *                  {@link androidx.paging.PagedList.Callback} calls are dispatched. Generally,
+     *                  this is the UI/main thread.
      * @return this
      */
     @NonNull