Converting `paging` related files in `room-runtime` from Java to Kotlin.

Test: LimitOffsetDataSourceTest.java
Bug: 206859668
Relnote: Converting `paging` related files in `room-runtime` from Java to Kotlin.
Change-Id: I82fc81469540315dbe4865ccde68396a4339dfd9
diff --git a/room/room-runtime/api/restricted_current.ignore b/room/room-runtime/api/restricted_current.ignore
index e6a9bf9..a566a8f 100644
--- a/room/room-runtime/api/restricted_current.ignore
+++ b/room/room-runtime/api/restricted_current.ignore
@@ -1,3 +1,7 @@
 // Baseline format: 1.0
+ChangedType: androidx.room.paging.LimitOffsetDataSource#convertRows(android.database.Cursor):
+    Method androidx.room.paging.LimitOffsetDataSource.convertRows has changed return type from java.util.List<T!> to java.util.List<T>
+
+
 RemovedClass: androidx.room.util.CopyLock:
     Removed class androidx.room.util.CopyLock
diff --git a/room/room-runtime/api/restricted_current.txt b/room/room-runtime/api/restricted_current.txt
index 8f29006..24bc4fb 100644
--- a/room/room-runtime/api/restricted_current.txt
+++ b/room/room-runtime/api/restricted_current.txt
@@ -236,13 +236,14 @@
 package androidx.room.paging {
 
   @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public abstract class LimitOffsetDataSource<T> extends androidx.paging.PositionalDataSource<T> {
-    ctor protected LimitOffsetDataSource(androidx.room.RoomDatabase, androidx.sqlite.db.SupportSQLiteQuery, boolean, java.lang.String!...);
-    ctor protected LimitOffsetDataSource(androidx.room.RoomDatabase, androidx.sqlite.db.SupportSQLiteQuery, boolean, boolean, java.lang.String!...);
-    ctor protected LimitOffsetDataSource(androidx.room.RoomDatabase, androidx.room.RoomSQLiteQuery, boolean, java.lang.String!...);
-    ctor protected LimitOffsetDataSource(androidx.room.RoomDatabase, androidx.room.RoomSQLiteQuery, boolean, boolean, java.lang.String!...);
-    method protected abstract java.util.List<T!> convertRows(android.database.Cursor);
-    method public void loadInitial(androidx.paging.PositionalDataSource.LoadInitialParams, androidx.paging.PositionalDataSource.LoadInitialCallback<T!>);
-    method public void loadRange(androidx.paging.PositionalDataSource.LoadRangeParams, androidx.paging.PositionalDataSource.LoadRangeCallback<T!>);
+    ctor protected LimitOffsetDataSource(androidx.room.RoomDatabase db, androidx.room.RoomSQLiteQuery sourceQuery, boolean inTransaction, boolean registerObserverImmediately, java.lang.String... tables);
+    ctor protected LimitOffsetDataSource(androidx.room.RoomDatabase db, androidx.sqlite.db.SupportSQLiteQuery query, boolean inTransaction, java.lang.String... tables);
+    ctor protected LimitOffsetDataSource(androidx.room.RoomDatabase db, androidx.sqlite.db.SupportSQLiteQuery query, boolean inTransaction, boolean registerObserverImmediately, java.lang.String... tables);
+    ctor protected LimitOffsetDataSource(androidx.room.RoomDatabase db, androidx.room.RoomSQLiteQuery query, boolean inTransaction, java.lang.String... tables);
+    method protected abstract java.util.List<T> convertRows(android.database.Cursor cursor);
+    method public void loadInitial(androidx.paging.PositionalDataSource.LoadInitialParams params, androidx.paging.PositionalDataSource.LoadInitialCallback<T> callback);
+    method public void loadRange(androidx.paging.PositionalDataSource.LoadRangeParams params, androidx.paging.PositionalDataSource.LoadRangeCallback<T> callback);
+    property public boolean isInvalid;
   }
 
 }
diff --git a/room/room-runtime/build.gradle b/room/room-runtime/build.gradle
index 0cdeecd..3e43c71 100644
--- a/room/room-runtime/build.gradle
+++ b/room/room-runtime/build.gradle
@@ -36,7 +36,7 @@
     api(project(":sqlite:sqlite-framework"))
     api(project(":sqlite:sqlite"))
     implementation("androidx.arch.core:core-runtime:2.0.1")
-    compileOnly("androidx.paging:paging-common:2.0.0")
+    compileOnly("androidx.paging:paging-common:3.1.0")
     compileOnly("androidx.lifecycle:lifecycle-livedata-core:2.0.0")
     implementation("androidx.annotation:annotation-experimental:1.1.0-rc01")
     compileOnly libs.kotlinStdlib // Due to :annotation-experimental
diff --git a/room/room-runtime/src/main/java/androidx/room/paging/LimitOffsetDataSource.kt b/room/room-runtime/src/main/java/androidx/room/paging/LimitOffsetDataSource.kt
index 81ba417..f1203ae 100644
--- a/room/room-runtime/src/main/java/androidx/room/paging/LimitOffsetDataSource.kt
+++ b/room/room-runtime/src/main/java/androidx/room/paging/LimitOffsetDataSource.kt
@@ -13,104 +13,88 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+@file:Suppress("DEPRECATION")
 
-package androidx.room.paging;
+package androidx.room.paging
 
-import android.database.Cursor;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
-import androidx.room.InvalidationTracker;
-import androidx.room.RoomDatabase;
-import androidx.room.RoomSQLiteQuery;
-import androidx.sqlite.db.SupportSQLiteQuery;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicBoolean;
+import android.database.Cursor
+import androidx.annotation.RestrictTo
+import androidx.paging.PositionalDataSource
+import androidx.room.InvalidationTracker
+import androidx.room.RoomDatabase
+import androidx.room.RoomSQLiteQuery
+import androidx.sqlite.db.SupportSQLiteQuery
+import java.util.concurrent.atomic.AtomicBoolean
 
 /**
  * A simple data source implementation that uses Limit & Offset to page the query.
- * <p>
+ *
  * This is NOT the most efficient way to do paging on SQLite. It is
- * <a href="http://www.sqlite.org/cvstrac/wiki?p=ScrollingCursor">recommended</a> to use an indexed
+ * [recommended](http://www.sqlite.org/cvstrac/wiki?p=ScrollingCursor) to use an indexed
  * ORDER BY statement but that requires a more complex API. This solution is technically equal to
- * receiving a {@link Cursor} from a large query but avoids the need to manually manage it, and
+ * receiving a [Cursor] from a large query but avoids the need to manually manage it, and
  * never returns inconsistent data if it is invalidated.
  *
- * This class is used for both Paging2 and Pagin3 (via its compat API). When used with Paging3,
+ * This class is used for both Paging2 and Paging3 (via its compat API). When used with Paging3,
  * it does lazy registration for observers to be suitable for initialization on the main thread
  * whereas in Paging2, it will register observer eagerly to obey Paging2's strict Data Source
  * rules. (Paging2 does not let data source to possibly return invalidated data).
  *
- * @param <T> Data type returned by the data source.
+ * @property <T> Data type returned by the data source.
  *
  * @hide
  */
-@SuppressWarnings("deprecation")
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
-public abstract class LimitOffsetDataSource<T> extends androidx.paging.PositionalDataSource<T> {
-    private final RoomSQLiteQuery mSourceQuery;
-    private final String mCountQuery;
-    private final String mLimitOffsetQuery;
-    private final RoomDatabase mDb;
-    @SuppressWarnings("FieldCanBeLocal")
-    private final InvalidationTracker.Observer mObserver;
-    private final boolean mInTransaction;
-    private final AtomicBoolean mRegisteredObserver = new AtomicBoolean(false);
+abstract class LimitOffsetDataSource<T : Any> protected constructor(
+    private val db: RoomDatabase,
+    private val sourceQuery: RoomSQLiteQuery,
+    private val inTransaction: Boolean,
+    registerObserverImmediately: Boolean,
+    vararg tables: String
+) : PositionalDataSource<T>() {
+    private val countQuery = "SELECT COUNT(*) FROM ( " + sourceQuery.sql + " )"
+    private val limitOffsetQuery = "SELECT * FROM ( " + sourceQuery.sql + " ) LIMIT ? OFFSET ?"
+    private val observer: InvalidationTracker.Observer
+    private val registeredObserver = AtomicBoolean(false)
 
-    protected LimitOffsetDataSource(@NonNull RoomDatabase db,
-            @NonNull SupportSQLiteQuery query,
-            boolean inTransaction,
-            @NonNull
-                    String... tables) {
-        this(db, RoomSQLiteQuery.copyFrom(query), inTransaction, tables);
-    }
+    protected constructor(
+        db: RoomDatabase,
+        query: SupportSQLiteQuery,
+        inTransaction: Boolean,
+        vararg tables: String
+    ) : this(db, RoomSQLiteQuery.copyFrom(query), inTransaction, *tables)
 
-    protected LimitOffsetDataSource(
-            @NonNull RoomDatabase db,
-            @NonNull SupportSQLiteQuery query,
-            boolean inTransaction,
-            boolean registerObserverImmediately,
-            @NonNull String... tables) {
-        this(db, RoomSQLiteQuery.copyFrom(query), inTransaction, registerObserverImmediately,
-                tables);
-    }
+    protected constructor(
+        db: RoomDatabase,
+        query: SupportSQLiteQuery,
+        inTransaction: Boolean,
+        registerObserverImmediately: Boolean,
+        vararg tables: String
+    ) : this(
+        db, RoomSQLiteQuery.copyFrom(query), inTransaction, registerObserverImmediately, *tables
+    )
 
-    protected LimitOffsetDataSource(
-            @NonNull RoomDatabase db,
-            @NonNull RoomSQLiteQuery query,
-            boolean inTransaction,
-            @NonNull String... tables) {
-        this(db, query, inTransaction, true /*register registerObserverImmediately*/, tables);
-    }
+    protected constructor(
+        db: RoomDatabase,
+        query: RoomSQLiteQuery,
+        inTransaction: Boolean,
+        vararg tables: String
+    ) : this(db, query, inTransaction, true, *tables)
 
-    protected LimitOffsetDataSource(
-            @NonNull RoomDatabase db,
-            @NonNull RoomSQLiteQuery query,
-            boolean inTransaction,
-            boolean registerObserverImmediately,
-            @NonNull String... tables) {
-        mDb = db;
-        mSourceQuery = query;
-        mInTransaction = inTransaction;
-        mCountQuery = "SELECT COUNT(*) FROM ( " + mSourceQuery.getSql() + " )";
-        mLimitOffsetQuery = "SELECT * FROM ( " + mSourceQuery.getSql() + " ) LIMIT ? OFFSET ?";
-        mObserver = new InvalidationTracker.Observer(tables) {
-            @Override
-            public void onInvalidated(@NonNull Set<String> tables) {
-                invalidate();
+    init {
+        observer = object : InvalidationTracker.Observer(tables) {
+            override fun onInvalidated(tables: Set<String>) {
+                invalidate()
             }
-        };
+        }
         if (registerObserverImmediately) {
-            registerObserverIfNecessary();
+            registerObserverIfNecessary()
         }
     }
 
-    private void registerObserverIfNecessary() {
-        if (mRegisteredObserver.compareAndSet(false, true)) {
-            mDb.getInvalidationTracker().addWeakObserver(mObserver);
+    private fun registerObserverIfNecessary() {
+        if (registeredObserver.compareAndSet(false, true)) {
+            db.invalidationTracker.addWeakObserver(observer)
         }
     }
 
@@ -119,76 +103,63 @@
      *
      * @hide
      */
-    @SuppressWarnings("WeakerAccess")
-    public int countItems() {
-        registerObserverIfNecessary();
-        final RoomSQLiteQuery sqLiteQuery = RoomSQLiteQuery.acquire(mCountQuery,
-                mSourceQuery.getArgCount());
-        sqLiteQuery.copyArgumentsFrom(mSourceQuery);
-        Cursor cursor = mDb.query(sqLiteQuery);
-        try {
+    fun countItems(): Int {
+        registerObserverIfNecessary()
+        val sqLiteQuery = RoomSQLiteQuery.acquire(
+            countQuery,
+            sourceQuery.argCount
+        )
+        sqLiteQuery.copyArgumentsFrom(sourceQuery)
+        val cursor = db.query(sqLiteQuery)
+        return try {
             if (cursor.moveToFirst()) {
-                return cursor.getInt(0);
-            }
-            return 0;
+                cursor.getInt(0)
+            } else 0
         } finally {
-            cursor.close();
-            sqLiteQuery.release();
+            cursor.close()
+            sqLiteQuery.release()
         }
     }
 
-    @Override
-    public boolean isInvalid() {
-        registerObserverIfNecessary();
-        mDb.getInvalidationTracker().refreshVersionsSync();
-        return super.isInvalid();
+    override val isInvalid: Boolean
+        get() {
+        registerObserverIfNecessary()
+        db.invalidationTracker.refreshVersionsSync()
+        return super.isInvalid
     }
 
-    @NonNull
-    @SuppressWarnings("WeakerAccess")
-    protected abstract List<T> convertRows(@NonNull Cursor cursor);
+    protected abstract fun convertRows(cursor: Cursor): List<T>
 
-    @SuppressWarnings("deprecation")
-    @Override
-    public void loadInitial(@NonNull LoadInitialParams params,
-            @NonNull LoadInitialCallback<T> callback) {
-        registerObserverIfNecessary();
-        List<T> list = Collections.emptyList();
-        int totalCount;
-        int firstLoadPosition = 0;
-        RoomSQLiteQuery sqLiteQuery = null;
-        Cursor cursor = null;
-        mDb.beginTransaction();
+    @Suppress("deprecation")
+    override fun loadInitial(params: LoadInitialParams, callback: LoadInitialCallback<T>) {
+        registerObserverIfNecessary()
+        val onResultCaller: () -> Unit
+        db.beginTransaction()
         try {
-            totalCount = countItems();
+            val totalCount = countItems()
             if (totalCount != 0) {
                 // bound the size requested, based on known count
-                firstLoadPosition = computeInitialLoadPosition(params, totalCount);
-                int firstLoadSize = computeInitialLoadSize(params, firstLoadPosition, totalCount);
-
-                sqLiteQuery = getSQLiteQuery(firstLoadPosition, firstLoadSize);
-                cursor = mDb.query(sqLiteQuery);
-                List<T> rows = convertRows(cursor);
-                mDb.setTransactionSuccessful();
-                list = rows;
+                val firstLoadPosition = computeInitialLoadPosition(params, totalCount)
+                val firstLoadSize = computeInitialLoadSize(params, firstLoadPosition, totalCount)
+                db.query(getSQLiteQuery(firstLoadPosition, firstLoadSize)).use { cursor ->
+                    val rows = convertRows(cursor)
+                    db.setTransactionSuccessful()
+                    onResultCaller = { callback.onResult(rows, firstLoadPosition, totalCount) }
+                }
+            } else {
+                onResultCaller = { callback.onResult(emptyList(), 0, totalCount) }
             }
         } finally {
-            if (cursor != null) {
-                cursor.close();
-            }
-            mDb.endTransaction();
-            if (sqLiteQuery != null) {
-                sqLiteQuery.release();
-            }
+            db.endTransaction()
         }
-
-        callback.onResult(list, firstLoadPosition, totalCount);
+        onResultCaller.invoke()
     }
 
-    @Override
-    public void loadRange(@NonNull LoadRangeParams params,
-            @NonNull LoadRangeCallback<T> callback) {
-        callback.onResult(loadRange(params.startPosition, params.loadSize));
+    override fun loadRange(
+        params: LoadRangeParams,
+        callback: LoadRangeCallback<T>
+    ) {
+        callback.onResult(loadRange(params.startPosition, params.loadSize))
     }
 
     /**
@@ -196,44 +167,39 @@
      *
      * @hide
      */
-    @SuppressWarnings("deprecation")
-    @NonNull
-    public List<T> loadRange(int startPosition, int loadCount) {
-        final RoomSQLiteQuery sqLiteQuery = getSQLiteQuery(startPosition, loadCount);
-        if (mInTransaction) {
-            mDb.beginTransaction();
-            Cursor cursor = null;
-            //noinspection TryFinallyCanBeTryWithResources
-            try {
-                cursor = mDb.query(sqLiteQuery);
-                List<T> rows = convertRows(cursor);
-                mDb.setTransactionSuccessful();
-                return rows;
-            } finally {
-                if (cursor != null) {
-                    cursor.close();
+    @Suppress("deprecation")
+    fun loadRange(startPosition: Int, loadCount: Int): List<T> {
+        val sqLiteQuery = getSQLiteQuery(startPosition, loadCount)
+        try {
+            if (inTransaction) {
+                db.beginTransaction()
+                try {
+                    db.query(sqLiteQuery).use { cursor ->
+                        val rows = convertRows(cursor)
+                        db.setTransactionSuccessful()
+                        return rows
+                    }
+                } finally {
+                    db.endTransaction()
                 }
-                mDb.endTransaction();
-                sqLiteQuery.release();
+            } else {
+                db.query(sqLiteQuery).use { cursor ->
+                    return convertRows(cursor)
+                }
             }
-        } else {
-            Cursor cursor = mDb.query(sqLiteQuery);
-            //noinspection TryFinallyCanBeTryWithResources
-            try {
-                return convertRows(cursor);
-            } finally {
-                cursor.close();
-                sqLiteQuery.release();
-            }
+        } finally {
+            sqLiteQuery.release()
         }
     }
 
-    private RoomSQLiteQuery getSQLiteQuery(int startPosition, int loadCount) {
-        final RoomSQLiteQuery sqLiteQuery = RoomSQLiteQuery.acquire(mLimitOffsetQuery,
-                mSourceQuery.getArgCount() + 2);
-        sqLiteQuery.copyArgumentsFrom(mSourceQuery);
-        sqLiteQuery.bindLong(sqLiteQuery.getArgCount() - 1, loadCount);
-        sqLiteQuery.bindLong(sqLiteQuery.getArgCount(), startPosition);
-        return sqLiteQuery;
+    private fun getSQLiteQuery(startPosition: Int, loadCount: Int): RoomSQLiteQuery {
+        val sqLiteQuery = RoomSQLiteQuery.acquire(
+            limitOffsetQuery,
+            sourceQuery.argCount + 2
+        )
+        sqLiteQuery.copyArgumentsFrom(sourceQuery)
+        sqLiteQuery.bindLong(sqLiteQuery.argCount - 1, loadCount.toLong())
+        sqLiteQuery.bindLong(sqLiteQuery.argCount, startPosition.toLong())
+        return sqLiteQuery
     }
-}
+}
\ No newline at end of file