Iterate on storage UI.
Support both grid and list view of documents. Show breadcrumb
navigation trail in action bar. Start supporting file and directory
creation.
Change-Id: I93a973da7b0d4387a57fe719e7bb20944adb0290
diff --git a/src/com/android/documentsui/DirectoryFragment.java b/src/com/android/documentsui/DirectoryFragment.java
index d43abde..5ba1930 100644
--- a/src/com/android/documentsui/DirectoryFragment.java
+++ b/src/com/android/documentsui/DirectoryFragment.java
@@ -16,9 +16,9 @@
package com.android.documentsui;
+import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
-import android.app.ListFragment;
import android.app.LoaderManager.LoaderCallbacks;
import android.content.Context;
import android.content.CursorLoader;
@@ -28,49 +28,94 @@
import android.os.Bundle;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.DocumentColumns;
+import android.text.format.DateUtils;
import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
import android.widget.CursorAdapter;
+import android.widget.GridView;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
-public class DirectoryFragment extends ListFragment {
+import com.android.documentsui.DocumentsActivity.Document;
+
+/**
+ * Display the documents inside a single directory.
+ */
+public class DirectoryFragment extends Fragment {
+
+ // TODO: show storage backend in item views when requested
+ // TODO: implement sorting dialog
+ // TODO: support multiple selection with actionmode
+
+ private ListView mListView;
+ private GridView mGridView;
+
private DocumentsAdapter mAdapter;
private LoaderCallbacks<Cursor> mCallbacks;
+ private int mFlags;
+
private static final String EXTRA_URI = "uri";
+ private static final String EXTRA_MODE = "display_mode";
+
+ 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, CharSequence title) {
+ 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);
final DirectoryFragment fragment = new DirectoryFragment();
fragment.setArguments(args);
final FragmentTransaction ft = fm.beginTransaction();
- ft.replace(android.R.id.content, fragment);
- ft.addToBackStack(title.toString());
- ft.setBreadCrumbTitle(title);
+ ft.replace(R.id.directory, fragment);
+ ft.addToBackStack(displayName);
+ ft.setBreadCrumbTitle(displayName);
ft.commitAllowingStateLoss();
}
@Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setHasOptionsMenu(true);
+ }
+
+ @Override
public View onCreateView(
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final Context context = inflater.getContext();
+ final View view = inflater.inflate(R.layout.fragment_directory, container, false);
+
+ mListView = (ListView) view.findViewById(R.id.list);
+ mListView.setOnItemClickListener(mItemListener);
+
+ mGridView = (GridView) view.findViewById(R.id.grid);
+ mGridView.setOnItemClickListener(mItemListener);
+
mAdapter = new DocumentsAdapter(context);
- setListAdapter(mAdapter);
+ updateMode();
+
+ // TODO: migrate flags query to loader
+ final Uri uri = getArguments().getParcelable(EXTRA_URI);
+ mFlags = getDocumentFlags(context, uri);
mCallbacks = new LoaderCallbacks<Cursor>() {
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
- final Uri uri = args.getParcelable(EXTRA_URI);
- return new CursorLoader(context, uri, null, null, null, null);
+ final Uri contentsUri = DocumentsContract.buildContentsUri(uri);
+ return new CursorLoader(context, contentsUri, null, null, null, null);
}
@Override
@@ -84,13 +129,17 @@
}
};
- return super.onCreateView(inflater, container, savedInstanceState);
+ return view;
}
@Override
public void onStart() {
super.onStart();
getLoaderManager().restartLoader(LOADER_DOCUMENTS, getArguments(), mCallbacks);
+
+ // TODO: clean up tracking of current directory
+ final Uri uri = getArguments().getParcelable(EXTRA_URI);
+ ((DocumentsActivity) getActivity()).onDirectoryChanged(uri, mFlags);
}
@Override
@@ -100,26 +149,70 @@
}
@Override
- public void onListItemClick(ListView l, View v, int position, long id) {
- final Cursor cursor = (Cursor) mAdapter.getItem(position);
- final String guid = getCursorString(cursor, DocumentColumns.GUID);
- final String mimeType = getCursorString(cursor, DocumentColumns.MIME_TYPE);
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ super.onCreateOptionsMenu(menu, inflater);
+ inflater.inflate(R.menu.directory, menu);
+ }
- final Uri uri = getArguments().getParcelable(EXTRA_URI);
- final Uri childUri = DocumentsContract.buildDocumentUri(uri.getAuthority(), guid);
+ @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);
+ }
- if (DocumentsContract.MIME_TYPE_DIRECTORY.equals(mimeType)) {
- // Nested directory picked, recurse using new fragment
- final Uri childContentsUri = DocumentsContract.buildContentsUri(childUri);
- final String displayName = cursor.getString(
- cursor.getColumnIndex(DocumentColumns.DISPLAY_NAME));
- DirectoryFragment.show(getFragmentManager(), childContentsUri, displayName);
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ final int id = item.getItemId();
+ if (id == R.id.menu_grid) {
+ getArguments().putInt(EXTRA_MODE, MODE_GRID);
+ updateMode();
+ return true;
+ } else if (id == R.id.menu_list) {
+ getArguments().putInt(EXTRA_MODE, MODE_LIST);
+ updateMode();
+ return true;
} else {
- // Explicit file picked, return
- ((DocumentsActivity) getActivity()).onDocumentPicked(childUri);
+ return super.onOptionsItemSelected(item);
}
}
+ private void updateMode() {
+ final int mode = getMode();
+
+ mListView.setVisibility(mode == MODE_LIST ? View.VISIBLE : View.GONE);
+ mGridView.setVisibility(mode == MODE_GRID ? View.VISIBLE : View.GONE);
+
+ if (mode == MODE_GRID) {
+ mListView.setAdapter(null);
+ mGridView.setAdapter(mAdapter);
+ mGridView.setNumColumns(GridView.AUTO_FIT);
+ } else {
+ mGridView.setAdapter(null);
+ mListView.setAdapter(mAdapter);
+ }
+ }
+
+ 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.getAuthority(), cursor);
+ ((DocumentsActivity) getActivity()).onDocumentPicked(doc);
+ }
+ };
+
+ private boolean getSupportsCreate() {
+ return (mFlags & DocumentsContract.FLAG_SUPPORTS_CREATE) != 0;
+ }
+
+ private int getMode() {
+ return getArguments().getInt(EXTRA_MODE, MODE_LIST);
+ }
+
private class DocumentsAdapter extends CursorAdapter {
public DocumentsAdapter(Context context) {
super(context, null, false);
@@ -127,8 +220,15 @@
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
- return LayoutInflater.from(context)
- .inflate(com.android.internal.R.layout.preference, parent, false);
+ final LayoutInflater inflater = LayoutInflater.from(context);
+ final int mode = getMode();
+ if (mode == MODE_LIST) {
+ return inflater.inflate(R.layout.item_doc_list, parent, false);
+ } else if (mode == MODE_GRID) {
+ return inflater.inflate(R.layout.item_doc_grid, parent, false);
+ } else {
+ throw new IllegalStateException();
+ }
}
@Override
@@ -137,12 +237,10 @@
final TextView summary = (TextView) view.findViewById(android.R.id.summary);
final ImageView icon = (ImageView) view.findViewById(android.R.id.icon);
- icon.setMaxWidth(128);
- icon.setMaxHeight(128);
-
final String guid = getCursorString(cursor, DocumentColumns.GUID);
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);
if ((flags & DocumentsContract.FLAG_SUPPORTS_THUMBNAIL) != 0) {
@@ -150,19 +248,39 @@
final Uri childUri = DocumentsContract.buildDocumentUri(uri.getAuthority(), guid);
icon.setImageURI(childUri);
} else {
- icon.setImageURI(null);
+ icon.setImageDrawable(DocumentsActivity.resolveDocumentIcon(context, mimeType));
}
title.setText(displayName);
- summary.setText(mimeType);
+ if (summary != null) {
+ summary.setText(DateUtils.getRelativeTimeSpanString(lastModified));
+ }
}
}
-
- private static String getCursorString(Cursor cursor, String columnName) {
+
+ 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));
}
- private static int getCursorInt(Cursor cursor, String columnName) {
+ public static long getCursorLong(Cursor cursor, String columnName) {
+ return cursor.getLong(cursor.getColumnIndex(columnName));
+ }
+
+ public static int getCursorInt(Cursor cursor, String columnName) {
return cursor.getInt(cursor.getColumnIndex(columnName));
}
}