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

Commit dc2963ae authored by Jeff Sharkey's avatar Jeff Sharkey
Browse files

Track and persist directory stacks; recents work.

Move to manual tracking of directory navigation stack so we have Uri
data to persist, instead of opaque fragment backstack.  Remember
directory stacks across launches on a per-app basis.

Start recording recently opened and created files.  Uniform Uri
parameter extraction utility methods in contract.

Change-Id: I79ed30ee10272bf7c53d339e797639c993f649bb
parent 92d7e697
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -20266,12 +20266,17 @@ package android.provider {
  public final class DocumentsContract {
    ctor public DocumentsContract();
    method public static android.net.Uri buildContentsUri(java.lang.String, java.lang.String, java.lang.String);
    method public static android.net.Uri buildContentsUri(android.net.Uri);
    method public static android.net.Uri buildDocumentUri(java.lang.String, java.lang.String, java.lang.String);
    method public static android.net.Uri buildDocumentUri(android.net.Uri, java.lang.String);
    method public static android.net.Uri buildRootUri(java.lang.String, java.lang.String);
    method public static android.net.Uri buildRootsUri(java.lang.String);
    method public static android.net.Uri buildSearchUri(java.lang.String, java.lang.String, java.lang.String, java.lang.String);
    method public static android.net.Uri buildSearchUri(android.net.Uri, java.lang.String);
    method public static java.lang.String getDocId(android.net.Uri);
    method public static java.lang.String getRootId(android.net.Uri);
    method public static java.lang.String getSearchQuery(android.net.Uri);
    method public static android.graphics.Bitmap getThumbnail(android.content.ContentResolver, android.net.Uri, android.graphics.Point);
    method public static boolean renameDocument(android.content.ContentResolver, android.net.Uri, java.lang.String);
    field public static final java.lang.String EXTRA_HAS_MORE = "has_more";
+52 −16
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ import libcore.io.IoUtils;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

/**
 * The contract between a storage backend and the platform. Contains definitions
@@ -152,36 +153,71 @@ public final class DocumentsContract {
                .authority(authority).appendPath(PATH_ROOTS).appendPath(rootId).build();
    }

    /**
     * Build URI representing the given {@link DocumentColumns#DOC_ID} in a
     * storage root.
     */
    public static Uri buildDocumentUri(String authority, String rootId, String docId) {
        return buildDocumentUri(buildRootUri(authority, rootId), docId);
        return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(authority)
                .appendPath(PATH_ROOTS).appendPath(rootId).appendPath(PATH_DOCS).appendPath(docId)
                .build();
    }

    /**
     * Build URI representing the given {@link DocumentColumns#DOC_ID} in a
     * storage root.
     * Build URI representing the contents of the given directory in a storage
     * backend. The given document must be {@link #MIME_TYPE_DIRECTORY}.
     */
    public static Uri buildDocumentUri(Uri rootUri, String docId) {
        return rootUri.buildUpon().appendPath(PATH_DOCS).appendPath(docId).build();
    public static Uri buildContentsUri(String authority, String rootId, String docId) {
        return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(authority)
                .appendPath(PATH_ROOTS).appendPath(rootId).appendPath(PATH_DOCS).appendPath(docId)
                .appendPath(PATH_CONTENTS).build();
    }

    /**
     * Build URI representing a search for matching documents under a directory
     * in a storage backend.
     *
     * @param documentUri directory to search under, which must have
     *            {@link #FLAG_SUPPORTS_SEARCH}.
     */
    public static Uri buildSearchUri(Uri documentUri, String query) {
        return documentUri.buildUpon()
    public static Uri buildSearchUri(String authority, String rootId, String docId, String query) {
        return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(authority)
                .appendPath(PATH_ROOTS).appendPath(rootId).appendPath(PATH_DOCS).appendPath(docId)
                .appendPath(PATH_SEARCH).appendQueryParameter(PARAM_QUERY, query).build();
    }

    /**
     * Build URI representing the contents of the given directory in a storage
     * backend. The given document must be {@link #MIME_TYPE_DIRECTORY}.
     */
    public static Uri buildContentsUri(Uri documentUri) {
        return documentUri.buildUpon().appendPath(PATH_CONTENTS).build();
    public static Uri buildDocumentUri(Uri relatedUri, String docId) {
        return buildDocumentUri(relatedUri.getAuthority(), getRootId(relatedUri), docId);
    }

    public static Uri buildContentsUri(Uri relatedUri) {
        return buildContentsUri(
                relatedUri.getAuthority(), getRootId(relatedUri), getDocId(relatedUri));
    }

    public static Uri buildSearchUri(Uri relatedUri, String query) {
        return buildSearchUri(
                relatedUri.getAuthority(), getRootId(relatedUri), getDocId(relatedUri), query);
    }

    public static String getRootId(Uri documentUri) {
        final List<String> paths = documentUri.getPathSegments();
        if (!PATH_ROOTS.equals(paths.get(0))) {
            throw new IllegalArgumentException();
        }
        return paths.get(1);
    }

    public static String getDocId(Uri documentUri) {
        final List<String> paths = documentUri.getPathSegments();
        if (!PATH_ROOTS.equals(paths.get(0))) {
            throw new IllegalArgumentException();
        }
        if (!PATH_DOCS.equals(paths.get(2))) {
            throw new IllegalArgumentException();
        }
        return paths.get(3);
    }

    public static String getSearchQuery(Uri documentUri) {
        return documentUri.getQueryParameter(PARAM_QUERY);
    }

    /**
+32 −46
Original line number Diff line number Diff line
@@ -67,31 +67,29 @@ public class DirectoryFragment extends Fragment {

    private AbsListView mCurrentView;

    private static final int TYPE_NORMAL = 1;
    private static final int TYPE_SEARCH = 2;
    private static final int TYPE_RECENT_OPEN = 3;
    private static final int TYPE_RECENT_CREATE = 4;

    private int mType = TYPE_NORMAL;

    private DocumentsAdapter mAdapter;
    private LoaderCallbacks<Cursor> mCallbacks;

    private int mFlags;

    private static final String EXTRA_ROOT_URI = "rootUri";
    private static final String EXTRA_DOCS_URI = "docsUri";
    private static final String EXTRA_URI = "uri";

    private static final int LOADER_DOCUMENTS = 2;

    public static void show(FragmentManager fm, Uri rootUri, Uri docsUri, String displayName,
            boolean addToBackStack) {
    public static void show(FragmentManager fm, Uri uri) {
        final Bundle args = new Bundle();
        args.putParcelable(EXTRA_ROOT_URI, rootUri);
        args.putParcelable(EXTRA_DOCS_URI, docsUri);
        args.putParcelable(EXTRA_URI, uri);

        final DirectoryFragment fragment = new DirectoryFragment();
        fragment.setArguments(args);

        final FragmentTransaction ft = fm.beginTransaction();
        ft.replace(R.id.directory, fragment);
        if (addToBackStack) {
            ft.addToBackStack(displayName);
        }
        ft.setBreadCrumbTitle(displayName);
        ft.commitAllowingStateLoss();
    }

@@ -119,9 +117,17 @@ public class DirectoryFragment extends Fragment {
        mAdapter = new DocumentsAdapter(context);
        updateMode();

        // TODO: migrate flags query to loader
        final Uri docsUri = getArguments().getParcelable(EXTRA_DOCS_URI);
        mFlags = getDocumentFlags(context, docsUri);
        final Uri uri = getArguments().getParcelable(EXTRA_URI);

        if (uri.getQueryParameter(DocumentsContract.PARAM_QUERY) != null) {
            mType = TYPE_SEARCH;
        } else if (RecentsProvider.buildRecentOpen().equals(uri)) {
            mType = TYPE_RECENT_OPEN;
        } else if (RecentsProvider.buildRecentCreate().equals(uri)) {
            mType = TYPE_RECENT_CREATE;
        } else {
            mType = TYPE_NORMAL;
        }

        mCallbacks = new LoaderCallbacks<Cursor>() {
            @Override
@@ -137,10 +143,10 @@ public class DirectoryFragment extends Fragment {
                }

                final Uri contentsUri;
                if (docsUri.getQueryParameter(DocumentsContract.PARAM_QUERY) != null) {
                    contentsUri = docsUri;
                if (mType == TYPE_NORMAL) {
                    contentsUri = DocumentsContract.buildContentsUri(uri);
                } else {
                    contentsUri = DocumentsContract.buildContentsUri(docsUri);
                    contentsUri = uri;
                }

                return new CursorLoader(context, contentsUri, null, null, null, sortOrder);
@@ -164,10 +170,6 @@ public class DirectoryFragment extends Fragment {
    public void onStart() {
        super.onStart();
        getLoaderManager().restartLoader(LOADER_DOCUMENTS, getArguments(), mCallbacks);

        // TODO: clean up tracking of current directory
        final Uri docsUri = getArguments().getParcelable(EXTRA_DOCS_URI);
        ((DocumentsActivity) getActivity()).onDirectoryChanged(docsUri, mFlags);
    }

    @Override
@@ -249,8 +251,8 @@ public class DirectoryFragment extends Fragment {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            final Cursor cursor = (Cursor) mAdapter.getItem(position);
            final Uri rootUri = getArguments().getParcelable(EXTRA_ROOT_URI);
            final Document doc = Document.fromCursor(rootUri, cursor);
            final Uri uri = getArguments().getParcelable(EXTRA_URI);
            final Document doc = Document.fromCursor(uri, cursor);
            ((DocumentsActivity) getActivity()).onDocumentPicked(doc);
        }
    };
@@ -270,7 +272,7 @@ public class DirectoryFragment extends Fragment {
        @Override
        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
            if (item.getItemId() == R.id.menu_open) {
                final Uri rootUri = getArguments().getParcelable(EXTRA_ROOT_URI);
                final Uri uri = getArguments().getParcelable(EXTRA_URI);
                final SparseBooleanArray checked = mCurrentView.getCheckedItemPositions();
                final ArrayList<Document> docs = Lists.newArrayList();

@@ -278,7 +280,7 @@ public class DirectoryFragment extends Fragment {
                for (int i = 0; i < size; i++) {
                    if (checked.valueAt(i)) {
                        final Cursor cursor = (Cursor) mAdapter.getItem(checked.keyAt(i));
                        docs.add(Document.fromCursor(rootUri, cursor));
                        docs.add(Document.fromCursor(uri, cursor));
                    }
                }

@@ -346,15 +348,13 @@ public class DirectoryFragment extends Fragment {
            final long lastModified = getCursorLong(cursor, DocumentColumns.LAST_MODIFIED);
            final int flags = getCursorInt(cursor, DocumentColumns.FLAGS);

            final Uri rootUri = getArguments().getParcelable(EXTRA_ROOT_URI);
            final String authority = rootUri.getAuthority();

            final Uri uri = getArguments().getParcelable(EXTRA_URI);
            if ((flags & DocumentsContract.FLAG_SUPPORTS_THUMBNAIL) != 0) {
                final Uri childUri = DocumentsContract.buildDocumentUri(rootUri, docId);
                final Uri childUri = DocumentsContract.buildDocumentUri(uri, docId);
                icon.setImageURI(childUri);
            } else {
                icon.setImageDrawable(
                        DocumentsActivity.resolveDocumentIcon(context, authority, mimeType));
                icon.setImageDrawable(DocumentsActivity.resolveDocumentIcon(
                        context, uri.getAuthority(), mimeType));
            }

            title.setText(displayName);
@@ -364,20 +364,6 @@ public class DirectoryFragment extends Fragment {
        }
    }

    private static int getDocumentFlags(Context context, Uri uri) {
        final Cursor cursor = context.getContentResolver().query(uri, new String[] {
                DocumentColumns.FLAGS }, null, null, null);
        try {
            if (cursor.moveToFirst()) {
                return getCursorInt(cursor, DocumentColumns.FLAGS);
            } else {
                return 0;
            }
        } finally {
            cursor.close();
        }
    }

    public static String getCursorString(Cursor cursor, String columnName) {
        return cursor.getString(cursor.getColumnIndex(columnName));
    }
+205 −80

File changed.

Preview size limit exceeded, changes collapsed.

+29 −6
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.documentsui;

import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
@@ -30,6 +31,9 @@ import android.util.Log;
public class RecentsProvider extends ContentProvider {
    private static final String TAG = "RecentsProvider";

    // TODO: offer view of recents that handles backend root resolution before
    // returning cursor, include extra columns

    public static final String AUTHORITY = "com.android.documentsui.recents";

    private static final UriMatcher sMatcher = new UriMatcher(UriMatcher.NO_MATCH);
@@ -53,13 +57,29 @@ public class RecentsProvider extends ContentProvider {
     * starting with root.
     */
    public static final String COL_PATH = "path";
    public static final String COL_URI = "uri";
    public static final String COL_PACKAGE_NAME = "package_name";
    public static final String COL_TIMESTAMP = "timestamp";

    public static Uri buildRecentOpen() {
        return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
                .authority(AUTHORITY).appendPath("recent_open").build();
    }

    public static Uri buildRecentCreate() {
        return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
                .authority(AUTHORITY).appendPath("recent_create").build();
    }

    public static Uri buildResume(String packageName) {
        return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
                .authority(AUTHORITY).appendPath("resume").appendPath(packageName).build();
    }

    private DatabaseHelper mHelper;

    private static class DatabaseHelper extends SQLiteOpenHelper {
        private static final String DB_NAME = "recents";
        private static final String DB_NAME = "recents.db";

        private static final int VERSION_INIT = 1;

@@ -70,19 +90,19 @@ public class RecentsProvider extends ContentProvider {
        @Override
        public void onCreate(SQLiteDatabase db) {
            db.execSQL("CREATE TABLE " + TABLE_RECENT_OPEN + " (" +
                    COL_PATH + " TEXT," +
                    COL_TIMESTAMP + " INTEGER," +
                    COL_URI + " TEXT PRIMARY KEY ON CONFLICT REPLACE," +
                    COL_TIMESTAMP + " INTEGER" +
                    ")");

            db.execSQL("CREATE TABLE " + TABLE_RECENT_CREATE + " (" +
                    COL_PATH + " TEXT," +
                    COL_TIMESTAMP + " INTEGER," +
                    COL_PATH + " TEXT PRIMARY KEY ON CONFLICT REPLACE," +
                    COL_TIMESTAMP + " INTEGER" +
                    ")");

            db.execSQL("CREATE TABLE " + TABLE_RESUME + " (" +
                    COL_PACKAGE_NAME + " TEXT PRIMARY KEY ON CONFLICT REPLACE," +
                    COL_PATH + " TEXT," +
                    COL_TIMESTAMP + " INTEGER," +
                    COL_TIMESTAMP + " INTEGER" +
                    ")");
        }

@@ -136,11 +156,13 @@ public class RecentsProvider extends ContentProvider {
        final SQLiteDatabase db = mHelper.getWritableDatabase();
        switch (sMatcher.match(uri)) {
            case URI_RECENT_OPEN: {
                values.put(COL_TIMESTAMP, System.currentTimeMillis());
                db.insert(TABLE_RECENT_OPEN, null, values);
                db.delete(TABLE_RECENT_OPEN, buildWhereOlder(DateUtils.WEEK_IN_MILLIS), null);
                return uri;
            }
            case URI_RECENT_CREATE: {
                values.put(COL_TIMESTAMP, System.currentTimeMillis());
                db.insert(TABLE_RECENT_CREATE, null, values);
                db.delete(TABLE_RECENT_CREATE, buildWhereOlder(DateUtils.WEEK_IN_MILLIS), null);
                return uri;
@@ -148,6 +170,7 @@ public class RecentsProvider extends ContentProvider {
            case URI_RESUME: {
                final String packageName = uri.getPathSegments().get(1);
                values.put(COL_PACKAGE_NAME, packageName);
                values.put(COL_TIMESTAMP, System.currentTimeMillis());
                db.insert(TABLE_RESUME, null, values);
                return uri;
            }
Loading