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

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

Merge "More recents work; filtering and sorting."

parents a8b3f7b6 ef7184a1
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -39,7 +39,7 @@

    <ListView
        android:id="@+id/roots_list"
        android:layout_width="300dp"
        android:layout_width="250dp"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:background="#fff" />
+2 −0
Original line number Diff line number Diff line
@@ -39,4 +39,6 @@

    <string name="save_error">Failed to save document</string>

    <string name="root_recent">Recent</string>

</resources>
+91 −79
Original line number Diff line number Diff line
@@ -21,13 +21,10 @@ import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.app.LoaderManager.LoaderCallbacks;
import android.content.Context;
import android.content.CursorLoader;
import android.content.Loader;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.DocumentColumns;
import android.text.format.DateUtils;
import android.util.SparseBooleanArray;
import android.view.ActionMode;
@@ -41,17 +38,20 @@ import android.widget.AbsListView;
import android.widget.AbsListView.MultiChoiceModeListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.CursorAdapter;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;

import com.android.documentsui.DocumentsActivity.DisplayState;
import com.android.documentsui.DocumentsActivity.Document;
import com.android.documentsui.model.Document;
import com.android.internal.util.Predicate;
import com.google.android.collect.Lists;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

/**
 * Display the documents inside a single directory.
@@ -59,23 +59,21 @@ import java.util.ArrayList;
public class DirectoryFragment extends Fragment {

    // TODO: show storage backend in item views when requested
    // TODO: apply sort order locally
    // TODO: apply MIME filtering locally

    private ListView mListView;
    private GridView mGridView;

    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;
    public static final int TYPE_NORMAL = 1;
    public static final int TYPE_SEARCH = 2;
    public static final int TYPE_RECENT_OPEN = 3;
    public static final int TYPE_RECENT_CREATE = 4;

    private int mType = TYPE_NORMAL;

    private DocumentsAdapter mAdapter;
    private LoaderCallbacks<Cursor> mCallbacks;
    private LoaderCallbacks<List<Document>> mCallbacks;

    private static final String EXTRA_URI = "uri";

@@ -93,6 +91,11 @@ public class DirectoryFragment extends Fragment {
        ft.commitAllowingStateLoss();
    }

    public static DirectoryFragment get(FragmentManager fm) {
        // TODO: deal with multiple directories shown at once
        return (DirectoryFragment) fm.findFragmentById(R.id.directory);
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
@@ -114,7 +117,7 @@ public class DirectoryFragment extends Fragment {
        mGridView.setOnItemClickListener(mItemListener);
        mGridView.setMultiChoiceModeListener(mMultiListener);

        mAdapter = new DocumentsAdapter(context);
        mAdapter = new DocumentsAdapter();
        updateMode();

        final Uri uri = getArguments().getParcelable(EXTRA_URI);
@@ -129,18 +132,10 @@ public class DirectoryFragment extends Fragment {
            mType = TYPE_NORMAL;
        }

        mCallbacks = new LoaderCallbacks<Cursor>() {
        mCallbacks = new LoaderCallbacks<List<Document>>() {
            @Override
            public Loader<Cursor> onCreateLoader(int id, Bundle args) {
            public Loader<List<Document>> onCreateLoader(int id, Bundle args) {
                final DisplayState state = getDisplayState(DirectoryFragment.this);
                final String sortOrder;
                if (state.sortBy == DisplayState.SORT_BY_NAME) {
                    sortOrder = DocumentColumns.DISPLAY_NAME + " ASC";
                } else if (state.sortBy == DisplayState.SORT_BY_DATE) {
                    sortOrder = DocumentColumns.LAST_MODIFIED + " DESC";
                } else {
                    sortOrder = null;
                }

                final Uri contentsUri;
                if (mType == TYPE_NORMAL) {
@@ -149,17 +144,29 @@ public class DirectoryFragment extends Fragment {
                    contentsUri = uri;
                }

                return new CursorLoader(context, contentsUri, null, null, null, sortOrder);
                final Predicate<Document> filter = new MimePredicate(state.acceptMimes);

                final Comparator<Document> sortOrder;
                if (state.sortOrder == DisplayState.SORT_ORDER_DATE || mType == TYPE_RECENT_OPEN
                        || mType == TYPE_RECENT_CREATE) {
                    sortOrder = new Document.DateComparator();
                } else if (state.sortOrder == DisplayState.SORT_ORDER_NAME) {
                    sortOrder = new Document.NameComparator();
                } else {
                    throw new IllegalArgumentException("Unknown sort order " + state.sortOrder);
                }

                return new DirectoryLoader(context, contentsUri, mType, filter, sortOrder);
            }

            @Override
            public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
                mAdapter.swapCursor(data);
            public void onLoadFinished(Loader<List<Document>> loader, List<Document> data) {
                mAdapter.swapDocuments(data);
            }

            @Override
            public void onLoaderReset(Loader<Cursor> loader) {
                mAdapter.swapCursor(null);
            public void onLoaderReset(Loader<List<Document>> loader) {
                mAdapter.swapDocuments(null);
            }
        };

@@ -243,16 +250,16 @@ public class DirectoryFragment extends Fragment {
        }
    }

    public void updateSortBy() {
    public void updateSortOrder() {
        getLoaderManager().restartLoader(LOADER_DOCUMENTS, getArguments(), mCallbacks);
        mListView.smoothScrollToPosition(0);
        mGridView.smoothScrollToPosition(0);
    }

    private OnItemClickListener mItemListener = new OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            final Cursor cursor = (Cursor) mAdapter.getItem(position);
            final Uri uri = getArguments().getParcelable(EXTRA_URI);
            final Document doc = Document.fromCursor(uri, cursor);
            final Document doc = mAdapter.getItem(position);
            ((DocumentsActivity) getActivity()).onDocumentPicked(doc);
        }
    };
@@ -279,8 +286,8 @@ public class DirectoryFragment extends Fragment {
                final int size = checked.size();
                for (int i = 0; i < size; i++) {
                    if (checked.valueAt(i)) {
                        final Cursor cursor = (Cursor) mAdapter.getItem(checked.keyAt(i));
                        docs.add(Document.fromCursor(uri, cursor));
                        final Document doc = mAdapter.getItem(checked.keyAt(i));
                        docs.add(doc);
                    }
                }

@@ -300,11 +307,9 @@ public class DirectoryFragment extends Fragment {
        public void onItemCheckedStateChanged(
                ActionMode mode, int position, long id, boolean checked) {
            if (checked) {
                final Cursor cursor = (Cursor) mAdapter.getItem(position);
                final String mimeType = getCursorString(cursor, DocumentColumns.MIME_TYPE);

                // Directories cannot be checked
                if (DocumentsContract.MIME_TYPE_DIRECTORY.equals(mimeType)) {
                final Document doc = mAdapter.getItem(position);
                if (DocumentsContract.MIME_TYPE_DIRECTORY.equals(doc.mimeType)) {
                    mCurrentView.setItemChecked(position, false);
                }
            }
@@ -318,61 +323,68 @@ public class DirectoryFragment extends Fragment {
        return ((DocumentsActivity) fragment.getActivity()).getDisplayState();
    }

    private class DocumentsAdapter extends CursorAdapter {
        public DocumentsAdapter(Context context) {
            super(context, null, false);
    private class DocumentsAdapter extends BaseAdapter {
        private List<Document> mDocuments;

        public DocumentsAdapter() {
        }

        public void swapDocuments(List<Document> documents) {
            mDocuments = documents;
            notifyDataSetChanged();
        }

        @Override
        public View newView(Context context, Cursor cursor, ViewGroup parent) {
        public View getView(int position, View convertView, ViewGroup parent) {
            final Context context = parent.getContext();

            if (convertView == null) {
                final LayoutInflater inflater = LayoutInflater.from(context);
                final DisplayState state = getDisplayState(DirectoryFragment.this);
                if (state.mode == DisplayState.MODE_LIST) {
                return inflater.inflate(R.layout.item_doc_list, parent, false);
                    convertView = inflater.inflate(R.layout.item_doc_list, parent, false);
                } else if (state.mode == DisplayState.MODE_GRID) {
                return inflater.inflate(R.layout.item_doc_grid, parent, false);
                    convertView = inflater.inflate(R.layout.item_doc_grid, parent, false);
                } else {
                    throw new IllegalStateException();
                }
            }

        @Override
        public void bindView(View view, Context context, Cursor cursor) {
            final TextView title = (TextView) view.findViewById(android.R.id.title);
            final TextView summary = (TextView) view.findViewById(android.R.id.summary);
            final ImageView icon = (ImageView) view.findViewById(android.R.id.icon);
            final Document doc = getItem(position);

            final String docId = getCursorString(cursor, DocumentColumns.DOC_ID);
            final String displayName = getCursorString(cursor, DocumentColumns.DISPLAY_NAME);
            final String mimeType = getCursorString(cursor, DocumentColumns.MIME_TYPE);
            final long lastModified = getCursorLong(cursor, DocumentColumns.LAST_MODIFIED);
            final int flags = getCursorInt(cursor, DocumentColumns.FLAGS);
            final TextView title = (TextView) convertView.findViewById(android.R.id.title);
            final TextView summary = (TextView) convertView.findViewById(android.R.id.summary);
            final ImageView icon = (ImageView) convertView.findViewById(android.R.id.icon);

            final Uri uri = getArguments().getParcelable(EXTRA_URI);
            if ((flags & DocumentsContract.FLAG_SUPPORTS_THUMBNAIL) != 0) {
                final Uri childUri = DocumentsContract.buildDocumentUri(uri, docId);
                icon.setImageURI(childUri);
            if (doc.isThumbnailSupported()) {
                // TODO: load thumbnails async
                icon.setImageURI(doc.uri);
            } else {
                icon.setImageDrawable(DocumentsActivity.resolveDocumentIcon(
                        context, uri.getAuthority(), mimeType));
                        context, doc.uri.getAuthority(), doc.mimeType));
            }

            title.setText(displayName);
            title.setText(doc.displayName);
            if (summary != null) {
                summary.setText(DateUtils.getRelativeTimeSpanString(lastModified));
            }
                summary.setText(DateUtils.getRelativeTimeSpanString(doc.lastModified));
            }

            return convertView;
        }

    public static String getCursorString(Cursor cursor, String columnName) {
        return cursor.getString(cursor.getColumnIndex(columnName));
        @Override
        public int getCount() {
            return mDocuments != null ? mDocuments.size() : 0;
        }

    public static long getCursorLong(Cursor cursor, String columnName) {
        return cursor.getLong(cursor.getColumnIndex(columnName));
        @Override
        public Document getItem(int position) {
            return mDocuments.get(position);
        }

    public static int getCursorInt(Cursor cursor, String columnName) {
        return cursor.getInt(cursor.getColumnIndex(columnName));
        @Override
        public long getItemId(int position) {
            return getItem(position).uri.hashCode();
        }
    }
}
+90 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2013 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.documentsui;

import static com.android.documentsui.DirectoryFragment.TYPE_NORMAL;
import static com.android.documentsui.DirectoryFragment.TYPE_RECENT_OPEN;
import static com.android.documentsui.DirectoryFragment.TYPE_SEARCH;

import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.CancellationSignal;

import com.android.documentsui.model.Document;
import com.android.internal.util.Predicate;
import com.google.android.collect.Lists;

import libcore.io.IoUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class DirectoryLoader extends UriDerivativeLoader<List<Document>> {

    private final int mType;
    private Predicate<Document> mFilter;
    private Comparator<Document> mSortOrder;

    public DirectoryLoader(Context context, Uri uri, int type, Predicate<Document> filter,
            Comparator<Document> sortOrder) {
        super(context, uri);
        mType = type;
        mFilter = filter;
        mSortOrder = sortOrder;
    }

    @Override
    public List<Document> loadInBackground(Uri uri, CancellationSignal signal) {
        final ArrayList<Document> result = Lists.newArrayList();

        // TODO: send selection and sorting hints to backend
        final ContentResolver resolver = getContext().getContentResolver();
        final Cursor cursor = resolver.query(uri, null, null, null, null, signal);
        try {
            while (cursor != null && cursor.moveToNext()) {
                final Document doc;
                switch (mType) {
                    case TYPE_NORMAL:
                    case TYPE_SEARCH:
                        doc = Document.fromDirectoryCursor(uri, cursor);
                        break;
                    case TYPE_RECENT_OPEN:
                        doc = Document.fromRecentOpenCursor(resolver, cursor);
                        break;
                    default:
                        throw new IllegalArgumentException("Unknown type");
                }

                if (mFilter == null || mFilter.apply(doc)) {
                    result.add(doc);
                }
            }
        } finally {
            IoUtils.closeQuietly(cursor);
        }

        if (mSortOrder != null) {
            Collections.sort(result, mSortOrder);
        }

        return result;
    }
}
+81 −257

File changed.

Preview size limit exceeded, changes collapsed.

Loading