package com.looker.droidify.database import android.content.Context import android.database.Cursor import android.os.CancellationSignal import android.os.OperationCanceledException import androidx.loader.content.AsyncTaskLoader class QueryLoader(context: Context, private val query: (CancellationSignal) -> Cursor?) : AsyncTaskLoader(context) { private val observer = ForceLoadContentObserver() private var cancellationSignal: CancellationSignal? = null private var cursor: Cursor? = null override fun loadInBackground(): Cursor? { val cancellationSignal = synchronized(this) { if (isLoadInBackgroundCanceled) { throw OperationCanceledException() } val cancellationSignal = CancellationSignal() this.cancellationSignal = cancellationSignal cancellationSignal } try { val cursor = query(cancellationSignal) if (cursor != null) { try { cursor.count // Ensure the cursor window is filled cursor.registerContentObserver(observer) } catch (e: Exception) { cursor.close() throw e } } return cursor } finally { synchronized(this) { this.cancellationSignal = null } } } override fun cancelLoadInBackground() { super.cancelLoadInBackground() synchronized(this) { cancellationSignal?.cancel() } } override fun deliverResult(data: Cursor?) { if (isReset) { data?.close() } else { val oldCursor = cursor cursor = data if (isStarted) { super.deliverResult(data) } if (oldCursor != data) { oldCursor.closeIfNeeded() } } } override fun onStartLoading() { cursor?.let(this::deliverResult) if (takeContentChanged() || cursor == null) { forceLoad() } } override fun onStopLoading() { cancelLoad() } override fun onCanceled(data: Cursor?) { data.closeIfNeeded() } override fun onReset() { super.onReset() stopLoading() cursor.closeIfNeeded() cursor = null } private fun Cursor?.closeIfNeeded() { if (this != null && !isClosed) { close() } } }