Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a31bcf96 authored by Tomasz Mikolajewski's avatar Tomasz Mikolajewski Committed by Android (Google) Code Review
Browse files

Merge "Load archives in background."

parents 0973ce11 e0063b21
Loading
Loading
Loading
Loading
+43 −8
Original line number Diff line number Diff line
@@ -21,10 +21,11 @@ import android.content.res.AssetFileDescriptor;
import android.content.res.Configuration;
import android.database.ContentObserver;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.database.MatrixCursor.RowBuilder;
import android.database.MatrixCursor;
import android.graphics.Point;
import android.net.Uri;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.ParcelFileDescriptor;
import android.provider.DocumentsContract.Document;
@@ -44,6 +45,8 @@ import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
@@ -95,9 +98,23 @@ public class ArchivesProvider extends DocumentsProvider implements Closeable {
    public Cursor queryChildDocuments(String documentId, @Nullable String[] projection,
            @Nullable String sortOrder)
            throws FileNotFoundException {
        final ArchiveId archiveId = ArchiveId.fromDocumentId(documentId);
        Loader loader = null;
        try {
            loader = obtainInstance(documentId);
            if (loader.mArchive == null) {
                final MatrixCursor cursor = new MatrixCursor(
                        projection != null ? projection : Archive.DEFAULT_PROJECTION);
                // Return an empty cursor with EXTRA_LOADING, which shows spinner
                // in DocumentsUI. Once the archive is loaded, the notification will
                // be sent, and the directory reloaded.
                final Bundle bundle = new Bundle();
                bundle.putBoolean(DocumentsContract.EXTRA_LOADING, true);
                cursor.setExtras(bundle);
                cursor.setNotificationUri(getContext().getContentResolver(),
                        buildUriForArchive(archiveId.mArchiveUri));
                return cursor;
            }
            return loader.get().queryChildDocuments(documentId, projection, sortOrder);
        } finally {
            releaseInstance(loader);
@@ -138,16 +155,11 @@ public class ArchivesProvider extends DocumentsProvider implements Closeable {
            throws FileNotFoundException {
        final ArchiveId archiveId = ArchiveId.fromDocumentId(documentId);
        if (archiveId.mPath.equals("/")) {
            // For the archive's root directory return hard-coded cursor, so clients know that
            // it's actually a directory and queryChildDocuments() can be called on it.
            //
            // TODO: Move this code to the Archive class, once opening archives is moved to
            // background.
            final MatrixCursor cursor = new MatrixCursor(
                    projection != null ? projection : Archive.DEFAULT_PROJECTION);
            final RowBuilder row = cursor.newRow();
            row.add(Document.COLUMN_DOCUMENT_ID, documentId);
            row.add(Document.COLUMN_DISPLAY_NAME, "Archive");  // TODO: Fix.
            row.add(Document.COLUMN_DISPLAY_NAME, "Archive");  // TODO: Translate.
            row.add(Document.COLUMN_SIZE, 0);
            row.add(Document.COLUMN_MIME_TYPE, Document.MIME_TYPE_DIR);
            return cursor;
@@ -278,12 +290,17 @@ public class ArchivesProvider extends DocumentsProvider implements Closeable {
        private final Uri mArchiveUri;
        private final Uri mNotificationUri;
        private final ReentrantReadWriteLock mLock = new ReentrantReadWriteLock();
        private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
        private Archive mArchive = null;
        private Exception mFailureException = null;

        Loader(Context context, Uri archiveUri, Uri notificationUri) {
            this.mContext = context;
            this.mArchiveUri = archiveUri;
            this.mNotificationUri = notificationUri;

            // Start loading the archive immediately in the background.
            mExecutor.submit(this::get);
        }

        synchronized Archive get() throws FileNotFoundException {
@@ -291,6 +308,15 @@ public class ArchivesProvider extends DocumentsProvider implements Closeable {
                return mArchive;
            }

            // Once loading the archive failed, do not to retry opening it until the
            // archive file has changed (the loader is deleted once we receive
            // a notification about the archive file being changed).
            if (mFailureException != null) {
                throw new IllegalStateException(
                        "Trying to perform an operation on an archive which failed to load.",
                        mFailureException);
            }

            try {
                mArchive = Archive.createForParcelFileDescriptor(
                        mContext,
@@ -298,9 +324,18 @@ public class ArchivesProvider extends DocumentsProvider implements Closeable {
                                mArchiveUri, "r", null /* signal */),
                        mArchiveUri, mNotificationUri);
            } catch (IOException e) {
                mFailureException = e;
                throw new IllegalStateException(e);
            } catch (RuntimeException e) {
                mFailureException = e;
                throw e;
            } finally {
                // Notify observers that the root directory is loaded (or failed)
                // so clients reload it.
                mContext.getContentResolver().notifyChange(
                        buildUriForArchive(mArchiveUri),
                        null /* observer */, false /* syncToNetwork */);
            }

            return mArchive;
        }