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

Commit 30590646 authored by Jeff Sharkey's avatar Jeff Sharkey Committed by Android (Google) Code Review
Browse files

Merge "Remember mode and sort on per-directory basis." into klp-dev

parents 910adb11 d182bb64
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -20796,6 +20796,7 @@ package android.provider {
    field public static final java.lang.String COLUMN_SIZE = "_size";
    field public static final java.lang.String COLUMN_SUMMARY = "summary";
    field public static final int FLAG_DIR_PREFERS_GRID = 32; // 0x20
    field public static final int FLAG_DIR_PREFERS_LAST_MODIFIED = 64; // 0x40
    field public static final int FLAG_DIR_SUPPORTS_CREATE = 8; // 0x8
    field public static final int FLAG_DIR_SUPPORTS_SEARCH = 16; // 0x10
    field public static final int FLAG_SUPPORTS_DELETE = 4; // 0x4
+9 −3
Original line number Diff line number Diff line
@@ -251,6 +251,15 @@ public final class DocumentsContract {
         * @see #COLUMN_FLAGS
         */
        public static final int FLAG_DIR_PREFERS_GRID = 1 << 5;

        /**
         * Flag indicating that a directory prefers its contents be sorted by
         * {@link #COLUMN_LAST_MODIFIED}. Only valid when
         * {@link #COLUMN_MIME_TYPE} is {@link #MIME_TYPE_DIR}.
         *
         * @see #COLUMN_FLAGS
         */
        public static final int FLAG_DIR_PREFERS_LAST_MODIFIED = 1 << 6;
    }

    /**
@@ -292,9 +301,6 @@ public final class DocumentsContract {
         * @see #FLAG_LOCAL_ONLY
         * @see #FLAG_SUPPORTS_CREATE
         * @see #FLAG_ADVANCED
         * @see #FLAG_PROVIDES_AUDIO
         * @see #FLAG_PROVIDES_IMAGES
         * @see #FLAG_PROVIDES_VIDEO
         */
        public static final String COLUMN_FLAGS = "flags";

+1 −1
Original line number Diff line number Diff line
@@ -70,7 +70,7 @@ public class CreateDirectoryFragment extends DialogFragment {

                try {
                    final Uri childUri = DocumentsContract.createDocument(
                            resolver, cwd.uri, Document.MIME_TYPE_DIR, displayName);
                            resolver, cwd.derivedUri, Document.MIME_TYPE_DIR, displayName);

                    // Navigate into newly created child
                    final DocumentInfo childDoc = DocumentInfo.fromUri(resolver, childUri);
+64 −41
Original line number Diff line number Diff line
@@ -20,6 +20,8 @@ import static com.android.documentsui.DocumentsActivity.TAG;
import static com.android.documentsui.DocumentsActivity.State.ACTION_MANAGE;
import static com.android.documentsui.DocumentsActivity.State.MODE_GRID;
import static com.android.documentsui.DocumentsActivity.State.MODE_LIST;
import static com.android.documentsui.DocumentsActivity.State.MODE_UNKNOWN;
import static com.android.documentsui.DocumentsActivity.State.SORT_ORDER_UNKNOWN;
import static com.android.documentsui.model.DocumentInfo.getCursorInt;
import static com.android.documentsui.model.DocumentInfo.getCursorLong;
import static com.android.documentsui.model.DocumentInfo.getCursorString;
@@ -91,43 +93,42 @@ public class DirectoryFragment extends Fragment {

    private int mType = TYPE_NORMAL;

    private int mLastMode = MODE_UNKNOWN;
    private int mLastSortOrder = SORT_ORDER_UNKNOWN;

    private Point mThumbSize;

    private DocumentsAdapter mAdapter;
    private LoaderCallbacks<DirectoryResult> mCallbacks;

    private static final String EXTRA_TYPE = "type";
    private static final String EXTRA_AUTHORITY = "authority";
    private static final String EXTRA_ROOT_ID = "rootId";
    private static final String EXTRA_DOC_ID = "docId";
    private static final String EXTRA_ROOT = "root";
    private static final String EXTRA_DOC = "doc";
    private static final String EXTRA_QUERY = "query";

    private static AtomicInteger sLoaderId = new AtomicInteger(4000);

    private int mLastSortOrder = -1;

    private final int mLoaderId = sLoaderId.incrementAndGet();

    public static void showNormal(FragmentManager fm, Uri uri) {
        show(fm, TYPE_NORMAL, uri.getAuthority(), null, DocumentsContract.getDocumentId(uri), null);
    public static void showNormal(FragmentManager fm, RootInfo root, DocumentInfo doc) {
        show(fm, TYPE_NORMAL, root, doc, null);
    }

    public static void showSearch(FragmentManager fm, Uri uri, String query) {
        show(fm, TYPE_SEARCH, uri.getAuthority(), null, DocumentsContract.getDocumentId(uri),
                query);
    public static void showSearch(
            FragmentManager fm, RootInfo root, DocumentInfo doc, String query) {
        show(fm, TYPE_SEARCH, root, doc, query);
    }

    public static void showRecentsOpen(FragmentManager fm) {
        show(fm, TYPE_RECENT_OPEN, null, null, null, null);
        show(fm, TYPE_RECENT_OPEN, null, null, null);
    }

    private static void show(FragmentManager fm, int type, String authority, String rootId,
            String docId, String query) {
    private static void show(
            FragmentManager fm, int type, RootInfo root, DocumentInfo doc, String query) {
        final Bundle args = new Bundle();
        args.putInt(EXTRA_TYPE, type);
        args.putString(EXTRA_AUTHORITY, authority);
        args.putString(EXTRA_ROOT_ID, rootId);
        args.putString(EXTRA_DOC_ID, docId);
        args.putParcelable(EXTRA_ROOT, root);
        args.putParcelable(EXTRA_DOC, doc);
        args.putString(EXTRA_QUERY, query);

        final DirectoryFragment fragment = new DirectoryFragment();
@@ -167,6 +168,7 @@ public class DirectoryFragment extends Fragment {
        super.onActivityCreated(savedInstanceState);

        final Context context = getActivity();
        final State state = getDisplayState(DirectoryFragment.this);

        mAdapter = new DocumentsAdapter();
        mType = getArguments().getInt(EXTRA_TYPE);
@@ -174,35 +176,48 @@ public class DirectoryFragment extends Fragment {
        mCallbacks = new LoaderCallbacks<DirectoryResult>() {
            @Override
            public Loader<DirectoryResult> onCreateLoader(int id, Bundle args) {
                final State state = getDisplayState(DirectoryFragment.this);

                final String authority = getArguments().getString(EXTRA_AUTHORITY);
                final String rootId = getArguments().getString(EXTRA_ROOT_ID);
                final String docId = getArguments().getString(EXTRA_DOC_ID);
                final RootInfo root = getArguments().getParcelable(EXTRA_ROOT);
                final DocumentInfo doc = getArguments().getParcelable(EXTRA_DOC);
                final String query = getArguments().getString(EXTRA_QUERY);

                Uri contentsUri;
                switch (mType) {
                    case TYPE_NORMAL:
                        contentsUri = DocumentsContract.buildChildDocumentsUri(authority, docId);
                        return new DirectoryLoader(context, rootId, contentsUri, state.sortOrder);
                        contentsUri = DocumentsContract.buildChildDocumentsUri(
                                doc.authority, doc.documentId);
                        return new DirectoryLoader(context, root, doc, contentsUri);
                    case TYPE_SEARCH:
                        contentsUri = DocumentsContract.buildSearchDocumentsUri(
                                authority, docId, query);
                        return new DirectoryLoader(context, rootId, contentsUri, state.sortOrder);
                                doc.authority, doc.documentId, query);
                        return new DirectoryLoader(context, root, doc, contentsUri);
                    case TYPE_RECENT_OPEN:
                        final RootsCache roots = DocumentsApplication.getRootsCache(context);
                        final List<RootInfo> matchingRoots = roots.getMatchingRoots(state);
                        return new RecentLoader(context, matchingRoots);
                        return new RecentLoader(context, matchingRoots, state.acceptMimes);
                    default:
                        throw new IllegalStateException("Unknown type " + mType);

                }
            }

            @Override
            public void onLoadFinished(Loader<DirectoryResult> loader, DirectoryResult result) {
                if (!isAdded()) return;

                mAdapter.swapCursor(result.cursor);

                // Push latest state up to UI
                // TODO: if mode change was racing with us, don't overwrite it
                state.mode = result.mode;
                state.sortOrder = result.sortOrder;
                ((DocumentsActivity) context).onStateChanged();

                updateDisplayState();

                if (mLastSortOrder != result.sortOrder) {
                    mLastSortOrder = result.sortOrder;
                    mListView.smoothScrollToPosition(0);
                    mGridView.smoothScrollToPosition(0);
                }
            }

            @Override
@@ -211,6 +226,9 @@ public class DirectoryFragment extends Fragment {
            }
        };

        // Kick off loader at least once
        getLoaderManager().restartLoader(mLoaderId, null, mCallbacks);

        updateDisplayState();
    }

@@ -220,22 +238,27 @@ public class DirectoryFragment extends Fragment {
        updateDisplayState();
    }

    public void updateDisplayState() {
        final State state = getDisplayState(this);

        if (mLastSortOrder != state.sortOrder) {
    public void onUserSortOrderChanged() {
        // User change always triggers reload
        getLoaderManager().restartLoader(mLoaderId, null, mCallbacks);
            mLastSortOrder = state.sortOrder;
    }

        mListView.smoothScrollToPosition(0);
        mGridView.smoothScrollToPosition(0);
    public void onUserModeChanged() {
        // Mode change is just display; no need to reload
        updateDisplayState();
    }

        mListView.setVisibility(state.mode == MODE_LIST ? View.VISIBLE : View.GONE);
        mGridView.setVisibility(state.mode == MODE_GRID ? View.VISIBLE : View.GONE);
    private void updateDisplayState() {
        final State state = getDisplayState(this);

        mFilter = new MimePredicate(state.acceptMimes);

        if (mLastMode == state.mode) return;
        mLastMode = state.mode;

        mListView.setVisibility(state.mode == MODE_LIST ? View.VISIBLE : View.GONE);
        mGridView.setVisibility(state.mode == MODE_GRID ? View.VISIBLE : View.GONE);

        final int choiceMode;
        if (state.allowMultiple) {
            choiceMode = ListView.CHOICE_MODE_MULTIPLE_MODAL;
@@ -254,14 +277,14 @@ public class DirectoryFragment extends Fragment {
            mGridView.setChoiceMode(choiceMode);
            mCurrentView = mGridView;
        } else if (state.mode == MODE_LIST) {
            thumbSize = getResources().getDimensionPixelSize(android.R.dimen.app_icon_size);
            thumbSize = getResources().getDimensionPixelSize(R.dimen.icon_size);
            mGridView.setAdapter(null);
            mGridView.setChoiceMode(ListView.CHOICE_MODE_NONE);
            mListView.setAdapter(mAdapter);
            mListView.setChoiceMode(choiceMode);
            mCurrentView = mListView;
        } else {
            throw new IllegalStateException();
            throw new IllegalStateException("Unknown state " + state.mode);
        }

        mThumbSize = new Point(thumbSize, thumbSize);
@@ -366,7 +389,7 @@ public class DirectoryFragment extends Fragment {
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            intent.addCategory(Intent.CATEGORY_DEFAULT);
            intent.setType(doc.mimeType);
            intent.putExtra(Intent.EXTRA_STREAM, doc.uri);
            intent.putExtra(Intent.EXTRA_STREAM, doc.derivedUri);

        } else if (docs.size() > 1) {
            intent = new Intent(Intent.ACTION_SEND_MULTIPLE);
@@ -377,7 +400,7 @@ public class DirectoryFragment extends Fragment {
            final ArrayList<Uri> uris = Lists.newArrayList();
            for (DocumentInfo doc : docs) {
                mimeTypes.add(doc.mimeType);
                uris.add(doc.uri);
                uris.add(doc.derivedUri);
            }

            intent.setType(findCommonMimeType(mimeTypes));
@@ -403,7 +426,7 @@ public class DirectoryFragment extends Fragment {
                continue;
            }

            if (!DocumentsContract.deleteDocument(resolver, doc.uri)) {
            if (!DocumentsContract.deleteDocument(resolver, doc.derivedUri)) {
                Log.w(TAG, "Failed to delete " + doc);
                hadTrouble = true;
            }
+72 −12
Original line number Diff line number Diff line
@@ -16,18 +16,29 @@

package com.android.documentsui;

import static com.android.documentsui.DocumentsActivity.TAG;
import static com.android.documentsui.DocumentsActivity.State.MODE_UNKNOWN;
import static com.android.documentsui.DocumentsActivity.State.SORT_ORDER_DISPLAY_NAME;
import static com.android.documentsui.DocumentsActivity.State.SORT_ORDER_LAST_MODIFIED;
import static com.android.documentsui.DocumentsActivity.State.SORT_ORDER_SIZE;
import static com.android.documentsui.DocumentsActivity.State.SORT_ORDER_UNKNOWN;
import static com.android.documentsui.model.DocumentInfo.getCursorInt;

import android.content.AsyncTaskLoader;
import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.CancellationSignal;
import android.os.OperationCanceledException;
import android.provider.DocumentsContract.Document;
import android.util.Log;

import com.android.documentsui.DocumentsActivity.State;
import com.android.documentsui.RecentsProvider.StateColumns;
import com.android.documentsui.model.DocumentInfo;
import com.android.documentsui.model.RootInfo;

import libcore.io.IoUtils;

@@ -36,6 +47,9 @@ class DirectoryResult implements AutoCloseable {
    Cursor cursor;
    Exception exception;

    int mode = MODE_UNKNOWN;
    int sortOrder = SORT_ORDER_UNKNOWN;

    @Override
    public void close() {
        IoUtils.closeQuietly(cursor);
@@ -48,18 +62,18 @@ class DirectoryResult implements AutoCloseable {
public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> {
    private final ForceLoadContentObserver mObserver = new ForceLoadContentObserver();

    private final String mRootId;
    private final RootInfo mRoot;
    private final DocumentInfo mDoc;
    private final Uri mUri;
    private final int mSortOrder;

    private CancellationSignal mSignal;
    private DirectoryResult mResult;

    public DirectoryLoader(Context context, String rootId, Uri uri, int sortOrder) {
    public DirectoryLoader(Context context, RootInfo root, DocumentInfo doc, Uri uri) {
        super(context);
        mRootId = rootId;
        mRoot = root;
        mDoc = doc;
        mUri = uri;
        mSortOrder = sortOrder;
    }

    @Override
@@ -70,20 +84,65 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> {
            }
            mSignal = new CancellationSignal();
        }
        final DirectoryResult result = new DirectoryResult();

        final ContentResolver resolver = getContext().getContentResolver();
        final String authority = mUri.getAuthority();

        final DirectoryResult result = new DirectoryResult();

        int userMode = State.MODE_UNKNOWN;
        int userSortOrder = State.SORT_ORDER_UNKNOWN;

        // Pick up any custom modes requested by user
        Cursor cursor = null;
        try {
            final Uri stateUri = RecentsProvider.buildState(
                    mRoot.authority, mRoot.rootId, mDoc.documentId);
            cursor = resolver.query(stateUri, null, null, null, null);
            if (cursor.moveToFirst()) {
                userMode = getCursorInt(cursor, StateColumns.MODE);
                userSortOrder = getCursorInt(cursor, StateColumns.SORT_ORDER);
            }
        } finally {
            IoUtils.closeQuietly(cursor);
        }

        if (userMode != State.MODE_UNKNOWN) {
            result.mode = userMode;
        } else {
            if ((mDoc.flags & Document.FLAG_DIR_PREFERS_GRID) != 0) {
                result.mode = State.MODE_GRID;
            } else {
                result.mode = State.MODE_LIST;
            }
        }

        if (userSortOrder != State.SORT_ORDER_UNKNOWN) {
            result.sortOrder = userSortOrder;
        } else {
            if ((mDoc.flags & Document.FLAG_DIR_PREFERS_LAST_MODIFIED) != 0) {
                result.sortOrder = State.SORT_ORDER_LAST_MODIFIED;
            } else {
                result.sortOrder = State.SORT_ORDER_DISPLAY_NAME;
            }
        }

        Log.d(TAG, "userMode=" + userMode + ", userSortOrder=" + userSortOrder + " --> mode="
                + result.mode + ", sortOrder=" + result.sortOrder);

        try {
            result.client = getContext()
                    .getContentResolver().acquireUnstableContentProviderClient(authority);
            final Cursor cursor = result.client.query(
                    mUri, null, null, null, getQuerySortOrder(mSortOrder), mSignal);
            result.client = resolver.acquireUnstableContentProviderClient(authority);
            cursor = result.client.query(
                    mUri, null, null, null, getQuerySortOrder(result.sortOrder), mSignal);
            cursor.registerContentObserver(mObserver);

            final Cursor withRoot = new RootCursorWrapper(mUri.getAuthority(), mRootId, cursor, -1);
            final Cursor sorted = new SortingCursorWrapper(withRoot, mSortOrder);
            final Cursor withRoot = new RootCursorWrapper(
                    mUri.getAuthority(), mRoot.rootId, cursor, -1);
            final Cursor sorted = new SortingCursorWrapper(withRoot, result.sortOrder);

            result.cursor = sorted;
        } catch (Exception e) {
            Log.d(TAG, "Failed to query", e);
            result.exception = e;
            ContentProviderClient.closeQuietly(result.client);
        } finally {
@@ -91,6 +150,7 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> {
                mSignal = null;
            }
        }

        return result;
    }

Loading