DirectoryFragment.java revision 0b14db3cf5eac43736462999337c9a3efdc1ac81
1/* 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 19import static com.android.documentsui.DocumentsActivity.TAG; 20import static com.android.documentsui.DocumentsActivity.State.ACTION_MANAGE; 21import static com.android.documentsui.DocumentsActivity.State.MODE_GRID; 22import static com.android.documentsui.DocumentsActivity.State.MODE_LIST; 23import static com.android.documentsui.model.DocumentInfo.getCursorInt; 24import static com.android.documentsui.model.DocumentInfo.getCursorLong; 25import static com.android.documentsui.model.DocumentInfo.getCursorString; 26 27import android.app.Fragment; 28import android.app.FragmentManager; 29import android.app.FragmentTransaction; 30import android.app.LoaderManager.LoaderCallbacks; 31import android.content.ContentResolver; 32import android.content.Context; 33import android.content.Intent; 34import android.content.Loader; 35import android.database.Cursor; 36import android.graphics.Bitmap; 37import android.graphics.Point; 38import android.net.Uri; 39import android.os.AsyncTask; 40import android.os.Bundle; 41import android.provider.DocumentsContract; 42import android.provider.DocumentsContract.Document; 43import android.text.format.DateUtils; 44import android.text.format.Formatter; 45import android.text.format.Time; 46import android.util.Log; 47import android.util.SparseBooleanArray; 48import android.view.ActionMode; 49import android.view.LayoutInflater; 50import android.view.Menu; 51import android.view.MenuItem; 52import android.view.View; 53import android.view.ViewGroup; 54import android.widget.AbsListView; 55import android.widget.AbsListView.MultiChoiceModeListener; 56import android.widget.AdapterView; 57import android.widget.AdapterView.OnItemClickListener; 58import android.widget.BaseAdapter; 59import android.widget.GridView; 60import android.widget.ImageView; 61import android.widget.ListView; 62import android.widget.TextView; 63import android.widget.Toast; 64 65import com.android.documentsui.DocumentsActivity.State; 66import com.android.documentsui.model.DocumentInfo; 67import com.android.documentsui.model.RootInfo; 68import com.android.internal.util.Predicate; 69import com.google.android.collect.Lists; 70 71import java.util.ArrayList; 72import java.util.List; 73import java.util.concurrent.atomic.AtomicInteger; 74 75/** 76 * Display the documents inside a single directory. 77 */ 78public class DirectoryFragment extends Fragment { 79 80 private View mEmptyView; 81 private ListView mListView; 82 private GridView mGridView; 83 84 private AbsListView mCurrentView; 85 86 private Predicate<DocumentInfo> mFilter; 87 88 public static final int TYPE_NORMAL = 1; 89 public static final int TYPE_SEARCH = 2; 90 public static final int TYPE_RECENT_OPEN = 3; 91 92 private int mType = TYPE_NORMAL; 93 94 private Point mThumbSize; 95 96 private DocumentsAdapter mAdapter; 97 private LoaderCallbacks<DirectoryResult> mCallbacks; 98 99 private static final String EXTRA_TYPE = "type"; 100 private static final String EXTRA_AUTHORITY = "authority"; 101 private static final String EXTRA_ROOT_ID = "rootId"; 102 private static final String EXTRA_DOC_ID = "docId"; 103 private static final String EXTRA_QUERY = "query"; 104 105 private static AtomicInteger sLoaderId = new AtomicInteger(4000); 106 107 private int mLastSortOrder = -1; 108 109 private final int mLoaderId = sLoaderId.incrementAndGet(); 110 111 public static void showNormal(FragmentManager fm, Uri uri) { 112 show(fm, TYPE_NORMAL, uri.getAuthority(), null, DocumentsContract.getDocumentId(uri), null); 113 } 114 115 public static void showSearch(FragmentManager fm, Uri uri, String query) { 116 show(fm, TYPE_SEARCH, uri.getAuthority(), null, DocumentsContract.getDocumentId(uri), 117 query); 118 } 119 120 public static void showRecentsOpen(FragmentManager fm) { 121 show(fm, TYPE_RECENT_OPEN, null, null, null, null); 122 } 123 124 private static void show(FragmentManager fm, int type, String authority, String rootId, 125 String docId, String query) { 126 final Bundle args = new Bundle(); 127 args.putInt(EXTRA_TYPE, type); 128 args.putString(EXTRA_AUTHORITY, authority); 129 args.putString(EXTRA_ROOT_ID, rootId); 130 args.putString(EXTRA_DOC_ID, docId); 131 args.putString(EXTRA_QUERY, query); 132 133 final DirectoryFragment fragment = new DirectoryFragment(); 134 fragment.setArguments(args); 135 136 final FragmentTransaction ft = fm.beginTransaction(); 137 ft.replace(R.id.container_directory, fragment); 138 ft.commitAllowingStateLoss(); 139 } 140 141 public static DirectoryFragment get(FragmentManager fm) { 142 // TODO: deal with multiple directories shown at once 143 return (DirectoryFragment) fm.findFragmentById(R.id.container_directory); 144 } 145 146 @Override 147 public View onCreateView( 148 LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 149 final Context context = inflater.getContext(); 150 final View view = inflater.inflate(R.layout.fragment_directory, container, false); 151 152 mEmptyView = view.findViewById(android.R.id.empty); 153 154 mListView = (ListView) view.findViewById(R.id.list); 155 mListView.setOnItemClickListener(mItemListener); 156 mListView.setMultiChoiceModeListener(mMultiListener); 157 158 mGridView = (GridView) view.findViewById(R.id.grid); 159 mGridView.setOnItemClickListener(mItemListener); 160 mGridView.setMultiChoiceModeListener(mMultiListener); 161 162 return view; 163 } 164 165 @Override 166 public void onActivityCreated(Bundle savedInstanceState) { 167 super.onActivityCreated(savedInstanceState); 168 169 final Context context = getActivity(); 170 171 mAdapter = new DocumentsAdapter(); 172 mType = getArguments().getInt(EXTRA_TYPE); 173 174 mCallbacks = new LoaderCallbacks<DirectoryResult>() { 175 @Override 176 public Loader<DirectoryResult> onCreateLoader(int id, Bundle args) { 177 final State state = getDisplayState(DirectoryFragment.this); 178 179 final String authority = getArguments().getString(EXTRA_AUTHORITY); 180 final String rootId = getArguments().getString(EXTRA_ROOT_ID); 181 final String docId = getArguments().getString(EXTRA_DOC_ID); 182 final String query = getArguments().getString(EXTRA_QUERY); 183 184 Uri contentsUri; 185 switch (mType) { 186 case TYPE_NORMAL: 187 contentsUri = DocumentsContract.buildChildDocumentsUri(authority, docId); 188 return new DirectoryLoader(context, rootId, contentsUri, state.sortOrder); 189 case TYPE_SEARCH: 190 contentsUri = DocumentsContract.buildSearchDocumentsUri( 191 authority, docId, query); 192 return new DirectoryLoader(context, rootId, contentsUri, state.sortOrder); 193 case TYPE_RECENT_OPEN: 194 final RootsCache roots = DocumentsApplication.getRootsCache(context); 195 final List<RootInfo> matchingRoots = roots.getMatchingRoots(state); 196 return new RecentLoader(context, matchingRoots); 197 default: 198 throw new IllegalStateException("Unknown type " + mType); 199 200 } 201 } 202 203 @Override 204 public void onLoadFinished(Loader<DirectoryResult> loader, DirectoryResult result) { 205 mAdapter.swapCursor(result.cursor); 206 } 207 208 @Override 209 public void onLoaderReset(Loader<DirectoryResult> loader) { 210 mAdapter.swapCursor(null); 211 } 212 }; 213 214 updateDisplayState(); 215 } 216 217 public void updateDisplayState() { 218 final State state = getDisplayState(this); 219 220 if (mLastSortOrder != state.sortOrder) { 221 getLoaderManager().restartLoader(mLoaderId, null, mCallbacks); 222 mLastSortOrder = state.sortOrder; 223 } 224 225 mListView.smoothScrollToPosition(0); 226 mGridView.smoothScrollToPosition(0); 227 228 mListView.setVisibility(state.mode == MODE_LIST ? View.VISIBLE : View.GONE); 229 mGridView.setVisibility(state.mode == MODE_GRID ? View.VISIBLE : View.GONE); 230 231 mFilter = new MimePredicate(state.acceptMimes); 232 233 final int choiceMode; 234 if (state.allowMultiple) { 235 choiceMode = ListView.CHOICE_MODE_MULTIPLE_MODAL; 236 } else { 237 choiceMode = ListView.CHOICE_MODE_NONE; 238 } 239 240 final int thumbSize; 241 if (state.mode == MODE_GRID) { 242 thumbSize = getResources().getDimensionPixelSize(R.dimen.grid_width); 243 mListView.setAdapter(null); 244 mListView.setChoiceMode(ListView.CHOICE_MODE_NONE); 245 mGridView.setAdapter(mAdapter); 246 mGridView.setColumnWidth(getResources().getDimensionPixelSize(R.dimen.grid_width)); 247 mGridView.setNumColumns(GridView.AUTO_FIT); 248 mGridView.setChoiceMode(choiceMode); 249 mCurrentView = mGridView; 250 } else if (state.mode == MODE_LIST) { 251 thumbSize = getResources().getDimensionPixelSize(android.R.dimen.app_icon_size); 252 mGridView.setAdapter(null); 253 mGridView.setChoiceMode(ListView.CHOICE_MODE_NONE); 254 mListView.setAdapter(mAdapter); 255 mListView.setChoiceMode(choiceMode); 256 mCurrentView = mListView; 257 } else { 258 throw new IllegalStateException(); 259 } 260 261 mThumbSize = new Point(thumbSize, thumbSize); 262 } 263 264 private OnItemClickListener mItemListener = new OnItemClickListener() { 265 @Override 266 public void onItemClick(AdapterView<?> parent, View view, int position, long id) { 267 final Cursor cursor = mAdapter.getItem(position); 268 final DocumentInfo doc = DocumentInfo.fromDirectoryCursor(cursor); 269 if (mFilter.apply(doc)) { 270 ((DocumentsActivity) getActivity()).onDocumentPicked(doc); 271 } 272 } 273 }; 274 275 private MultiChoiceModeListener mMultiListener = new MultiChoiceModeListener() { 276 @Override 277 public boolean onCreateActionMode(ActionMode mode, Menu menu) { 278 mode.getMenuInflater().inflate(R.menu.mode_directory, menu); 279 return true; 280 } 281 282 @Override 283 public boolean onPrepareActionMode(ActionMode mode, Menu menu) { 284 final State state = getDisplayState(DirectoryFragment.this); 285 286 final MenuItem open = menu.findItem(R.id.menu_open); 287 final MenuItem share = menu.findItem(R.id.menu_share); 288 final MenuItem delete = menu.findItem(R.id.menu_delete); 289 290 final boolean manageMode = state.action == ACTION_MANAGE; 291 open.setVisible(!manageMode); 292 share.setVisible(manageMode); 293 delete.setVisible(manageMode); 294 295 return true; 296 } 297 298 @Override 299 public boolean onActionItemClicked(ActionMode mode, MenuItem item) { 300 final SparseBooleanArray checked = mCurrentView.getCheckedItemPositions(); 301 final ArrayList<DocumentInfo> docs = Lists.newArrayList(); 302 final int size = checked.size(); 303 for (int i = 0; i < size; i++) { 304 if (checked.valueAt(i)) { 305 final Cursor cursor = mAdapter.getItem(checked.keyAt(i)); 306 final DocumentInfo doc = DocumentInfo.fromDirectoryCursor(cursor); 307 docs.add(doc); 308 } 309 } 310 311 final int id = item.getItemId(); 312 if (id == R.id.menu_open) { 313 DocumentsActivity.get(DirectoryFragment.this).onDocumentsPicked(docs); 314 mode.finish(); 315 return true; 316 317 } else if (id == R.id.menu_share) { 318 onShareDocuments(docs); 319 mode.finish(); 320 return true; 321 322 } else if (id == R.id.menu_delete) { 323 onDeleteDocuments(docs); 324 mode.finish(); 325 return true; 326 327 } else { 328 return false; 329 } 330 } 331 332 @Override 333 public void onDestroyActionMode(ActionMode mode) { 334 // ignored 335 } 336 337 @Override 338 public void onItemCheckedStateChanged( 339 ActionMode mode, int position, long id, boolean checked) { 340 if (checked) { 341 // Directories cannot be checked 342 final Cursor cursor = mAdapter.getItem(position); 343 final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE); 344 if (Document.MIME_TYPE_DIR.equals(docMimeType)) { 345 mCurrentView.setItemChecked(position, false); 346 } 347 } 348 349 mode.setTitle(getResources() 350 .getString(R.string.mode_selected_count, mCurrentView.getCheckedItemCount())); 351 } 352 }; 353 354 private void onShareDocuments(List<DocumentInfo> docs) { 355 Intent intent; 356 if (docs.size() == 1) { 357 final DocumentInfo doc = docs.get(0); 358 359 intent = new Intent(Intent.ACTION_SEND); 360 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 361 intent.addCategory(Intent.CATEGORY_DEFAULT); 362 intent.setType(doc.mimeType); 363 intent.putExtra(Intent.EXTRA_STREAM, doc.uri); 364 365 } else if (docs.size() > 1) { 366 intent = new Intent(Intent.ACTION_SEND_MULTIPLE); 367 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 368 intent.addCategory(Intent.CATEGORY_DEFAULT); 369 370 final ArrayList<String> mimeTypes = Lists.newArrayList(); 371 final ArrayList<Uri> uris = Lists.newArrayList(); 372 for (DocumentInfo doc : docs) { 373 mimeTypes.add(doc.mimeType); 374 uris.add(doc.uri); 375 } 376 377 intent.setType(findCommonMimeType(mimeTypes)); 378 intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris); 379 380 } else { 381 return; 382 } 383 384 intent = Intent.createChooser(intent, getActivity().getText(R.string.share_via)); 385 startActivity(intent); 386 } 387 388 private void onDeleteDocuments(List<DocumentInfo> docs) { 389 final Context context = getActivity(); 390 final ContentResolver resolver = context.getContentResolver(); 391 392 boolean hadTrouble = false; 393 for (DocumentInfo doc : docs) { 394 if (!doc.isDeleteSupported()) { 395 Log.w(TAG, "Skipping " + doc); 396 hadTrouble = true; 397 continue; 398 } 399 400 if (!DocumentsContract.deleteDocument(resolver, doc.uri)) { 401 Log.w(TAG, "Failed to delete " + doc); 402 hadTrouble = true; 403 } 404 } 405 406 if (hadTrouble) { 407 Toast.makeText(context, R.string.toast_failed_delete, Toast.LENGTH_SHORT).show(); 408 } 409 } 410 411 private static State getDisplayState(Fragment fragment) { 412 return ((DocumentsActivity) fragment.getActivity()).getDisplayState(); 413 } 414 415 private interface Footer { 416 public View getView(View convertView, ViewGroup parent); 417 } 418 419 private static class LoadingFooter implements Footer { 420 @Override 421 public View getView(View convertView, ViewGroup parent) { 422 final Context context = parent.getContext(); 423 if (convertView == null) { 424 final LayoutInflater inflater = LayoutInflater.from(context); 425 convertView = inflater.inflate(R.layout.item_loading, parent, false); 426 } 427 return convertView; 428 } 429 } 430 431 private class MessageFooter implements Footer { 432 private final int mIcon; 433 private final String mMessage; 434 435 public MessageFooter(int icon, String message) { 436 mIcon = icon; 437 mMessage = message; 438 } 439 440 @Override 441 public View getView(View convertView, ViewGroup parent) { 442 final Context context = parent.getContext(); 443 final State state = getDisplayState(DirectoryFragment.this); 444 445 if (convertView == null) { 446 final LayoutInflater inflater = LayoutInflater.from(context); 447 if (state.mode == MODE_LIST) { 448 convertView = inflater.inflate(R.layout.item_message_list, parent, false); 449 } else if (state.mode == MODE_GRID) { 450 convertView = inflater.inflate(R.layout.item_message_grid, parent, false); 451 } else { 452 throw new IllegalStateException(); 453 } 454 } 455 456 final ImageView icon = (ImageView) convertView.findViewById(android.R.id.icon); 457 final TextView title = (TextView) convertView.findViewById(android.R.id.title); 458 icon.setImageResource(mIcon); 459 title.setText(mMessage); 460 return convertView; 461 } 462 } 463 464 private class DocumentsAdapter extends BaseAdapter { 465 private Cursor mCursor; 466 private int mCursorCount; 467 468 private List<Footer> mFooters = Lists.newArrayList(); 469 470 public void swapCursor(Cursor cursor) { 471 mCursor = cursor; 472 mCursorCount = cursor != null ? cursor.getCount() : 0; 473 474 mFooters.clear(); 475 476 final Bundle extras = cursor != null ? cursor.getExtras() : null; 477 if (extras != null) { 478 final String info = extras.getString(DocumentsContract.EXTRA_INFO); 479 if (info != null) { 480 mFooters.add(new MessageFooter( 481 com.android.internal.R.drawable.ic_menu_info_details, info)); 482 } 483 final String error = extras.getString(DocumentsContract.EXTRA_ERROR); 484 if (error != null) { 485 mFooters.add(new MessageFooter( 486 com.android.internal.R.drawable.ic_dialog_alert, error)); 487 } 488 if (extras.getBoolean(DocumentsContract.EXTRA_LOADING, false)) { 489 mFooters.add(new LoadingFooter()); 490 } 491 } 492 493 if (isEmpty()) { 494 mEmptyView.setVisibility(View.VISIBLE); 495 } else { 496 mEmptyView.setVisibility(View.GONE); 497 } 498 499 notifyDataSetChanged(); 500 } 501 502 @Override 503 public View getView(int position, View convertView, ViewGroup parent) { 504 if (position < mCursorCount) { 505 return getDocumentView(position, convertView, parent); 506 } else { 507 position -= mCursorCount; 508 return mFooters.get(position).getView(convertView, parent); 509 } 510 } 511 512 private View getDocumentView(int position, View convertView, ViewGroup parent) { 513 final Context context = parent.getContext(); 514 final State state = getDisplayState(DirectoryFragment.this); 515 516 final RootsCache roots = DocumentsApplication.getRootsCache(context); 517 final ThumbnailCache thumbs = DocumentsApplication.getThumbnailsCache( 518 context, mThumbSize); 519 520 if (convertView == null) { 521 final LayoutInflater inflater = LayoutInflater.from(context); 522 if (state.mode == MODE_LIST) { 523 convertView = inflater.inflate(R.layout.item_doc_list, parent, false); 524 } else if (state.mode == MODE_GRID) { 525 convertView = inflater.inflate(R.layout.item_doc_grid, parent, false); 526 } else { 527 throw new IllegalStateException(); 528 } 529 } 530 531 final Cursor cursor = getItem(position); 532 533 final String docAuthority = getCursorString(cursor, RootCursorWrapper.COLUMN_AUTHORITY); 534 final String docRootId = getCursorString(cursor, RootCursorWrapper.COLUMN_ROOT_ID); 535 final String docId = getCursorString(cursor, Document.COLUMN_DOCUMENT_ID); 536 final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE); 537 final String docDisplayName = getCursorString(cursor, Document.COLUMN_DISPLAY_NAME); 538 final long docLastModified = getCursorLong(cursor, Document.COLUMN_LAST_MODIFIED); 539 final int docIcon = getCursorInt(cursor, Document.COLUMN_ICON); 540 final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS); 541 final String docSummary = getCursorString(cursor, Document.COLUMN_SUMMARY); 542 final long docSize = getCursorLong(cursor, Document.COLUMN_SIZE); 543 544 final ImageView icon = (ImageView) convertView.findViewById(android.R.id.icon); 545 final TextView title = (TextView) convertView.findViewById(android.R.id.title); 546 final View summaryGrid = convertView.findViewById(R.id.summary_grid); 547 final ImageView icon1 = (ImageView) convertView.findViewById(android.R.id.icon1); 548 final TextView summary = (TextView) convertView.findViewById(android.R.id.summary); 549 final TextView date = (TextView) convertView.findViewById(R.id.date); 550 final TextView size = (TextView) convertView.findViewById(R.id.size); 551 552 final ThumbnailAsyncTask oldTask = (ThumbnailAsyncTask) icon.getTag(); 553 if (oldTask != null) { 554 oldTask.cancel(false); 555 } 556 557 if ((docFlags & Document.FLAG_SUPPORTS_THUMBNAIL) != 0) { 558 final Uri uri = DocumentsContract.buildDocumentUri(docAuthority, docId); 559 final Bitmap cachedResult = thumbs.get(uri); 560 if (cachedResult != null) { 561 icon.setImageBitmap(cachedResult); 562 } else { 563 final ThumbnailAsyncTask task = new ThumbnailAsyncTask(icon, mThumbSize); 564 icon.setImageBitmap(null); 565 icon.setTag(task); 566 task.execute(uri); 567 } 568 } else if (docIcon != 0) { 569 icon.setImageDrawable(IconUtils.loadPackageIcon(context, docAuthority, docIcon)); 570 } else { 571 icon.setImageDrawable(IconUtils.loadMimeIcon(context, docMimeType)); 572 } 573 574 title.setText(docDisplayName); 575 576 if (mType == TYPE_RECENT_OPEN) { 577 final RootInfo root = roots.getRoot(docAuthority, docRootId); 578 icon1.setVisibility(View.VISIBLE); 579 icon1.setImageDrawable(root.loadIcon(context)); 580 summary.setText(root.getDirectoryString()); 581 summary.setVisibility(View.VISIBLE); 582 } else { 583 icon1.setVisibility(View.GONE); 584 if (docSummary != null) { 585 summary.setText(docSummary); 586 summary.setVisibility(View.VISIBLE); 587 } else { 588 summary.setVisibility(View.INVISIBLE); 589 } 590 } 591 592 if (summaryGrid != null) { 593 summaryGrid.setVisibility( 594 (summary.getVisibility() == View.VISIBLE) ? View.VISIBLE : View.GONE); 595 } 596 597 if (docLastModified == -1) { 598 date.setText(null); 599 } else { 600 date.setText(formatTime(context, docLastModified)); 601 } 602 603 if (state.showSize) { 604 size.setVisibility(View.VISIBLE); 605 if (Document.MIME_TYPE_DIR.equals(docMimeType) || docSize == -1) { 606 size.setText(null); 607 } else { 608 size.setText(Formatter.formatFileSize(context, docSize)); 609 } 610 } else { 611 size.setVisibility(View.GONE); 612 } 613 614 return convertView; 615 } 616 617 @Override 618 public int getCount() { 619 return mCursorCount + mFooters.size(); 620 } 621 622 @Override 623 public Cursor getItem(int position) { 624 if (position < mCursorCount) { 625 mCursor.moveToPosition(position); 626 return mCursor; 627 } else { 628 return null; 629 } 630 } 631 632 @Override 633 public long getItemId(int position) { 634 return position; 635 } 636 637 @Override 638 public int getItemViewType(int position) { 639 if (position < mCursorCount) { 640 return 0; 641 } else { 642 return IGNORE_ITEM_VIEW_TYPE; 643 } 644 } 645 646 @Override 647 public boolean areAllItemsEnabled() { 648 return false; 649 } 650 651 @Override 652 public boolean isEnabled(int position) { 653 return position < mCursorCount; 654 } 655 } 656 657 private static class ThumbnailAsyncTask extends AsyncTask<Uri, Void, Bitmap> { 658 private final ImageView mTarget; 659 private final Point mThumbSize; 660 661 public ThumbnailAsyncTask(ImageView target, Point thumbSize) { 662 mTarget = target; 663 mThumbSize = thumbSize; 664 } 665 666 @Override 667 protected void onPreExecute() { 668 mTarget.setTag(this); 669 } 670 671 @Override 672 protected Bitmap doInBackground(Uri... params) { 673 final Context context = mTarget.getContext(); 674 final Uri uri = params[0]; 675 676 Bitmap result = null; 677 try { 678 result = DocumentsContract.getDocumentThumbnail( 679 context.getContentResolver(), uri, mThumbSize, null); 680 if (result != null) { 681 final ThumbnailCache thumbs = DocumentsApplication.getThumbnailsCache( 682 context, mThumbSize); 683 thumbs.put(uri, result); 684 } 685 } catch (Exception e) { 686 Log.w(TAG, "Failed to load thumbnail: " + e); 687 } 688 return result; 689 } 690 691 @Override 692 protected void onPostExecute(Bitmap result) { 693 if (mTarget.getTag() == this) { 694 mTarget.setImageBitmap(result); 695 mTarget.setTag(null); 696 } 697 } 698 } 699 700 private static String formatTime(Context context, long when) { 701 // TODO: DateUtils should make this easier 702 Time then = new Time(); 703 then.set(when); 704 Time now = new Time(); 705 now.setToNow(); 706 707 int flags = DateUtils.FORMAT_NO_NOON | DateUtils.FORMAT_NO_MIDNIGHT 708 | DateUtils.FORMAT_ABBREV_ALL; 709 710 if (then.year != now.year) { 711 flags |= DateUtils.FORMAT_SHOW_YEAR | DateUtils.FORMAT_SHOW_DATE; 712 } else if (then.yearDay != now.yearDay) { 713 flags |= DateUtils.FORMAT_SHOW_DATE; 714 } else { 715 flags |= DateUtils.FORMAT_SHOW_TIME; 716 } 717 718 return DateUtils.formatDateTime(context, when, flags); 719 } 720 721 private String findCommonMimeType(List<String> mimeTypes) { 722 String[] commonType = mimeTypes.get(0).split("/"); 723 if (commonType.length != 2) { 724 return "*/*"; 725 } 726 727 for (int i = 1; i < mimeTypes.size(); i++) { 728 String[] type = mimeTypes.get(i).split("/"); 729 if (type.length != 2) continue; 730 731 if (!commonType[1].equals(type[1])) { 732 commonType[1] = "*"; 733 } 734 735 if (!commonType[0].equals(type[0])) { 736 commonType[0] = "*"; 737 commonType[1] = "*"; 738 break; 739 } 740 } 741 742 return commonType[0] + "/" + commonType[1]; 743 } 744} 745