Loading src/com/android/documentsui/loaders/BaseFileLoader.kt +49 −26 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import android.util.Log import androidx.loader.content.AsyncTaskLoader import com.android.documentsui.DirectoryResult import com.android.documentsui.base.Lookup import com.android.documentsui.base.SharedMinimal.DEBUG import com.android.documentsui.base.UserId import com.android.documentsui.roots.RootCursorWrapper Loading Loading @@ -69,30 +70,34 @@ fun toSingleCursor(cursorList: List<Cursor>): Cursor { */ abstract class BaseFileLoader( context: Context, private val mUserIdList: List<UserId>, protected val mMimeTypeLookup: Lookup<String, String>, private val userIdList: List<UserId>, protected val mimeTypeLookup: Lookup<String, String>, ) : AsyncTaskLoader<DirectoryResult>(context) { private var mSignal: CancellationSignal? = null private var mResult: DirectoryResult? = null private var signal: CancellationSignal? = null private var storedResult: DirectoryResult? = null override fun cancelLoadInBackground() { if (DEBUG) { Log.d(TAG, "${this::class.simpleName}.cancelLoadInBackground") } super.cancelLoadInBackground() synchronized(this) { mSignal?.cancel() signal?.cancel() } } override fun deliverResult(result: DirectoryResult?) { if (DEBUG) { Log.d(TAG, "${this::class.simpleName}.deliverResult") } if (isReset) { closeResult(result) return } val oldResult: DirectoryResult? = mResult mResult = result val oldResult: DirectoryResult? = storedResult storedResult = result if (isStarted) { super.deliverResult(result) Loading @@ -104,35 +109,43 @@ abstract class BaseFileLoader( } override fun onStartLoading() { if (DEBUG) { Log.d(TAG, "${this::class.simpleName}.onStartLoading") val isCursorStale: Boolean = checkIfCursorStale(mResult) if (mResult != null && !isCursorStale) { deliverResult(mResult) } if (takeContentChanged() || mResult == null || isCursorStale) { val isCursorStale: Boolean = checkIfCursorStale(storedResult) if (storedResult != null && !isCursorStale) { deliverResult(storedResult) } if (takeContentChanged() || storedResult == null || isCursorStale) { forceLoad() } } override fun onStopLoading() { if (DEBUG) { Log.d(TAG, "${this::class.simpleName}.onStopLoading") } cancelLoad() } override fun onCanceled(result: DirectoryResult?) { if (DEBUG) { Log.d(TAG, "${this::class.simpleName}.onCanceled") } closeResult(result) } override fun onReset() { if (DEBUG) { Log.d(TAG, "${this::class.simpleName}.onReset") } super.onReset() // Ensure the loader is stopped onStopLoading() closeResult(mResult) mResult = null closeResult(storedResult) storedResult = null } /** Loading @@ -142,9 +155,11 @@ abstract class BaseFileLoader( try { result?.close() } catch (e: Exception) { if (DEBUG) { Log.d(TAG, "Failed to close result", e) } } } private fun checkIfCursorStale(result: DirectoryResult?): Boolean { if (result == null) { Loading @@ -154,7 +169,9 @@ abstract class BaseFileLoader( if (cursor.isClosed) { return true } if (DEBUG) { Log.d(TAG, "Long check of cursor staleness") } val count = cursor.count if (!cursor.moveToPosition(-1)) { return true Loading @@ -180,8 +197,10 @@ abstract class BaseFileLoader( maxResults: Int, ): Cursor? { val authority = locationUri.authority ?: return null for (userId in mUserIdList) { for (userId in userIdList) { if (DEBUG) { Log.d(TAG, "BaseFileLoader.queryLocation for $userId at $locationUri") } val resolver = userId.getContentResolver(context) try { resolver.acquireUnstableContentProviderClient( Loading @@ -192,16 +211,20 @@ abstract class BaseFileLoader( } try { val cursor = client.query(locationUri, null, queryArgs, mSignal) ?: return null client.query(locationUri, null, queryArgs, signal) ?: return null return RootCursorWrapper(userId, authority, rootId, cursor, maxResults) } catch (e: RemoteException) { if (DEBUG) { Log.d(TAG, "Failed to get cursor for $locationUri", e) } } } } catch (e: Exception) { if (DEBUG) { Log.d(TAG, "Failed to get a content provider client for $locationUri", e) } } } return null } Loading src/com/android/documentsui/loaders/FolderLoader.kt +1 −1 Original line number Diff line number Diff line Loading @@ -86,7 +86,7 @@ class FolderLoader( filteredCursor.filterLastModified(rejectBeforeTimestamp) } // TODO(b:380945065): Add filtering by category, such as images, audio, video. val sortedCursor = mSortModel.sortCursor(filteredCursor, mMimeTypeLookup) val sortedCursor = mSortModel.sortCursor(filteredCursor, mimeTypeLookup) result.doc = mListedDir result.cursor = sortedCursor Loading src/com/android/documentsui/loaders/SearchLoader.kt +39 −14 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ import com.android.documentsui.base.DocumentInfo import com.android.documentsui.base.FilteringCursorWrapper import com.android.documentsui.base.FolderInfo import com.android.documentsui.base.Lookup import com.android.documentsui.base.SharedMinimal.DEBUG import com.android.documentsui.base.UserId import com.android.documentsui.sorting.SortModel import com.google.common.util.concurrent.AbstractFuture Loading Loading @@ -95,9 +96,11 @@ class SearchLoader( latch.countDown() } } if (DEBUG) { Log.d(TAG, "Query on $searchUri took $queryDuration") } } } @Volatile private var mSearchTaskList: List<SearchTask> = listOf() Loading @@ -115,7 +118,9 @@ class SearchLoader( // Step 1: Build a list of search tasks. val searchTaskList = createSearchTaskList(rejectBeforeTimestamp, countDownLatch, folderList) if (DEBUG) { Log.d(TAG, "${searchTaskList.size} tasks have been created") } // Check if we are cancelled; if not copy the task list. if (isLoadInBackgroundCanceled) { Loading @@ -127,23 +132,33 @@ class SearchLoader( for (task in mSearchTaskList) { executorService.execute(task) } if (DEBUG) { Log.d(TAG, "${mSearchTaskList.size} tasks have been enqueued") } // Step 3: Wait for the results. try { if (options.isQueryTimeUnlimited()) { if (DEBUG) { Log.d(TAG, "Waiting for results with no time limit") } countDownLatch.await() } else { if (DEBUG) { Log.d(TAG, "Waiting ${options.maxQueryTime!!.toMillis()}ms for results") } countDownLatch.await( options.maxQueryTime.toMillis(), options.maxQueryTime!!.toMillis(), TimeUnit.MILLISECONDS ) } if (DEBUG) { Log.d(TAG, "Waiting for results is done") } } catch (e: InterruptedException) { if (DEBUG) { Log.d(TAG, "Failed to complete all searches within ${options.maxQueryTime}") } // TODO(b:388336095): Record a metrics indicating incomplete search. throw RuntimeException(e) } Loading @@ -151,7 +166,9 @@ class SearchLoader( // Step 4: Collect cursors from done tasks. val cursorList = mutableListOf<Cursor>() for (task in mSearchTaskList) { if (DEBUG) { Log.d(TAG, "Processing task ${task.taskId}") } if (isLoadInBackgroundCanceled) { break } Loading @@ -159,11 +176,15 @@ class SearchLoader( val cursor = task.cursor if (task.isDone && cursor != null) { // TODO(b:388336095): Record a metric for null and not null cursor. if (DEBUG) { Log.d(TAG, "Task ${task.taskId} has ${cursor.count} results") } cursorList.add(cursor) } } if (DEBUG) { Log.d(TAG, "Search complete with ${cursorList.size} cursors collected") } // Step 5: Assign the cursor, after adding filtering and sorting, to the results. val mergedCursor = toSingleCursor(cursorList) Loading @@ -177,7 +198,7 @@ class SearchLoader( if (rejectBeforeTimestamp > 0L) { filteringCursor.filterLastModified(rejectBeforeTimestamp) } result.cursor = sortModel.sortCursor(filteringCursor, mMimeTypeLookup) result.cursor = sortModel.sortCursor(filteringCursor, mimeTypeLookup) // TODO(b:388336095): Record the total time it took to complete search. return result Loading Loading @@ -232,7 +253,9 @@ class SearchLoader( // TODO(b:385789236): Correctly pass sort order information. val queryArgs = createQueryArgs(rejectBeforeTimestamp) sortModel.addQuerySortArgs(queryArgs) if (DEBUG) { Log.d(TAG, "Query $rootSearchUri and queryArgs $queryArgs") } val task = SearchTask( folder.folderId, rootSearchUri, Loading @@ -248,7 +271,9 @@ class SearchLoader( for (task in mSearchTaskList) { task.close() } if (DEBUG) { Log.d(TAG, "Resetting search loader; search task list emptied.") } super.onReset() } } Loading
src/com/android/documentsui/loaders/BaseFileLoader.kt +49 −26 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import android.util.Log import androidx.loader.content.AsyncTaskLoader import com.android.documentsui.DirectoryResult import com.android.documentsui.base.Lookup import com.android.documentsui.base.SharedMinimal.DEBUG import com.android.documentsui.base.UserId import com.android.documentsui.roots.RootCursorWrapper Loading Loading @@ -69,30 +70,34 @@ fun toSingleCursor(cursorList: List<Cursor>): Cursor { */ abstract class BaseFileLoader( context: Context, private val mUserIdList: List<UserId>, protected val mMimeTypeLookup: Lookup<String, String>, private val userIdList: List<UserId>, protected val mimeTypeLookup: Lookup<String, String>, ) : AsyncTaskLoader<DirectoryResult>(context) { private var mSignal: CancellationSignal? = null private var mResult: DirectoryResult? = null private var signal: CancellationSignal? = null private var storedResult: DirectoryResult? = null override fun cancelLoadInBackground() { if (DEBUG) { Log.d(TAG, "${this::class.simpleName}.cancelLoadInBackground") } super.cancelLoadInBackground() synchronized(this) { mSignal?.cancel() signal?.cancel() } } override fun deliverResult(result: DirectoryResult?) { if (DEBUG) { Log.d(TAG, "${this::class.simpleName}.deliverResult") } if (isReset) { closeResult(result) return } val oldResult: DirectoryResult? = mResult mResult = result val oldResult: DirectoryResult? = storedResult storedResult = result if (isStarted) { super.deliverResult(result) Loading @@ -104,35 +109,43 @@ abstract class BaseFileLoader( } override fun onStartLoading() { if (DEBUG) { Log.d(TAG, "${this::class.simpleName}.onStartLoading") val isCursorStale: Boolean = checkIfCursorStale(mResult) if (mResult != null && !isCursorStale) { deliverResult(mResult) } if (takeContentChanged() || mResult == null || isCursorStale) { val isCursorStale: Boolean = checkIfCursorStale(storedResult) if (storedResult != null && !isCursorStale) { deliverResult(storedResult) } if (takeContentChanged() || storedResult == null || isCursorStale) { forceLoad() } } override fun onStopLoading() { if (DEBUG) { Log.d(TAG, "${this::class.simpleName}.onStopLoading") } cancelLoad() } override fun onCanceled(result: DirectoryResult?) { if (DEBUG) { Log.d(TAG, "${this::class.simpleName}.onCanceled") } closeResult(result) } override fun onReset() { if (DEBUG) { Log.d(TAG, "${this::class.simpleName}.onReset") } super.onReset() // Ensure the loader is stopped onStopLoading() closeResult(mResult) mResult = null closeResult(storedResult) storedResult = null } /** Loading @@ -142,9 +155,11 @@ abstract class BaseFileLoader( try { result?.close() } catch (e: Exception) { if (DEBUG) { Log.d(TAG, "Failed to close result", e) } } } private fun checkIfCursorStale(result: DirectoryResult?): Boolean { if (result == null) { Loading @@ -154,7 +169,9 @@ abstract class BaseFileLoader( if (cursor.isClosed) { return true } if (DEBUG) { Log.d(TAG, "Long check of cursor staleness") } val count = cursor.count if (!cursor.moveToPosition(-1)) { return true Loading @@ -180,8 +197,10 @@ abstract class BaseFileLoader( maxResults: Int, ): Cursor? { val authority = locationUri.authority ?: return null for (userId in mUserIdList) { for (userId in userIdList) { if (DEBUG) { Log.d(TAG, "BaseFileLoader.queryLocation for $userId at $locationUri") } val resolver = userId.getContentResolver(context) try { resolver.acquireUnstableContentProviderClient( Loading @@ -192,16 +211,20 @@ abstract class BaseFileLoader( } try { val cursor = client.query(locationUri, null, queryArgs, mSignal) ?: return null client.query(locationUri, null, queryArgs, signal) ?: return null return RootCursorWrapper(userId, authority, rootId, cursor, maxResults) } catch (e: RemoteException) { if (DEBUG) { Log.d(TAG, "Failed to get cursor for $locationUri", e) } } } } catch (e: Exception) { if (DEBUG) { Log.d(TAG, "Failed to get a content provider client for $locationUri", e) } } } return null } Loading
src/com/android/documentsui/loaders/FolderLoader.kt +1 −1 Original line number Diff line number Diff line Loading @@ -86,7 +86,7 @@ class FolderLoader( filteredCursor.filterLastModified(rejectBeforeTimestamp) } // TODO(b:380945065): Add filtering by category, such as images, audio, video. val sortedCursor = mSortModel.sortCursor(filteredCursor, mMimeTypeLookup) val sortedCursor = mSortModel.sortCursor(filteredCursor, mimeTypeLookup) result.doc = mListedDir result.cursor = sortedCursor Loading
src/com/android/documentsui/loaders/SearchLoader.kt +39 −14 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ import com.android.documentsui.base.DocumentInfo import com.android.documentsui.base.FilteringCursorWrapper import com.android.documentsui.base.FolderInfo import com.android.documentsui.base.Lookup import com.android.documentsui.base.SharedMinimal.DEBUG import com.android.documentsui.base.UserId import com.android.documentsui.sorting.SortModel import com.google.common.util.concurrent.AbstractFuture Loading Loading @@ -95,9 +96,11 @@ class SearchLoader( latch.countDown() } } if (DEBUG) { Log.d(TAG, "Query on $searchUri took $queryDuration") } } } @Volatile private var mSearchTaskList: List<SearchTask> = listOf() Loading @@ -115,7 +118,9 @@ class SearchLoader( // Step 1: Build a list of search tasks. val searchTaskList = createSearchTaskList(rejectBeforeTimestamp, countDownLatch, folderList) if (DEBUG) { Log.d(TAG, "${searchTaskList.size} tasks have been created") } // Check if we are cancelled; if not copy the task list. if (isLoadInBackgroundCanceled) { Loading @@ -127,23 +132,33 @@ class SearchLoader( for (task in mSearchTaskList) { executorService.execute(task) } if (DEBUG) { Log.d(TAG, "${mSearchTaskList.size} tasks have been enqueued") } // Step 3: Wait for the results. try { if (options.isQueryTimeUnlimited()) { if (DEBUG) { Log.d(TAG, "Waiting for results with no time limit") } countDownLatch.await() } else { if (DEBUG) { Log.d(TAG, "Waiting ${options.maxQueryTime!!.toMillis()}ms for results") } countDownLatch.await( options.maxQueryTime.toMillis(), options.maxQueryTime!!.toMillis(), TimeUnit.MILLISECONDS ) } if (DEBUG) { Log.d(TAG, "Waiting for results is done") } } catch (e: InterruptedException) { if (DEBUG) { Log.d(TAG, "Failed to complete all searches within ${options.maxQueryTime}") } // TODO(b:388336095): Record a metrics indicating incomplete search. throw RuntimeException(e) } Loading @@ -151,7 +166,9 @@ class SearchLoader( // Step 4: Collect cursors from done tasks. val cursorList = mutableListOf<Cursor>() for (task in mSearchTaskList) { if (DEBUG) { Log.d(TAG, "Processing task ${task.taskId}") } if (isLoadInBackgroundCanceled) { break } Loading @@ -159,11 +176,15 @@ class SearchLoader( val cursor = task.cursor if (task.isDone && cursor != null) { // TODO(b:388336095): Record a metric for null and not null cursor. if (DEBUG) { Log.d(TAG, "Task ${task.taskId} has ${cursor.count} results") } cursorList.add(cursor) } } if (DEBUG) { Log.d(TAG, "Search complete with ${cursorList.size} cursors collected") } // Step 5: Assign the cursor, after adding filtering and sorting, to the results. val mergedCursor = toSingleCursor(cursorList) Loading @@ -177,7 +198,7 @@ class SearchLoader( if (rejectBeforeTimestamp > 0L) { filteringCursor.filterLastModified(rejectBeforeTimestamp) } result.cursor = sortModel.sortCursor(filteringCursor, mMimeTypeLookup) result.cursor = sortModel.sortCursor(filteringCursor, mimeTypeLookup) // TODO(b:388336095): Record the total time it took to complete search. return result Loading Loading @@ -232,7 +253,9 @@ class SearchLoader( // TODO(b:385789236): Correctly pass sort order information. val queryArgs = createQueryArgs(rejectBeforeTimestamp) sortModel.addQuerySortArgs(queryArgs) if (DEBUG) { Log.d(TAG, "Query $rootSearchUri and queryArgs $queryArgs") } val task = SearchTask( folder.folderId, rootSearchUri, Loading @@ -248,7 +271,9 @@ class SearchLoader( for (task in mSearchTaskList) { task.close() } if (DEBUG) { Log.d(TAG, "Resetting search loader; search task list emptied.") } super.onReset() } }