blob: 2740e53e93ac37edd82aaa4bb48d01a142a60284 [file] [log] [blame]
Jeff Sharkeye22d02e2013-04-26 16:54:55 -07001/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.documentsui;
18
Jeff Sharkey09c10bf2013-06-30 20:02:59 -070019import android.app.Fragment;
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070020import android.app.FragmentManager;
21import android.app.FragmentTransaction;
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070022import android.app.LoaderManager.LoaderCallbacks;
23import android.content.Context;
24import android.content.CursorLoader;
25import android.content.Loader;
26import android.database.Cursor;
27import android.net.Uri;
28import android.os.Bundle;
29import android.provider.DocumentsContract;
30import android.provider.DocumentsContract.DocumentColumns;
Jeff Sharkey09c10bf2013-06-30 20:02:59 -070031import android.text.format.DateUtils;
Jeff Sharkeyc317af82013-07-01 16:56:54 -070032import android.util.SparseBooleanArray;
33import android.view.ActionMode;
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070034import android.view.LayoutInflater;
Jeff Sharkey09c10bf2013-06-30 20:02:59 -070035import android.view.Menu;
36import android.view.MenuInflater;
37import android.view.MenuItem;
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070038import android.view.View;
39import android.view.ViewGroup;
Jeff Sharkeyc317af82013-07-01 16:56:54 -070040import android.widget.AbsListView;
41import android.widget.AbsListView.MultiChoiceModeListener;
Jeff Sharkey09c10bf2013-06-30 20:02:59 -070042import android.widget.AdapterView;
43import android.widget.AdapterView.OnItemClickListener;
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070044import android.widget.CursorAdapter;
Jeff Sharkey09c10bf2013-06-30 20:02:59 -070045import android.widget.GridView;
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070046import android.widget.ImageView;
47import android.widget.ListView;
48import android.widget.TextView;
49
Jeff Sharkey3c28b792013-07-01 17:22:02 -070050import com.android.documentsui.DocumentsActivity.DisplayState;
Jeff Sharkey09c10bf2013-06-30 20:02:59 -070051import com.android.documentsui.DocumentsActivity.Document;
Jeff Sharkeyc317af82013-07-01 16:56:54 -070052import com.google.android.collect.Lists;
53
54import java.util.ArrayList;
Jeff Sharkey09c10bf2013-06-30 20:02:59 -070055
56/**
57 * Display the documents inside a single directory.
58 */
59public class DirectoryFragment extends Fragment {
60
61 // TODO: show storage backend in item views when requested
Jeff Sharkey50d35622013-08-02 10:33:21 -070062 // TODO: apply sort order locally
63 // TODO: apply MIME filtering locally
Jeff Sharkey3c28b792013-07-01 17:22:02 -070064
Jeff Sharkey09c10bf2013-06-30 20:02:59 -070065 private ListView mListView;
66 private GridView mGridView;
67
Jeff Sharkeyc317af82013-07-01 16:56:54 -070068 private AbsListView mCurrentView;
69
Jeff Sharkey5b535922013-08-02 15:55:26 -070070 private static final int TYPE_NORMAL = 1;
71 private static final int TYPE_SEARCH = 2;
72 private static final int TYPE_RECENT_OPEN = 3;
73 private static final int TYPE_RECENT_CREATE = 4;
74
75 private int mType = TYPE_NORMAL;
76
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070077 private DocumentsAdapter mAdapter;
78 private LoaderCallbacks<Cursor> mCallbacks;
79
Jeff Sharkey5b535922013-08-02 15:55:26 -070080 private static final String EXTRA_URI = "uri";
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070081
82 private static final int LOADER_DOCUMENTS = 2;
83
Jeff Sharkey5b535922013-08-02 15:55:26 -070084 public static void show(FragmentManager fm, Uri uri) {
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070085 final Bundle args = new Bundle();
Jeff Sharkey5b535922013-08-02 15:55:26 -070086 args.putParcelable(EXTRA_URI, uri);
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070087
88 final DirectoryFragment fragment = new DirectoryFragment();
89 fragment.setArguments(args);
90
91 final FragmentTransaction ft = fm.beginTransaction();
Jeff Sharkey09c10bf2013-06-30 20:02:59 -070092 ft.replace(R.id.directory, fragment);
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070093 ft.commitAllowingStateLoss();
94 }
95
96 @Override
Jeff Sharkey09c10bf2013-06-30 20:02:59 -070097 public void onCreate(Bundle savedInstanceState) {
98 super.onCreate(savedInstanceState);
99 setHasOptionsMenu(true);
100 }
101
102 @Override
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700103 public View onCreateView(
104 LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
105 final Context context = inflater.getContext();
106
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700107 final View view = inflater.inflate(R.layout.fragment_directory, container, false);
108
109 mListView = (ListView) view.findViewById(R.id.list);
110 mListView.setOnItemClickListener(mItemListener);
Jeff Sharkeyc317af82013-07-01 16:56:54 -0700111 mListView.setMultiChoiceModeListener(mMultiListener);
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700112
113 mGridView = (GridView) view.findViewById(R.id.grid);
114 mGridView.setOnItemClickListener(mItemListener);
Jeff Sharkeyc317af82013-07-01 16:56:54 -0700115 mGridView.setMultiChoiceModeListener(mMultiListener);
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700116
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700117 mAdapter = new DocumentsAdapter(context);
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700118 updateMode();
119
Jeff Sharkey5b535922013-08-02 15:55:26 -0700120 final Uri uri = getArguments().getParcelable(EXTRA_URI);
121
122 if (uri.getQueryParameter(DocumentsContract.PARAM_QUERY) != null) {
123 mType = TYPE_SEARCH;
124 } else if (RecentsProvider.buildRecentOpen().equals(uri)) {
125 mType = TYPE_RECENT_OPEN;
126 } else if (RecentsProvider.buildRecentCreate().equals(uri)) {
127 mType = TYPE_RECENT_CREATE;
128 } else {
129 mType = TYPE_NORMAL;
130 }
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700131
132 mCallbacks = new LoaderCallbacks<Cursor>() {
133 @Override
134 public Loader<Cursor> onCreateLoader(int id, Bundle args) {
Jeff Sharkey3c28b792013-07-01 17:22:02 -0700135 final DisplayState state = getDisplayState(DirectoryFragment.this);
136 final String sortOrder;
137 if (state.sortBy == DisplayState.SORT_BY_NAME) {
138 sortOrder = DocumentColumns.DISPLAY_NAME + " ASC";
139 } else if (state.sortBy == DisplayState.SORT_BY_DATE) {
140 sortOrder = DocumentColumns.LAST_MODIFIED + " DESC";
141 } else {
142 sortOrder = null;
143 }
144
Jeff Sharkey46165b52013-07-31 20:53:22 -0700145 final Uri contentsUri;
Jeff Sharkey5b535922013-08-02 15:55:26 -0700146 if (mType == TYPE_NORMAL) {
147 contentsUri = DocumentsContract.buildContentsUri(uri);
Jeff Sharkey46165b52013-07-31 20:53:22 -0700148 } else {
Jeff Sharkey5b535922013-08-02 15:55:26 -0700149 contentsUri = uri;
Jeff Sharkey46165b52013-07-31 20:53:22 -0700150 }
151
Jeff Sharkey3c28b792013-07-01 17:22:02 -0700152 return new CursorLoader(context, contentsUri, null, null, null, sortOrder);
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700153 }
154
155 @Override
156 public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
157 mAdapter.swapCursor(data);
158 }
159
160 @Override
161 public void onLoaderReset(Loader<Cursor> loader) {
162 mAdapter.swapCursor(null);
163 }
164 };
165
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700166 return view;
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700167 }
168
169 @Override
170 public void onStart() {
171 super.onStart();
172 getLoaderManager().restartLoader(LOADER_DOCUMENTS, getArguments(), mCallbacks);
173 }
174
175 @Override
176 public void onStop() {
177 super.onStop();
178 getLoaderManager().destroyLoader(LOADER_DOCUMENTS);
179 }
180
181 @Override
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700182 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
183 super.onCreateOptionsMenu(menu, inflater);
184 inflater.inflate(R.menu.directory, menu);
185 }
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700186
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700187 @Override
188 public void onPrepareOptionsMenu(Menu menu) {
189 super.onPrepareOptionsMenu(menu);
Jeff Sharkey3c28b792013-07-01 17:22:02 -0700190 final DisplayState state = getDisplayState(this);
191 menu.findItem(R.id.menu_grid).setVisible(state.mode != DisplayState.MODE_GRID);
192 menu.findItem(R.id.menu_list).setVisible(state.mode != DisplayState.MODE_LIST);
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700193 }
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700194
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700195 @Override
196 public boolean onOptionsItemSelected(MenuItem item) {
Jeff Sharkey3c28b792013-07-01 17:22:02 -0700197 final DisplayState state = getDisplayState(this);
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700198 final int id = item.getItemId();
199 if (id == R.id.menu_grid) {
Jeff Sharkey3c28b792013-07-01 17:22:02 -0700200 state.mode = DisplayState.MODE_GRID;
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700201 updateMode();
Jeff Sharkey3c28b792013-07-01 17:22:02 -0700202 getFragmentManager().invalidateOptionsMenu();
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700203 return true;
204 } else if (id == R.id.menu_list) {
Jeff Sharkey3c28b792013-07-01 17:22:02 -0700205 state.mode = DisplayState.MODE_LIST;
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700206 updateMode();
Jeff Sharkey3c28b792013-07-01 17:22:02 -0700207 getFragmentManager().invalidateOptionsMenu();
208 return true;
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700209 } else {
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700210 return super.onOptionsItemSelected(item);
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700211 }
212 }
213
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700214 private void updateMode() {
Jeff Sharkey3c28b792013-07-01 17:22:02 -0700215 final DisplayState state = getDisplayState(this);
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700216
Jeff Sharkey3c28b792013-07-01 17:22:02 -0700217 mListView.setVisibility(state.mode == DisplayState.MODE_LIST ? View.VISIBLE : View.GONE);
218 mGridView.setVisibility(state.mode == DisplayState.MODE_GRID ? View.VISIBLE : View.GONE);
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700219
Jeff Sharkeyc317af82013-07-01 16:56:54 -0700220 final int choiceMode;
Jeff Sharkey3c28b792013-07-01 17:22:02 -0700221 if (state.allowMultiple) {
Jeff Sharkeyc317af82013-07-01 16:56:54 -0700222 choiceMode = ListView.CHOICE_MODE_MULTIPLE_MODAL;
223 } else {
224 choiceMode = ListView.CHOICE_MODE_NONE;
225 }
226
Jeff Sharkey3c28b792013-07-01 17:22:02 -0700227 if (state.mode == DisplayState.MODE_GRID) {
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700228 mListView.setAdapter(null);
Jeff Sharkeyc317af82013-07-01 16:56:54 -0700229 mListView.setChoiceMode(ListView.CHOICE_MODE_NONE);
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700230 mGridView.setAdapter(mAdapter);
Jeff Sharkeyc317af82013-07-01 16:56:54 -0700231 mGridView.setColumnWidth(getResources().getDimensionPixelSize(R.dimen.grid_width));
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700232 mGridView.setNumColumns(GridView.AUTO_FIT);
Jeff Sharkeyc317af82013-07-01 16:56:54 -0700233 mGridView.setChoiceMode(choiceMode);
234 mCurrentView = mGridView;
Jeff Sharkey3c28b792013-07-01 17:22:02 -0700235 } else if (state.mode == DisplayState.MODE_LIST) {
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700236 mGridView.setAdapter(null);
Jeff Sharkeyc317af82013-07-01 16:56:54 -0700237 mGridView.setChoiceMode(ListView.CHOICE_MODE_NONE);
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700238 mListView.setAdapter(mAdapter);
Jeff Sharkeyc317af82013-07-01 16:56:54 -0700239 mListView.setChoiceMode(choiceMode);
240 mCurrentView = mListView;
Jeff Sharkey3c28b792013-07-01 17:22:02 -0700241 } else {
242 throw new IllegalStateException();
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700243 }
244 }
245
Jeff Sharkey46165b52013-07-31 20:53:22 -0700246 public void updateSortBy() {
Jeff Sharkey3c28b792013-07-01 17:22:02 -0700247 getLoaderManager().restartLoader(LOADER_DOCUMENTS, getArguments(), mCallbacks);
248 }
249
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700250 private OnItemClickListener mItemListener = new OnItemClickListener() {
251 @Override
252 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
253 final Cursor cursor = (Cursor) mAdapter.getItem(position);
Jeff Sharkey5b535922013-08-02 15:55:26 -0700254 final Uri uri = getArguments().getParcelable(EXTRA_URI);
255 final Document doc = Document.fromCursor(uri, cursor);
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700256 ((DocumentsActivity) getActivity()).onDocumentPicked(doc);
257 }
258 };
259
Jeff Sharkeyc317af82013-07-01 16:56:54 -0700260 private MultiChoiceModeListener mMultiListener = new MultiChoiceModeListener() {
261 @Override
262 public boolean onCreateActionMode(ActionMode mode, Menu menu) {
263 mode.getMenuInflater().inflate(R.menu.mode_directory, menu);
264 return true;
265 }
266
267 @Override
268 public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
269 return true;
270 }
271
272 @Override
273 public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
274 if (item.getItemId() == R.id.menu_open) {
Jeff Sharkey5b535922013-08-02 15:55:26 -0700275 final Uri uri = getArguments().getParcelable(EXTRA_URI);
Jeff Sharkeyc317af82013-07-01 16:56:54 -0700276 final SparseBooleanArray checked = mCurrentView.getCheckedItemPositions();
277 final ArrayList<Document> docs = Lists.newArrayList();
278
279 final int size = checked.size();
280 for (int i = 0; i < size; i++) {
281 if (checked.valueAt(i)) {
282 final Cursor cursor = (Cursor) mAdapter.getItem(checked.keyAt(i));
Jeff Sharkey5b535922013-08-02 15:55:26 -0700283 docs.add(Document.fromCursor(uri, cursor));
Jeff Sharkeyc317af82013-07-01 16:56:54 -0700284 }
285 }
286
287 ((DocumentsActivity) getActivity()).onDocumentsPicked(docs);
288 return true;
289 } else {
290 return false;
291 }
292 }
293
294 @Override
295 public void onDestroyActionMode(ActionMode mode) {
296 // ignored
297 }
298
299 @Override
300 public void onItemCheckedStateChanged(
301 ActionMode mode, int position, long id, boolean checked) {
302 if (checked) {
303 final Cursor cursor = (Cursor) mAdapter.getItem(position);
304 final String mimeType = getCursorString(cursor, DocumentColumns.MIME_TYPE);
305
306 // Directories cannot be checked
307 if (DocumentsContract.MIME_TYPE_DIRECTORY.equals(mimeType)) {
308 mCurrentView.setItemChecked(position, false);
309 }
310 }
311
312 mode.setTitle(getResources()
313 .getString(R.string.mode_selected_count, mCurrentView.getCheckedItemCount()));
314 }
315 };
316
Jeff Sharkey3c28b792013-07-01 17:22:02 -0700317 private static DisplayState getDisplayState(Fragment fragment) {
318 return ((DocumentsActivity) fragment.getActivity()).getDisplayState();
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700319 }
320
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700321 private class DocumentsAdapter extends CursorAdapter {
322 public DocumentsAdapter(Context context) {
323 super(context, null, false);
324 }
325
326 @Override
327 public View newView(Context context, Cursor cursor, ViewGroup parent) {
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700328 final LayoutInflater inflater = LayoutInflater.from(context);
Jeff Sharkey3c28b792013-07-01 17:22:02 -0700329 final DisplayState state = getDisplayState(DirectoryFragment.this);
330 if (state.mode == DisplayState.MODE_LIST) {
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700331 return inflater.inflate(R.layout.item_doc_list, parent, false);
Jeff Sharkey3c28b792013-07-01 17:22:02 -0700332 } else if (state.mode == DisplayState.MODE_GRID) {
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700333 return inflater.inflate(R.layout.item_doc_grid, parent, false);
334 } else {
335 throw new IllegalStateException();
336 }
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700337 }
338
339 @Override
340 public void bindView(View view, Context context, Cursor cursor) {
341 final TextView title = (TextView) view.findViewById(android.R.id.title);
342 final TextView summary = (TextView) view.findViewById(android.R.id.summary);
343 final ImageView icon = (ImageView) view.findViewById(android.R.id.icon);
344
Jeff Sharkey50d35622013-08-02 10:33:21 -0700345 final String docId = getCursorString(cursor, DocumentColumns.DOC_ID);
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700346 final String displayName = getCursorString(cursor, DocumentColumns.DISPLAY_NAME);
347 final String mimeType = getCursorString(cursor, DocumentColumns.MIME_TYPE);
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700348 final long lastModified = getCursorLong(cursor, DocumentColumns.LAST_MODIFIED);
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700349 final int flags = getCursorInt(cursor, DocumentColumns.FLAGS);
350
Jeff Sharkey5b535922013-08-02 15:55:26 -0700351 final Uri uri = getArguments().getParcelable(EXTRA_URI);
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700352 if ((flags & DocumentsContract.FLAG_SUPPORTS_THUMBNAIL) != 0) {
Jeff Sharkey5b535922013-08-02 15:55:26 -0700353 final Uri childUri = DocumentsContract.buildDocumentUri(uri, docId);
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700354 icon.setImageURI(childUri);
355 } else {
Jeff Sharkey5b535922013-08-02 15:55:26 -0700356 icon.setImageDrawable(DocumentsActivity.resolveDocumentIcon(
357 context, uri.getAuthority(), mimeType));
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700358 }
359
360 title.setText(displayName);
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700361 if (summary != null) {
362 summary.setText(DateUtils.getRelativeTimeSpanString(lastModified));
363 }
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700364 }
365 }
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700366
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700367 public static String getCursorString(Cursor cursor, String columnName) {
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700368 return cursor.getString(cursor.getColumnIndex(columnName));
369 }
370
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700371 public static long getCursorLong(Cursor cursor, String columnName) {
372 return cursor.getLong(cursor.getColumnIndex(columnName));
373 }
374
375 public static int getCursorInt(Cursor cursor, String columnName) {
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700376 return cursor.getInt(cursor.getColumnIndex(columnName));
377 }
378}