blob: 5ba1930261db9ba022b87434b908f2e58c9b5d78 [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 Sharkeye22d02e2013-04-26 16:54:55 -070032import android.view.LayoutInflater;
Jeff Sharkey09c10bf2013-06-30 20:02:59 -070033import android.view.Menu;
34import android.view.MenuInflater;
35import android.view.MenuItem;
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070036import android.view.View;
37import android.view.ViewGroup;
Jeff Sharkey09c10bf2013-06-30 20:02:59 -070038import android.widget.AdapterView;
39import android.widget.AdapterView.OnItemClickListener;
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070040import android.widget.CursorAdapter;
Jeff Sharkey09c10bf2013-06-30 20:02:59 -070041import android.widget.GridView;
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070042import android.widget.ImageView;
43import android.widget.ListView;
44import android.widget.TextView;
45
Jeff Sharkey09c10bf2013-06-30 20:02:59 -070046import com.android.documentsui.DocumentsActivity.Document;
47
48/**
49 * Display the documents inside a single directory.
50 */
51public class DirectoryFragment extends Fragment {
52
53 // TODO: show storage backend in item views when requested
54 // TODO: implement sorting dialog
55 // TODO: support multiple selection with actionmode
56
57 private ListView mListView;
58 private GridView mGridView;
59
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070060 private DocumentsAdapter mAdapter;
61 private LoaderCallbacks<Cursor> mCallbacks;
62
Jeff Sharkey09c10bf2013-06-30 20:02:59 -070063 private int mFlags;
64
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070065 private static final String EXTRA_URI = "uri";
Jeff Sharkey09c10bf2013-06-30 20:02:59 -070066 private static final String EXTRA_MODE = "display_mode";
67
68 private static final int MODE_LIST = 1;
69 private static final int MODE_GRID = 2;
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070070
71 private static final int LOADER_DOCUMENTS = 2;
72
Jeff Sharkey09c10bf2013-06-30 20:02:59 -070073 public static void show(FragmentManager fm, Uri uri, String displayName) {
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070074 final Bundle args = new Bundle();
75 args.putParcelable(EXTRA_URI, uri);
Jeff Sharkey09c10bf2013-06-30 20:02:59 -070076 args.putInt(EXTRA_MODE, MODE_LIST);
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070077
78 final DirectoryFragment fragment = new DirectoryFragment();
79 fragment.setArguments(args);
80
81 final FragmentTransaction ft = fm.beginTransaction();
Jeff Sharkey09c10bf2013-06-30 20:02:59 -070082 ft.replace(R.id.directory, fragment);
83 ft.addToBackStack(displayName);
84 ft.setBreadCrumbTitle(displayName);
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070085 ft.commitAllowingStateLoss();
86 }
87
88 @Override
Jeff Sharkey09c10bf2013-06-30 20:02:59 -070089 public void onCreate(Bundle savedInstanceState) {
90 super.onCreate(savedInstanceState);
91 setHasOptionsMenu(true);
92 }
93
94 @Override
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070095 public View onCreateView(
96 LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
97 final Context context = inflater.getContext();
98
Jeff Sharkey09c10bf2013-06-30 20:02:59 -070099 final View view = inflater.inflate(R.layout.fragment_directory, container, false);
100
101 mListView = (ListView) view.findViewById(R.id.list);
102 mListView.setOnItemClickListener(mItemListener);
103
104 mGridView = (GridView) view.findViewById(R.id.grid);
105 mGridView.setOnItemClickListener(mItemListener);
106
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700107 mAdapter = new DocumentsAdapter(context);
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700108 updateMode();
109
110 // TODO: migrate flags query to loader
111 final Uri uri = getArguments().getParcelable(EXTRA_URI);
112 mFlags = getDocumentFlags(context, uri);
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700113
114 mCallbacks = new LoaderCallbacks<Cursor>() {
115 @Override
116 public Loader<Cursor> onCreateLoader(int id, Bundle args) {
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700117 final Uri contentsUri = DocumentsContract.buildContentsUri(uri);
118 return new CursorLoader(context, contentsUri, null, null, null, null);
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700119 }
120
121 @Override
122 public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
123 mAdapter.swapCursor(data);
124 }
125
126 @Override
127 public void onLoaderReset(Loader<Cursor> loader) {
128 mAdapter.swapCursor(null);
129 }
130 };
131
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700132 return view;
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700133 }
134
135 @Override
136 public void onStart() {
137 super.onStart();
138 getLoaderManager().restartLoader(LOADER_DOCUMENTS, getArguments(), mCallbacks);
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700139
140 // TODO: clean up tracking of current directory
141 final Uri uri = getArguments().getParcelable(EXTRA_URI);
142 ((DocumentsActivity) getActivity()).onDirectoryChanged(uri, mFlags);
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700143 }
144
145 @Override
146 public void onStop() {
147 super.onStop();
148 getLoaderManager().destroyLoader(LOADER_DOCUMENTS);
149 }
150
151 @Override
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700152 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
153 super.onCreateOptionsMenu(menu, inflater);
154 inflater.inflate(R.menu.directory, menu);
155 }
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700156
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700157 @Override
158 public void onPrepareOptionsMenu(Menu menu) {
159 super.onPrepareOptionsMenu(menu);
160 final int mode = getMode();
161 menu.findItem(R.id.menu_grid).setVisible(mode != MODE_GRID);
162 menu.findItem(R.id.menu_list).setVisible(mode != MODE_LIST);
163 }
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700164
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700165 @Override
166 public boolean onOptionsItemSelected(MenuItem item) {
167 final int id = item.getItemId();
168 if (id == R.id.menu_grid) {
169 getArguments().putInt(EXTRA_MODE, MODE_GRID);
170 updateMode();
171 return true;
172 } else if (id == R.id.menu_list) {
173 getArguments().putInt(EXTRA_MODE, MODE_LIST);
174 updateMode();
175 return true;
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700176 } else {
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700177 return super.onOptionsItemSelected(item);
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700178 }
179 }
180
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700181 private void updateMode() {
182 final int mode = getMode();
183
184 mListView.setVisibility(mode == MODE_LIST ? View.VISIBLE : View.GONE);
185 mGridView.setVisibility(mode == MODE_GRID ? View.VISIBLE : View.GONE);
186
187 if (mode == MODE_GRID) {
188 mListView.setAdapter(null);
189 mGridView.setAdapter(mAdapter);
190 mGridView.setNumColumns(GridView.AUTO_FIT);
191 } else {
192 mGridView.setAdapter(null);
193 mListView.setAdapter(mAdapter);
194 }
195 }
196
197 private OnItemClickListener mItemListener = new OnItemClickListener() {
198 @Override
199 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
200 final Cursor cursor = (Cursor) mAdapter.getItem(position);
201
202 final Uri uri = getArguments().getParcelable(EXTRA_URI);
203 final Document doc = Document.fromCursor(uri.getAuthority(), cursor);
204 ((DocumentsActivity) getActivity()).onDocumentPicked(doc);
205 }
206 };
207
208 private boolean getSupportsCreate() {
209 return (mFlags & DocumentsContract.FLAG_SUPPORTS_CREATE) != 0;
210 }
211
212 private int getMode() {
213 return getArguments().getInt(EXTRA_MODE, MODE_LIST);
214 }
215
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700216 private class DocumentsAdapter extends CursorAdapter {
217 public DocumentsAdapter(Context context) {
218 super(context, null, false);
219 }
220
221 @Override
222 public View newView(Context context, Cursor cursor, ViewGroup parent) {
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700223 final LayoutInflater inflater = LayoutInflater.from(context);
224 final int mode = getMode();
225 if (mode == MODE_LIST) {
226 return inflater.inflate(R.layout.item_doc_list, parent, false);
227 } else if (mode == MODE_GRID) {
228 return inflater.inflate(R.layout.item_doc_grid, parent, false);
229 } else {
230 throw new IllegalStateException();
231 }
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700232 }
233
234 @Override
235 public void bindView(View view, Context context, Cursor cursor) {
236 final TextView title = (TextView) view.findViewById(android.R.id.title);
237 final TextView summary = (TextView) view.findViewById(android.R.id.summary);
238 final ImageView icon = (ImageView) view.findViewById(android.R.id.icon);
239
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700240 final String guid = getCursorString(cursor, DocumentColumns.GUID);
241 final String displayName = getCursorString(cursor, DocumentColumns.DISPLAY_NAME);
242 final String mimeType = getCursorString(cursor, DocumentColumns.MIME_TYPE);
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700243 final long lastModified = getCursorLong(cursor, DocumentColumns.LAST_MODIFIED);
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700244 final int flags = getCursorInt(cursor, DocumentColumns.FLAGS);
245
246 if ((flags & DocumentsContract.FLAG_SUPPORTS_THUMBNAIL) != 0) {
247 final Uri uri = getArguments().getParcelable(EXTRA_URI);
248 final Uri childUri = DocumentsContract.buildDocumentUri(uri.getAuthority(), guid);
249 icon.setImageURI(childUri);
250 } else {
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700251 icon.setImageDrawable(DocumentsActivity.resolveDocumentIcon(context, mimeType));
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700252 }
253
254 title.setText(displayName);
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700255 if (summary != null) {
256 summary.setText(DateUtils.getRelativeTimeSpanString(lastModified));
257 }
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700258 }
259 }
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700260
261 private static int getDocumentFlags(Context context, Uri uri) {
262 final Cursor cursor = context.getContentResolver().query(uri, new String[] {
263 DocumentColumns.FLAGS }, null, null, null);
264 try {
265 if (cursor.moveToFirst()) {
266 return getCursorInt(cursor, DocumentColumns.FLAGS);
267 } else {
268 return 0;
269 }
270 } finally {
271 cursor.close();
272 }
273 }
274
275 public static String getCursorString(Cursor cursor, String columnName) {
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700276 return cursor.getString(cursor.getColumnIndex(columnName));
277 }
278
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700279 public static long getCursorLong(Cursor cursor, String columnName) {
280 return cursor.getLong(cursor.getColumnIndex(columnName));
281 }
282
283 public static int getCursorInt(Cursor cursor, String columnName) {
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700284 return cursor.getInt(cursor.getColumnIndex(columnName));
285 }
286}