blob: d3421e78f7e0f9f6a3b99d83d6b7c6260acff0e4 [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 Sharkey8a8fb672013-05-07 12:41:33 -070019import static com.android.documentsui.DocumentsActivity.TAG;
Jeff Sharkey873daa32013-08-18 17:38:20 -070020import static com.android.documentsui.DocumentsActivity.DisplayState.ACTION_MANAGE;
21import static com.android.documentsui.DocumentsActivity.DisplayState.MODE_GRID;
22import static com.android.documentsui.DocumentsActivity.DisplayState.MODE_LIST;
23import static com.android.documentsui.DocumentsActivity.DisplayState.SORT_ORDER_DATE;
24import static com.android.documentsui.DocumentsActivity.DisplayState.SORT_ORDER_NAME;
25import static com.android.documentsui.DocumentsActivity.DisplayState.SORT_ORDER_SIZE;
Jeff Sharkey8a8fb672013-05-07 12:41:33 -070026
Jeff Sharkey09c10bf2013-06-30 20:02:59 -070027import android.app.Fragment;
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070028import android.app.FragmentManager;
29import android.app.FragmentTransaction;
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070030import android.app.LoaderManager.LoaderCallbacks;
Jeff Sharkey873daa32013-08-18 17:38:20 -070031import android.content.ContentResolver;
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070032import android.content.Context;
Jeff Sharkey873daa32013-08-18 17:38:20 -070033import android.content.Intent;
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070034import android.content.Loader;
Jeff Sharkey8a8fb672013-05-07 12:41:33 -070035import android.graphics.Bitmap;
36import android.graphics.Point;
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070037import android.net.Uri;
Jeff Sharkey8a8fb672013-05-07 12:41:33 -070038import android.os.AsyncTask;
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070039import android.os.Bundle;
40import android.provider.DocumentsContract;
Jeff Sharkey09c10bf2013-06-30 20:02:59 -070041import android.text.format.DateUtils;
Jeff Sharkey2e694f82013-08-06 16:26:14 -070042import android.text.format.Formatter;
Jeff Sharkey8a8fb672013-05-07 12:41:33 -070043import android.text.format.Time;
44import android.util.Log;
Jeff Sharkeyc317af82013-07-01 16:56:54 -070045import android.util.SparseBooleanArray;
46import android.view.ActionMode;
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070047import android.view.LayoutInflater;
Jeff Sharkey09c10bf2013-06-30 20:02:59 -070048import android.view.Menu;
Jeff Sharkey09c10bf2013-06-30 20:02:59 -070049import android.view.MenuItem;
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070050import android.view.View;
51import android.view.ViewGroup;
Jeff Sharkeyc317af82013-07-01 16:56:54 -070052import android.widget.AbsListView;
53import android.widget.AbsListView.MultiChoiceModeListener;
Jeff Sharkey09c10bf2013-06-30 20:02:59 -070054import android.widget.AdapterView;
55import android.widget.AdapterView.OnItemClickListener;
Jeff Sharkeya5defe32013-08-05 17:56:48 -070056import android.widget.BaseAdapter;
Jeff Sharkey09c10bf2013-06-30 20:02:59 -070057import android.widget.GridView;
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070058import android.widget.ImageView;
59import android.widget.ListView;
60import android.widget.TextView;
Jeff Sharkey873daa32013-08-18 17:38:20 -070061import android.widget.Toast;
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070062
Jeff Sharkey3c28b792013-07-01 17:22:02 -070063import com.android.documentsui.DocumentsActivity.DisplayState;
Jeff Sharkeya5defe32013-08-05 17:56:48 -070064import com.android.documentsui.model.Document;
Jeff Sharkey2e694f82013-08-06 16:26:14 -070065import com.android.documentsui.model.Root;
Jeff Sharkeya5defe32013-08-05 17:56:48 -070066import com.android.internal.util.Predicate;
Jeff Sharkeyc317af82013-07-01 16:56:54 -070067import com.google.android.collect.Lists;
68
69import java.util.ArrayList;
Jeff Sharkeya5defe32013-08-05 17:56:48 -070070import java.util.Comparator;
71import java.util.List;
Jeff Sharkey2e694f82013-08-06 16:26:14 -070072import java.util.concurrent.atomic.AtomicInteger;
Jeff Sharkey09c10bf2013-06-30 20:02:59 -070073
74/**
75 * Display the documents inside a single directory.
76 */
77public class DirectoryFragment extends Fragment {
78
Jeff Sharkeyc6cbdf12013-08-07 16:22:02 -070079 private View mEmptyView;
Jeff Sharkey09c10bf2013-06-30 20:02:59 -070080 private ListView mListView;
81 private GridView mGridView;
82
Jeff Sharkeyc317af82013-07-01 16:56:54 -070083 private AbsListView mCurrentView;
84
Jeff Sharkeyc6cbdf12013-08-07 16:22:02 -070085 private Predicate<Document> mFilter;
86
Jeff Sharkeya5defe32013-08-05 17:56:48 -070087 public static final int TYPE_NORMAL = 1;
88 public static final int TYPE_SEARCH = 2;
89 public static final int TYPE_RECENT_OPEN = 3;
Jeff Sharkey5b535922013-08-02 15:55:26 -070090
91 private int mType = TYPE_NORMAL;
92
Jeff Sharkey8a8fb672013-05-07 12:41:33 -070093 private Point mThumbSize;
94
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070095 private DocumentsAdapter mAdapter;
Jeff Sharkeya5defe32013-08-05 17:56:48 -070096 private LoaderCallbacks<List<Document>> mCallbacks;
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070097
Jeff Sharkey2e694f82013-08-06 16:26:14 -070098 private static final String EXTRA_TYPE = "type";
Jeff Sharkey5b535922013-08-02 15:55:26 -070099 private static final String EXTRA_URI = "uri";
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700100
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700101 private static AtomicInteger sLoaderId = new AtomicInteger(4000);
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700102
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700103 private final int mLoaderId = sLoaderId.incrementAndGet();
104
105 public static void showNormal(FragmentManager fm, Uri uri) {
106 show(fm, TYPE_NORMAL, uri);
107 }
108
109 public static void showSearch(FragmentManager fm, Uri uri, String query) {
110 final Uri searchUri = DocumentsContract.buildSearchUri(uri, query);
111 show(fm, TYPE_SEARCH, searchUri);
112 }
113
114 public static void showRecentsOpen(FragmentManager fm) {
115 show(fm, TYPE_RECENT_OPEN, null);
116 }
117
118 private static void show(FragmentManager fm, int type, Uri uri) {
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700119 final Bundle args = new Bundle();
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700120 args.putInt(EXTRA_TYPE, type);
Jeff Sharkey5b535922013-08-02 15:55:26 -0700121 args.putParcelable(EXTRA_URI, uri);
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700122
123 final DirectoryFragment fragment = new DirectoryFragment();
124 fragment.setArguments(args);
125
126 final FragmentTransaction ft = fm.beginTransaction();
Jeff Sharkey76112212013-08-06 11:26:10 -0700127 ft.replace(R.id.container_directory, fragment);
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700128 ft.commitAllowingStateLoss();
129 }
130
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700131 public static DirectoryFragment get(FragmentManager fm) {
132 // TODO: deal with multiple directories shown at once
Jeff Sharkey76112212013-08-06 11:26:10 -0700133 return (DirectoryFragment) fm.findFragmentById(R.id.container_directory);
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700134 }
135
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700136 @Override
137 public View onCreateView(
138 LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
139 final Context context = inflater.getContext();
140
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700141 final View view = inflater.inflate(R.layout.fragment_directory, container, false);
142
Jeff Sharkeyc6cbdf12013-08-07 16:22:02 -0700143 mEmptyView = view.findViewById(android.R.id.empty);
144
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700145 mListView = (ListView) view.findViewById(R.id.list);
146 mListView.setOnItemClickListener(mItemListener);
Jeff Sharkeyc317af82013-07-01 16:56:54 -0700147 mListView.setMultiChoiceModeListener(mMultiListener);
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700148
149 mGridView = (GridView) view.findViewById(R.id.grid);
150 mGridView.setOnItemClickListener(mItemListener);
Jeff Sharkeyc317af82013-07-01 16:56:54 -0700151 mGridView.setMultiChoiceModeListener(mMultiListener);
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700152
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700153 mAdapter = new DocumentsAdapter();
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700154
Jeff Sharkey5b535922013-08-02 15:55:26 -0700155 final Uri uri = getArguments().getParcelable(EXTRA_URI);
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700156 mType = getArguments().getInt(EXTRA_TYPE);
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700157
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700158 mCallbacks = new LoaderCallbacks<List<Document>>() {
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700159 @Override
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700160 public Loader<List<Document>> onCreateLoader(int id, Bundle args) {
Jeff Sharkey3c28b792013-07-01 17:22:02 -0700161 final DisplayState state = getDisplayState(DirectoryFragment.this);
Jeff Sharkeyc6cbdf12013-08-07 16:22:02 -0700162 mFilter = new MimePredicate(state.acceptMimes);
Jeff Sharkey3c28b792013-07-01 17:22:02 -0700163
Jeff Sharkey1d890e02013-08-15 11:24:03 -0700164 Uri contentsUri;
Jeff Sharkey5b535922013-08-02 15:55:26 -0700165 if (mType == TYPE_NORMAL) {
166 contentsUri = DocumentsContract.buildContentsUri(uri);
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700167 } else if (mType == TYPE_RECENT_OPEN) {
168 contentsUri = RecentsProvider.buildRecentOpen();
Jeff Sharkey46165b52013-07-31 20:53:22 -0700169 } else {
Jeff Sharkey5b535922013-08-02 15:55:26 -0700170 contentsUri = uri;
Jeff Sharkey46165b52013-07-31 20:53:22 -0700171 }
172
Jeff Sharkey1d890e02013-08-15 11:24:03 -0700173 if (state.localOnly) {
174 contentsUri = DocumentsContract.setLocalOnly(contentsUri);
175 }
176
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700177 final Comparator<Document> sortOrder;
Jeff Sharkey873daa32013-08-18 17:38:20 -0700178 if (state.sortOrder == SORT_ORDER_DATE || mType == TYPE_RECENT_OPEN) {
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700179 sortOrder = new Document.DateComparator();
Jeff Sharkey873daa32013-08-18 17:38:20 -0700180 } else if (state.sortOrder == SORT_ORDER_NAME) {
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700181 sortOrder = new Document.NameComparator();
Jeff Sharkey873daa32013-08-18 17:38:20 -0700182 } else if (state.sortOrder == SORT_ORDER_SIZE) {
Jeff Sharkeyc6cbdf12013-08-07 16:22:02 -0700183 sortOrder = new Document.SizeComparator();
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700184 } else {
185 throw new IllegalArgumentException("Unknown sort order " + state.sortOrder);
186 }
187
Jeff Sharkeyc6cbdf12013-08-07 16:22:02 -0700188 return new DirectoryLoader(context, contentsUri, mType, null, sortOrder);
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700189 }
190
191 @Override
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700192 public void onLoadFinished(Loader<List<Document>> loader, List<Document> data) {
193 mAdapter.swapDocuments(data);
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700194 }
195
196 @Override
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700197 public void onLoaderReset(Loader<List<Document>> loader) {
198 mAdapter.swapDocuments(null);
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700199 }
200 };
201
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700202 updateDisplayState();
203
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700204 return view;
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700205 }
206
207 @Override
208 public void onStart() {
209 super.onStart();
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700210 getLoaderManager().restartLoader(mLoaderId, getArguments(), mCallbacks);
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700211 }
212
213 @Override
214 public void onStop() {
215 super.onStop();
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700216 getLoaderManager().destroyLoader(mLoaderId);
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700217 }
218
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700219 public void updateDisplayState() {
Jeff Sharkey3c28b792013-07-01 17:22:02 -0700220 final DisplayState state = getDisplayState(this);
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700221
Jeff Sharkeyc6cbdf12013-08-07 16:22:02 -0700222 // TODO: avoid kicking loader when nothing changed
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700223 getLoaderManager().restartLoader(mLoaderId, getArguments(), mCallbacks);
224 mListView.smoothScrollToPosition(0);
225 mGridView.smoothScrollToPosition(0);
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700226
Jeff Sharkey873daa32013-08-18 17:38:20 -0700227 mListView.setVisibility(state.mode == MODE_LIST ? View.VISIBLE : View.GONE);
228 mGridView.setVisibility(state.mode == MODE_GRID ? View.VISIBLE : View.GONE);
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700229
Jeff Sharkeyc317af82013-07-01 16:56:54 -0700230 final int choiceMode;
Jeff Sharkey3c28b792013-07-01 17:22:02 -0700231 if (state.allowMultiple) {
Jeff Sharkeyc317af82013-07-01 16:56:54 -0700232 choiceMode = ListView.CHOICE_MODE_MULTIPLE_MODAL;
233 } else {
234 choiceMode = ListView.CHOICE_MODE_NONE;
235 }
236
Jeff Sharkey8a8fb672013-05-07 12:41:33 -0700237 final int thumbSize;
Jeff Sharkey873daa32013-08-18 17:38:20 -0700238 if (state.mode == MODE_GRID) {
Jeff Sharkey8a8fb672013-05-07 12:41:33 -0700239 thumbSize = getResources().getDimensionPixelSize(R.dimen.grid_width);
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700240 mListView.setAdapter(null);
Jeff Sharkeyc317af82013-07-01 16:56:54 -0700241 mListView.setChoiceMode(ListView.CHOICE_MODE_NONE);
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700242 mGridView.setAdapter(mAdapter);
Jeff Sharkeyc317af82013-07-01 16:56:54 -0700243 mGridView.setColumnWidth(getResources().getDimensionPixelSize(R.dimen.grid_width));
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700244 mGridView.setNumColumns(GridView.AUTO_FIT);
Jeff Sharkeyc317af82013-07-01 16:56:54 -0700245 mGridView.setChoiceMode(choiceMode);
246 mCurrentView = mGridView;
Jeff Sharkey873daa32013-08-18 17:38:20 -0700247 } else if (state.mode == MODE_LIST) {
Jeff Sharkey8a8fb672013-05-07 12:41:33 -0700248 thumbSize = getResources().getDimensionPixelSize(android.R.dimen.app_icon_size);
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700249 mGridView.setAdapter(null);
Jeff Sharkeyc317af82013-07-01 16:56:54 -0700250 mGridView.setChoiceMode(ListView.CHOICE_MODE_NONE);
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700251 mListView.setAdapter(mAdapter);
Jeff Sharkeyc317af82013-07-01 16:56:54 -0700252 mListView.setChoiceMode(choiceMode);
253 mCurrentView = mListView;
Jeff Sharkey3c28b792013-07-01 17:22:02 -0700254 } else {
255 throw new IllegalStateException();
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700256 }
Jeff Sharkey8a8fb672013-05-07 12:41:33 -0700257
258 mThumbSize = new Point(thumbSize, thumbSize);
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700259 }
260
261 private OnItemClickListener mItemListener = new OnItemClickListener() {
262 @Override
263 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700264 final Document doc = mAdapter.getItem(position);
Jeff Sharkeyf339f252013-08-15 16:17:41 -0700265 if (mFilter.apply(doc)) {
266 ((DocumentsActivity) getActivity()).onDocumentPicked(doc);
267 }
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700268 }
269 };
270
Jeff Sharkeyc317af82013-07-01 16:56:54 -0700271 private MultiChoiceModeListener mMultiListener = new MultiChoiceModeListener() {
272 @Override
273 public boolean onCreateActionMode(ActionMode mode, Menu menu) {
274 mode.getMenuInflater().inflate(R.menu.mode_directory, menu);
275 return true;
276 }
277
278 @Override
279 public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
Jeff Sharkey873daa32013-08-18 17:38:20 -0700280 final DisplayState state = getDisplayState(DirectoryFragment.this);
281
282 final MenuItem open = menu.findItem(R.id.menu_open);
283 final MenuItem share = menu.findItem(R.id.menu_share);
284 final MenuItem delete = menu.findItem(R.id.menu_delete);
285
286 final boolean manageMode = state.action == ACTION_MANAGE;
287 open.setVisible(!manageMode);
288 share.setVisible(manageMode);
289 delete.setVisible(manageMode);
290
Jeff Sharkeyc317af82013-07-01 16:56:54 -0700291 return true;
292 }
293
294 @Override
295 public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
Jeff Sharkey873daa32013-08-18 17:38:20 -0700296 final SparseBooleanArray checked = mCurrentView.getCheckedItemPositions();
297 final ArrayList<Document> docs = Lists.newArrayList();
298 final int size = checked.size();
299 for (int i = 0; i < size; i++) {
300 if (checked.valueAt(i)) {
301 final Document doc = mAdapter.getItem(checked.keyAt(i));
302 docs.add(doc);
Jeff Sharkeyc317af82013-07-01 16:56:54 -0700303 }
Jeff Sharkey873daa32013-08-18 17:38:20 -0700304 }
Jeff Sharkeyc317af82013-07-01 16:56:54 -0700305
Jeff Sharkey873daa32013-08-18 17:38:20 -0700306 final int id = item.getItemId();
307 if (id == R.id.menu_open) {
308 DocumentsActivity.get(DirectoryFragment.this).onDocumentsPicked(docs);
Jeff Sharkeyc317af82013-07-01 16:56:54 -0700309 return true;
Jeff Sharkey873daa32013-08-18 17:38:20 -0700310
311 } else if (id == R.id.menu_share) {
312 onShareDocuments(docs);
313 return true;
314
315 } else if (id == R.id.menu_delete) {
316 onDeleteDocuments(docs);
317 return true;
318
Jeff Sharkeyc317af82013-07-01 16:56:54 -0700319 } else {
320 return false;
321 }
322 }
323
324 @Override
325 public void onDestroyActionMode(ActionMode mode) {
326 // ignored
327 }
328
329 @Override
330 public void onItemCheckedStateChanged(
331 ActionMode mode, int position, long id, boolean checked) {
332 if (checked) {
Jeff Sharkeyc317af82013-07-01 16:56:54 -0700333 // Directories cannot be checked
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700334 final Document doc = mAdapter.getItem(position);
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700335 if (doc.isDirectory()) {
Jeff Sharkeyc317af82013-07-01 16:56:54 -0700336 mCurrentView.setItemChecked(position, false);
337 }
338 }
339
340 mode.setTitle(getResources()
341 .getString(R.string.mode_selected_count, mCurrentView.getCheckedItemCount()));
342 }
343 };
344
Jeff Sharkey873daa32013-08-18 17:38:20 -0700345 private void onShareDocuments(List<Document> docs) {
346 final ArrayList<Uri> uris = Lists.newArrayList();
347 for (Document doc : docs) {
348 uris.add(doc.uri);
349 }
350
351 final Intent intent;
352 if (uris.size() > 1) {
353 intent = new Intent(Intent.ACTION_SEND_MULTIPLE);
354 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
355 intent.addCategory(Intent.CATEGORY_DEFAULT);
356 // TODO: find common mimetype
357 intent.setType("*/*");
358 intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris);
359 } else {
360 intent = new Intent(Intent.ACTION_SEND);
361 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
362 intent.addCategory(Intent.CATEGORY_DEFAULT);
363 intent.setData(uris.get(0));
364 }
365
366 startActivity(intent);
367 }
368
369 private void onDeleteDocuments(List<Document> docs) {
370 final Context context = getActivity();
371 final ContentResolver resolver = context.getContentResolver();
372
373 boolean hadTrouble = false;
374 for (Document doc : docs) {
375 if (!doc.isDeleteSupported()) {
376 Log.w(TAG, "Skipping " + doc);
377 hadTrouble = true;
378 continue;
379 }
380
381 try {
382 if (resolver.delete(doc.uri, null, null) != 1) {
383 Log.w(TAG, "Failed to delete " + doc);
384 hadTrouble = true;
385 }
386 } catch (Exception e) {
387 Log.w(TAG, "Failed to delete " + doc + ": " + e);
388 hadTrouble = true;
389 }
390 }
391
392 if (hadTrouble) {
393 Toast.makeText(context, R.string.toast_failed_delete, Toast.LENGTH_SHORT).show();
394 }
395 }
396
Jeff Sharkey3c28b792013-07-01 17:22:02 -0700397 private static DisplayState getDisplayState(Fragment fragment) {
398 return ((DocumentsActivity) fragment.getActivity()).getDisplayState();
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700399 }
400
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700401 private class DocumentsAdapter extends BaseAdapter {
402 private List<Document> mDocuments;
403
404 public DocumentsAdapter() {
405 }
406
407 public void swapDocuments(List<Document> documents) {
408 mDocuments = documents;
Jeff Sharkeyc6cbdf12013-08-07 16:22:02 -0700409
410 if (documents != null && documents.isEmpty()) {
411 mEmptyView.setVisibility(View.VISIBLE);
412 } else {
413 mEmptyView.setVisibility(View.GONE);
414 }
415
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700416 notifyDataSetChanged();
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700417 }
418
419 @Override
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700420 public View getView(int position, View convertView, ViewGroup parent) {
421 final Context context = parent.getContext();
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700422 final DisplayState state = getDisplayState(DirectoryFragment.this);
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700423
Jeff Sharkey873daa32013-08-18 17:38:20 -0700424 final RootsCache roots = DocumentsApplication.getRootsCache(context);
425 final ThumbnailCache thumbs = DocumentsApplication.getThumbnailsCache(
426 context, mThumbSize);
427
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700428 if (convertView == null) {
429 final LayoutInflater inflater = LayoutInflater.from(context);
Jeff Sharkey873daa32013-08-18 17:38:20 -0700430 if (state.mode == MODE_LIST) {
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700431 convertView = inflater.inflate(R.layout.item_doc_list, parent, false);
Jeff Sharkey873daa32013-08-18 17:38:20 -0700432 } else if (state.mode == MODE_GRID) {
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700433 convertView = inflater.inflate(R.layout.item_doc_grid, parent, false);
434 } else {
435 throw new IllegalStateException();
436 }
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700437 }
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700438
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700439 final Document doc = getItem(position);
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700440
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700441 final ImageView icon = (ImageView) convertView.findViewById(android.R.id.icon);
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700442 final TextView title = (TextView) convertView.findViewById(android.R.id.title);
Jeff Sharkeyc6cbdf12013-08-07 16:22:02 -0700443 final View summaryGrid = convertView.findViewById(R.id.summary_grid);
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700444 final ImageView icon1 = (ImageView) convertView.findViewById(android.R.id.icon1);
445 final TextView summary = (TextView) convertView.findViewById(android.R.id.summary);
446 final TextView date = (TextView) convertView.findViewById(R.id.date);
447 final TextView size = (TextView) convertView.findViewById(R.id.size);
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700448
Jeff Sharkey8a8fb672013-05-07 12:41:33 -0700449 final ThumbnailAsyncTask oldTask = (ThumbnailAsyncTask) icon.getTag();
450 if (oldTask != null) {
451 oldTask.cancel(false);
452 }
453
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700454 if (doc.isThumbnailSupported()) {
Jeff Sharkey873daa32013-08-18 17:38:20 -0700455 final Bitmap cachedResult = thumbs.get(doc.uri);
Jeff Sharkey8a8fb672013-05-07 12:41:33 -0700456 if (cachedResult != null) {
457 icon.setImageBitmap(cachedResult);
458 } else {
459 final ThumbnailAsyncTask task = new ThumbnailAsyncTask(icon, mThumbSize);
460 icon.setImageBitmap(null);
461 icon.setTag(task);
462 task.execute(doc.uri);
463 }
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700464 } else {
Jeff Sharkey873daa32013-08-18 17:38:20 -0700465 icon.setImageDrawable(roots.resolveDocumentIcon(
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700466 context, doc.uri.getAuthority(), doc.mimeType));
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700467 }
468
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700469 title.setText(doc.displayName);
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700470
471 if (mType == TYPE_NORMAL || mType == TYPE_SEARCH) {
472 icon1.setVisibility(View.GONE);
473 if (doc.summary != null) {
474 summary.setText(doc.summary);
475 summary.setVisibility(View.VISIBLE);
476 } else {
477 summary.setVisibility(View.INVISIBLE);
478 }
479 } else if (mType == TYPE_RECENT_OPEN) {
Jeff Sharkey873daa32013-08-18 17:38:20 -0700480 final Root root = roots.findRoot(doc);
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700481 icon1.setVisibility(View.VISIBLE);
482 icon1.setImageDrawable(root.icon);
483 summary.setText(root.getDirectoryString());
484 summary.setVisibility(View.VISIBLE);
485 }
486
Jeff Sharkeyc6cbdf12013-08-07 16:22:02 -0700487 if (summaryGrid != null) {
488 summaryGrid.setVisibility(
489 (summary.getVisibility() == View.VISIBLE) ? View.VISIBLE : View.GONE);
490 }
491
Jeff Sharkey8a8fb672013-05-07 12:41:33 -0700492 if (doc.lastModified == -1) {
493 date.setText(null);
494 } else {
495 date.setText(formatTime(context, doc.lastModified));
496 }
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700497
498 if (state.showSize) {
499 size.setVisibility(View.VISIBLE);
Jeff Sharkeyf339f252013-08-15 16:17:41 -0700500 if (doc.isDirectory() || doc.size == -1) {
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700501 size.setText(null);
502 } else {
503 size.setText(Formatter.formatFileSize(context, doc.size));
504 }
505 } else {
506 size.setVisibility(View.GONE);
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700507 }
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700508
509 return convertView;
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700510 }
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700511
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700512 @Override
513 public int getCount() {
514 return mDocuments != null ? mDocuments.size() : 0;
515 }
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700516
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700517 @Override
518 public Document getItem(int position) {
519 return mDocuments.get(position);
520 }
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700521
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700522 @Override
523 public long getItemId(int position) {
524 return getItem(position).uri.hashCode();
525 }
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700526 }
Jeff Sharkey8a8fb672013-05-07 12:41:33 -0700527
528 private static class ThumbnailAsyncTask extends AsyncTask<Uri, Void, Bitmap> {
529 private final ImageView mTarget;
Jeff Sharkey873daa32013-08-18 17:38:20 -0700530 private final Point mThumbSize;
Jeff Sharkey8a8fb672013-05-07 12:41:33 -0700531
Jeff Sharkey873daa32013-08-18 17:38:20 -0700532 public ThumbnailAsyncTask(ImageView target, Point thumbSize) {
Jeff Sharkey8a8fb672013-05-07 12:41:33 -0700533 mTarget = target;
Jeff Sharkey873daa32013-08-18 17:38:20 -0700534 mThumbSize = thumbSize;
Jeff Sharkey8a8fb672013-05-07 12:41:33 -0700535 }
536
537 @Override
538 protected void onPreExecute() {
539 mTarget.setTag(this);
540 }
541
542 @Override
543 protected Bitmap doInBackground(Uri... params) {
544 final Context context = mTarget.getContext();
545 final Uri uri = params[0];
546
547 Bitmap result = null;
548 try {
549 result = DocumentsContract.getThumbnail(
Jeff Sharkey873daa32013-08-18 17:38:20 -0700550 context.getContentResolver(), uri, mThumbSize);
Jeff Sharkey8a8fb672013-05-07 12:41:33 -0700551 if (result != null) {
Jeff Sharkey873daa32013-08-18 17:38:20 -0700552 final ThumbnailCache thumbs = DocumentsApplication.getThumbnailsCache(
553 context, mThumbSize);
554 thumbs.put(uri, result);
Jeff Sharkey8a8fb672013-05-07 12:41:33 -0700555 }
556 } catch (Exception e) {
557 Log.w(TAG, "Failed to load thumbnail: " + e);
558 }
559 return result;
560 }
561
562 @Override
563 protected void onPostExecute(Bitmap result) {
564 if (mTarget.getTag() == this) {
565 mTarget.setImageBitmap(result);
566 mTarget.setTag(null);
567 }
568 }
569 }
570
571 private static String formatTime(Context context, long when) {
572 // TODO: DateUtils should make this easier
573 Time then = new Time();
574 then.set(when);
575 Time now = new Time();
576 now.setToNow();
577
578 int flags = DateUtils.FORMAT_NO_NOON | DateUtils.FORMAT_NO_MIDNIGHT
579 | DateUtils.FORMAT_ABBREV_ALL;
580
581 if (then.year != now.year) {
582 flags |= DateUtils.FORMAT_SHOW_YEAR | DateUtils.FORMAT_SHOW_DATE;
583 } else if (then.yearDay != now.yearDay) {
584 flags |= DateUtils.FORMAT_SHOW_DATE;
585 } else {
586 flags |= DateUtils.FORMAT_SHOW_TIME;
587 }
588
589 return DateUtils.formatDateTime(context, when, flags);
590 }
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700591}