blob: 531eaf3c7863424556043bb73af5abd55bda2398 [file] [log] [blame]
/*
* 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 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;
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;
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.AbsListView;
import android.widget.AbsListView.MultiChoiceModeListener;
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;
import com.android.documentsui.DocumentsActivity.DisplayState;
import com.android.documentsui.DocumentsActivity.Document;
import com.google.android.collect.Lists;
import java.util.ArrayList;
/**
* Display the documents inside a single directory.
*/
public class DirectoryFragment extends Fragment {
// TODO: show storage backend in item views when requested
private static final String TAG_SORT = "sort";
private ListView mListView;
private GridView mGridView;
private AbsListView mCurrentView;
private DocumentsAdapter mAdapter;
private LoaderCallbacks<Cursor> mCallbacks;
private int mFlags;
private static final String EXTRA_URI = "uri";
private static final int LOADER_DOCUMENTS = 2;
public static void show(FragmentManager fm, Uri uri, String displayName) {
final Bundle args = new Bundle();
args.putParcelable(EXTRA_URI, uri);
final DirectoryFragment fragment = new DirectoryFragment();
fragment.setArguments(args);
final FragmentTransaction ft = fm.beginTransaction();
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);
mListView.setMultiChoiceModeListener(mMultiListener);
mGridView = (GridView) view.findViewById(R.id.grid);
mGridView.setOnItemClickListener(mItemListener);
mGridView.setMultiChoiceModeListener(mMultiListener);
mAdapter = new DocumentsAdapter(context);
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 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, sortOrder);
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
mAdapter.swapCursor(data);
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
mAdapter.swapCursor(null);
}
};
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
public void onStop() {
super.onStop();
getLoaderManager().destroyLoader(LOADER_DOCUMENTS);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.directory, menu);
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
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) {
state.mode = DisplayState.MODE_GRID;
updateMode();
getFragmentManager().invalidateOptionsMenu();
return true;
} else if (id == R.id.menu_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);
}
}
private void updateMode() {
final DisplayState state = getDisplayState(this);
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 (state.allowMultiple) {
choiceMode = ListView.CHOICE_MODE_MULTIPLE_MODAL;
} else {
choiceMode = ListView.CHOICE_MODE_NONE;
}
if (state.mode == DisplayState.MODE_GRID) {
mListView.setAdapter(null);
mListView.setChoiceMode(ListView.CHOICE_MODE_NONE);
mGridView.setAdapter(mAdapter);
mGridView.setColumnWidth(getResources().getDimensionPixelSize(R.dimen.grid_width));
mGridView.setNumColumns(GridView.AUTO_FIT);
mGridView.setChoiceMode(choiceMode);
mCurrentView = mGridView;
} 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) {
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 MultiChoiceModeListener mMultiListener = new MultiChoiceModeListener() {
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
mode.getMenuInflater().inflate(R.menu.mode_directory, menu);
return true;
}
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return true;
}
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
if (item.getItemId() == R.id.menu_open) {
final Uri uri = getArguments().getParcelable(EXTRA_URI);
final SparseBooleanArray checked = mCurrentView.getCheckedItemPositions();
final ArrayList<Document> docs = Lists.newArrayList();
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.getAuthority(), cursor));
}
}
((DocumentsActivity) getActivity()).onDocumentsPicked(docs);
return true;
} else {
return false;
}
}
@Override
public void onDestroyActionMode(ActionMode mode) {
// ignored
}
@Override
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)) {
mCurrentView.setItemChecked(position, false);
}
}
mode.setTitle(getResources()
.getString(R.string.mode_selected_count, mCurrentView.getCheckedItemCount()));
}
};
private static DisplayState getDisplayState(Fragment fragment) {
return ((DocumentsActivity) fragment.getActivity()).getDisplayState();
}
private class DocumentsAdapter extends CursorAdapter {
public DocumentsAdapter(Context context) {
super(context, null, false);
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
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);
} else if (state.mode == DisplayState.MODE_GRID) {
return 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 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) {
final Uri uri = getArguments().getParcelable(EXTRA_URI);
final Uri childUri = DocumentsContract.buildDocumentUri(uri.getAuthority(), guid);
icon.setImageURI(childUri);
} else {
icon.setImageDrawable(DocumentsActivity.resolveDocumentIcon(context, mimeType));
}
title.setText(displayName);
if (summary != null) {
summary.setText(DateUtils.getRelativeTimeSpanString(lastModified));
}
}
}
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);
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));
}
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));
}
}