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

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

Merge "Support sorting in storage UI."

parents 76c07a5c 14827893
Loading
Loading
Loading
Loading
+11 −1
Original line number Diff line number Diff line
@@ -12,11 +12,21 @@
            <intent-filter android:priority="100">
                <action android:name="android.intent.action.OPEN_DOCUMENT" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:mimeType="*/*" />
            </intent-filter>
            <intent-filter android:priority="100">
                <action android:name="android.intent.action.CREATE_DOCUMENT" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:mimeType="*/*" />
            </intent-filter>
        </activity>

        <!-- TODO: remove when we have real clients -->
        <activity android:name=".TestActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
+6 −3
Original line number Diff line number Diff line
@@ -18,13 +18,16 @@
    <item
        android:id="@+id/menu_grid"
        android:title="@string/menu_grid"
        android:icon="@drawable/ic_menu_grid" />
        android:icon="@drawable/ic_menu_grid"
        android:showAsAction="ifRoom" />
    <item
        android:id="@+id/menu_list"
        android:title="@string/menu_list"
        android:icon="@drawable/ic_menu_list" />
        android:icon="@drawable/ic_menu_list"
        android:showAsAction="ifRoom" />
    <item
        android:id="@+id/menu_sort"
        android:title="@string/menu_sort"
        android:icon="@drawable/ic_menu_sort" />
        android:icon="@drawable/ic_menu_sort"
        android:showAsAction="ifRoom" />
</menu>
+3 −0
Original line number Diff line number Diff line
@@ -30,4 +30,7 @@

    <string name="mode_selected_count"><xliff:g id="count" example="3">%1$d</xliff:g> selected</string>

    <string name="sort_name">Name</string>
    <string name="sort_date">Date modified</string>

</resources>
+80 −31
Original line number Diff line number Diff line
@@ -16,12 +16,17 @@

package com.android.documentsui;

import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.app.LoaderManager.LoaderCallbacks;
import android.content.Context;
import android.content.CursorLoader;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Loader;
import android.database.Cursor;
import android.net.Uri;
@@ -47,6 +52,7 @@ 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.google.android.collect.Lists;

@@ -58,7 +64,8 @@ import java.util.ArrayList;
public class DirectoryFragment extends Fragment {

    // TODO: show storage backend in item views when requested
    // TODO: implement sorting dialog

    private static final String TAG_SORT = "sort";

    private ListView mListView;
    private GridView mGridView;
@@ -71,20 +78,12 @@ public class DirectoryFragment extends Fragment {
    private int mFlags;

    private static final String EXTRA_URI = "uri";
    private static final String EXTRA_MODE = "display_mode";
    private static final String EXTRA_ALLOW_MULTIPLE = "allow_multiple";

    private static final int MODE_LIST = 1;
    private static final int MODE_GRID = 2;

    private static final int LOADER_DOCUMENTS = 2;

    public static void show(
            FragmentManager fm, Uri uri, String displayName, boolean allowMultiple) {
    public static void show(FragmentManager fm, Uri uri, String displayName) {
        final Bundle args = new Bundle();
        args.putParcelable(EXTRA_URI, uri);
        args.putInt(EXTRA_MODE, MODE_LIST);
        args.putBoolean(EXTRA_ALLOW_MULTIPLE, allowMultiple);

        final DirectoryFragment fragment = new DirectoryFragment();
        fragment.setArguments(args);
@@ -127,8 +126,18 @@ public class DirectoryFragment extends Fragment {
        mCallbacks = new LoaderCallbacks<Cursor>() {
            @Override
            public Loader<Cursor> 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 = DocumentsContract.buildContentsUri(uri);
                return new CursorLoader(context, contentsUri, null, null, null, null);
                return new CursorLoader(context, contentsUri, null, null, null, sortOrder);
            }

            @Override
@@ -170,21 +179,27 @@ public class DirectoryFragment extends Fragment {
    @Override
    public void onPrepareOptionsMenu(Menu menu) {
        super.onPrepareOptionsMenu(menu);
        final int mode = getMode();
        menu.findItem(R.id.menu_grid).setVisible(mode != MODE_GRID);
        menu.findItem(R.id.menu_list).setVisible(mode != MODE_LIST);
        final DisplayState state = getDisplayState(this);
        menu.findItem(R.id.menu_grid).setVisible(state.mode != DisplayState.MODE_GRID);
        menu.findItem(R.id.menu_list).setVisible(state.mode != DisplayState.MODE_LIST);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        final DisplayState state = getDisplayState(this);
        final int id = item.getItemId();
        if (id == R.id.menu_grid) {
            getArguments().putInt(EXTRA_MODE, MODE_GRID);
            state.mode = DisplayState.MODE_GRID;
            updateMode();
            getFragmentManager().invalidateOptionsMenu();
            return true;
        } else if (id == R.id.menu_list) {
            getArguments().putInt(EXTRA_MODE, MODE_LIST);
            state.mode = DisplayState.MODE_LIST;
            updateMode();
            getFragmentManager().invalidateOptionsMenu();
            return true;
        } else if (id == R.id.menu_sort) {
            SortFragment.show(this);
            return true;
        } else {
            return super.onOptionsItemSelected(item);
@@ -192,19 +207,19 @@ public class DirectoryFragment extends Fragment {
    }

    private void updateMode() {
        final int mode = getMode();
        final DisplayState state = getDisplayState(this);

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

        final int choiceMode;
        if (getArguments().getBoolean(EXTRA_ALLOW_MULTIPLE)) {
        if (state.allowMultiple) {
            choiceMode = ListView.CHOICE_MODE_MULTIPLE_MODAL;
        } else {
            choiceMode = ListView.CHOICE_MODE_NONE;
        }

        if (mode == MODE_GRID) {
        if (state.mode == DisplayState.MODE_GRID) {
            mListView.setAdapter(null);
            mListView.setChoiceMode(ListView.CHOICE_MODE_NONE);
            mGridView.setAdapter(mAdapter);
@@ -212,15 +227,21 @@ public class DirectoryFragment extends Fragment {
            mGridView.setNumColumns(GridView.AUTO_FIT);
            mGridView.setChoiceMode(choiceMode);
            mCurrentView = mGridView;
        } else {
        } else if (state.mode == DisplayState.MODE_LIST) {
            mGridView.setAdapter(null);
            mGridView.setChoiceMode(ListView.CHOICE_MODE_NONE);
            mListView.setAdapter(mAdapter);
            mListView.setChoiceMode(choiceMode);
            mCurrentView = mListView;
        } else {
            throw new IllegalStateException();
        }
    }

    private void updateSortBy() {
        getLoaderManager().restartLoader(LOADER_DOCUMENTS, getArguments(), mCallbacks);
    }

    private OnItemClickListener mItemListener = new OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
@@ -288,12 +309,8 @@ public class DirectoryFragment extends Fragment {
        }
    };

    private boolean getSupportsCreate() {
        return (mFlags & DocumentsContract.FLAG_SUPPORTS_CREATE) != 0;
    }

    private int getMode() {
        return getArguments().getInt(EXTRA_MODE, MODE_LIST);
    private static DisplayState getDisplayState(Fragment fragment) {
        return ((DocumentsActivity) fragment.getActivity()).getDisplayState();
    }

    private class DocumentsAdapter extends CursorAdapter {
@@ -304,10 +321,10 @@ public class DirectoryFragment extends Fragment {
        @Override
        public View newView(Context context, Cursor cursor, ViewGroup parent) {
            final LayoutInflater inflater = LayoutInflater.from(context);
            final int mode = getMode();
            if (mode == MODE_LIST) {
            final DisplayState state = getDisplayState(DirectoryFragment.this);
            if (state.mode == DisplayState.MODE_LIST) {
                return inflater.inflate(R.layout.item_doc_list, parent, false);
            } else if (mode == MODE_GRID) {
            } else if (state.mode == DisplayState.MODE_GRID) {
                return inflater.inflate(R.layout.item_doc_grid, parent, false);
            } else {
                throw new IllegalStateException();
@@ -341,6 +358,38 @@ public class DirectoryFragment extends Fragment {
        }
    }

    public static class SortFragment extends DialogFragment {
        public static void show(DirectoryFragment parent) {
            if (!parent.isAdded()) return;

            final SortFragment dialog = new SortFragment();
            dialog.setTargetFragment(parent, 0);
            dialog.show(parent.getFragmentManager(), TAG_SORT);
        }

        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            final Context context = getActivity();
            final DisplayState state = getDisplayState(this);

            final AlertDialog.Builder builder = new AlertDialog.Builder(context);
            builder.setTitle(R.string.menu_sort);
            builder.setSingleChoiceItems(new CharSequence[] {
                    getText(R.string.sort_name),
                    getText(R.string.sort_date),
            }, state.sortBy, new OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    state.sortBy = which;
                    ((DirectoryFragment) getTargetFragment()).updateSortBy();
                    dismiss();
                }
            });

            return builder.create();
        }
    }

    private static int getDocumentFlags(Context context, Uri uri) {
        final Cursor cursor = context.getContentResolver().query(uri, new String[] {
                DocumentColumns.FLAGS }, null, null, null);
+62 −18
Original line number Diff line number Diff line
@@ -55,13 +55,14 @@ public class DocumentsActivity extends Activity {
    // TODO: fragment to show recently opened documents
    // TODO: pull actionbar icon from current backend

    private static final int MODE_OPEN = 1;
    private static final int MODE_CREATE = 2;
    private static final int ACTION_OPEN = 1;
    private static final int ACTION_CREATE = 2;

    private int mMode;
    private boolean mAllowMultiple;
    private int mAction;
    private String[] mAcceptMimes;

    private final DisplayState mDisplayState = new DisplayState();

    private boolean mIgnoreNextNavigation;

    private Uri mCurrentDir;
@@ -74,11 +75,11 @@ public class DocumentsActivity extends Activity {
        final Intent intent = getIntent();
        final String action = intent.getAction();
        if (Intent.ACTION_OPEN_DOCUMENT.equals(action)) {
            mMode = MODE_OPEN;
            mAllowMultiple = intent.getBooleanExtra(Intent.EXTRA_ALLOW_MULTIPLE, false);
            mAction = ACTION_OPEN;
            mDisplayState.allowMultiple = intent.getBooleanExtra(Intent.EXTRA_ALLOW_MULTIPLE, false);
        } else if (Intent.ACTION_CREATE_DOCUMENT.equals(action)) {
            mMode = MODE_CREATE;
            mAllowMultiple = false;
            mAction = ACTION_CREATE;
            mDisplayState.allowMultiple = false;
        }

        if (intent.hasExtra(Intent.EXTRA_MIME_TYPES)) {
@@ -87,6 +88,12 @@ public class DocumentsActivity extends Activity {
            mAcceptMimes = new String[] { intent.getType() };
        }

        if (mimeMatches("image/*", mAcceptMimes)) {
            mDisplayState.mode = DisplayState.MODE_GRID;
        } else {
            mDisplayState.mode = DisplayState.MODE_LIST;
        }

        setResult(Activity.RESULT_CANCELED);
        setContentView(R.layout.activity);

@@ -95,7 +102,7 @@ public class DocumentsActivity extends Activity {

        updateActionBar();

        if (mMode == MODE_CREATE) {
        if (mAction == ACTION_CREATE) {
            final String mimeType = getIntent().getType();
            final String title = getIntent().getStringExtra(Intent.EXTRA_TITLE);
            SaveFragment.show(getFragmentManager(), mimeType, title);
@@ -120,9 +127,9 @@ public class DocumentsActivity extends Activity {
            actionBar.setDisplayShowHomeEnabled(false);
            actionBar.setDisplayHomeAsUpEnabled(false);

            if (mMode == MODE_OPEN) {
            if (mAction == ACTION_OPEN) {
                actionBar.setTitle(R.string.title_open);
            } else if (mMode == MODE_CREATE) {
            } else if (mAction == ACTION_CREATE) {
                actionBar.setTitle(R.string.title_save);
            }
        }
@@ -140,7 +147,7 @@ public class DocumentsActivity extends Activity {
        super.onPrepareOptionsMenu(menu);

        final MenuItem createDir = menu.findItem(R.id.menu_create_dir);
        createDir.setVisible(mMode == MODE_CREATE);
        createDir.setVisible(mAction == ACTION_CREATE);
        createDir.setEnabled(mCurrentSupportsCreate);

        return true;
@@ -209,11 +216,15 @@ public class DocumentsActivity extends Activity {
        }
    };

    public DisplayState getDisplayState() {
        return mDisplayState;
    }

    public void onDirectoryChanged(Uri uri, int flags) {
        mCurrentDir = uri;
        mCurrentSupportsCreate = (flags & DocumentsContract.FLAG_SUPPORTS_CREATE) != 0;

        if (mMode == MODE_CREATE) {
        if (mAction == ACTION_CREATE) {
            final FragmentManager fm = getFragmentManager();
            SaveFragment.get(fm).setSaveEnabled(mCurrentSupportsCreate);
        }
@@ -225,18 +236,18 @@ public class DocumentsActivity extends Activity {
        final Uri uri = DocumentsContract.buildDocumentUri(
                info.authority, DocumentsContract.ROOT_GUID);
        final CharSequence displayName = info.loadLabel(getPackageManager());
        DirectoryFragment.show(getFragmentManager(), uri, displayName.toString(), mAllowMultiple);
        DirectoryFragment.show(getFragmentManager(), uri, displayName.toString());
    }

    public void onDocumentPicked(Document doc) {
        final FragmentManager fm = getFragmentManager();
        if (DocumentsContract.MIME_TYPE_DIRECTORY.equals(doc.mimeType)) {
            // Nested directory picked, recurse using new fragment
            DirectoryFragment.show(fm, doc.uri, doc.displayName, mAllowMultiple);
        } else if (mMode == MODE_OPEN) {
            DirectoryFragment.show(fm, doc.uri, doc.displayName);
        } else if (mAction == ACTION_OPEN) {
            // Explicit file picked, return
            onFinished(doc.uri);
        } else if (mMode == MODE_CREATE) {
        } else if (mAction == ACTION_CREATE) {
            // Overwrite current filename
            SaveFragment.get(fm).setDisplayName(doc.displayName);
        }
@@ -274,7 +285,7 @@ public class DocumentsActivity extends Activity {

        intent.addFlags(
                Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_PERSIST_GRANT_URI_PERMISSION);
        if (mMode == MODE_CREATE) {
        if (mAction == ACTION_CREATE) {
            intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
        }

@@ -282,6 +293,18 @@ public class DocumentsActivity extends Activity {
        finish();
    }

    public static class DisplayState {
        public int mode;
        public int sortBy;
        public boolean allowMultiple;

        public static final int MODE_LIST = 0;
        public static final int MODE_GRID = 1;

        public static final int SORT_BY_NAME = 0;
        public static final int SORT_BY_DATE = 1;
    }

    public static class Document {
        public Uri uri;
        public String mimeType;
@@ -297,6 +320,27 @@ public class DocumentsActivity extends Activity {
        }
    }

    public static boolean mimeMatches(String filter, String[] tests) {
        for (String test : tests) {
            if (mimeMatches(filter, test)) {
                return true;
            }
        }
        return false;
    }

    public static boolean mimeMatches(String filter, String test) {
        if (filter.equals(test)) {
            return true;
        } else if ("*/*".equals(filter)) {
            return true;
        } else if (filter.endsWith("/*")) {
            return filter.regionMatches(0, test, 0, filter.indexOf('/'));
        } else {
            return false;
        }
    }

    public static Drawable resolveDocumentIcon(Context context, String mimeType) {
        // TODO: allow backends to provide custom MIME icons
        if (DocumentsContract.MIME_TYPE_DIRECTORY.equals(mimeType)) {
Loading