summaryrefslogtreecommitdiffstats
path: root/packages/DocumentsUI/src
diff options
context:
space:
mode:
authorJeff Sharkey <jsharkey@android.com>2013-05-07 12:41:33 -0700
committerJeff Sharkey <jsharkey@android.com>2013-08-17 19:05:07 -0700
commit9d0843df7e3984293dc4ab6ee2f9502e898b63aa (patch)
tree9487940fefdc3034d1e13838b0cca47ed5bce35b /packages/DocumentsUI/src
parenta5599ef636e37cb0b6474349936999be1afe6987 (diff)
downloadframeworks_base-9d0843df7e3984293dc4ab6ee2f9502e898b63aa.zip
frameworks_base-9d0843df7e3984293dc4ab6ee2f9502e898b63aa.tar.gz
frameworks_base-9d0843df7e3984293dc4ab6ee2f9502e898b63aa.tar.bz2
Resized thumbnails; async; extend MatrixCursor.
When requesting thumbnails, check if their dimensions are larger than requested, and downscale to avoid memory pressure. Load them async and with LruCache. Extend MatrixCursor so that RowBuilder can offer() columns without requiring they know the projection map. This makes it easier to respond to query() calls, where the remote side controls the projection map. Use it to handle custom projections in external storage backend. Update date/time formatting to match spec. Bug: 10333418, 10331689 Change-Id: I7e947a8e8068af8a39b55e6766b3241de4f3fc16
Diffstat (limited to 'packages/DocumentsUI/src')
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java101
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/ThumbnailCache.java46
2 files changed, 141 insertions, 6 deletions
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
index ac5629e..fbdb3a7 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
@@ -16,17 +16,24 @@
package com.android.documentsui;
+import static com.android.documentsui.DocumentsActivity.TAG;
+
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.app.LoaderManager.LoaderCallbacks;
import android.content.Context;
import android.content.Loader;
+import android.graphics.Bitmap;
+import android.graphics.Point;
import android.net.Uri;
+import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.DocumentsContract;
import android.text.format.DateUtils;
import android.text.format.Formatter;
+import android.text.format.Time;
+import android.util.Log;
import android.util.SparseBooleanArray;
import android.view.ActionMode;
import android.view.LayoutInflater;
@@ -75,6 +82,8 @@ public class DirectoryFragment extends Fragment {
private int mType = TYPE_NORMAL;
+ private Point mThumbSize;
+
private DocumentsAdapter mAdapter;
private LoaderCallbacks<List<Document>> mCallbacks;
@@ -217,7 +226,9 @@ public class DirectoryFragment extends Fragment {
choiceMode = ListView.CHOICE_MODE_NONE;
}
+ final int thumbSize;
if (state.mode == DisplayState.MODE_GRID) {
+ thumbSize = getResources().getDimensionPixelSize(R.dimen.grid_width);
mListView.setAdapter(null);
mListView.setChoiceMode(ListView.CHOICE_MODE_NONE);
mGridView.setAdapter(mAdapter);
@@ -226,6 +237,7 @@ public class DirectoryFragment extends Fragment {
mGridView.setChoiceMode(choiceMode);
mCurrentView = mGridView;
} else if (state.mode == DisplayState.MODE_LIST) {
+ thumbSize = getResources().getDimensionPixelSize(android.R.dimen.app_icon_size);
mGridView.setAdapter(null);
mGridView.setChoiceMode(ListView.CHOICE_MODE_NONE);
mListView.setAdapter(mAdapter);
@@ -234,6 +246,8 @@ public class DirectoryFragment extends Fragment {
} else {
throw new IllegalStateException();
}
+
+ mThumbSize = new Point(thumbSize, thumbSize);
}
private OnItemClickListener mItemListener = new OnItemClickListener() {
@@ -349,9 +363,21 @@ public class DirectoryFragment extends Fragment {
final TextView date = (TextView) convertView.findViewById(R.id.date);
final TextView size = (TextView) convertView.findViewById(R.id.size);
+ final ThumbnailAsyncTask oldTask = (ThumbnailAsyncTask) icon.getTag();
+ if (oldTask != null) {
+ oldTask.cancel(false);
+ }
+
if (doc.isThumbnailSupported()) {
- // TODO: load thumbnails async
- icon.setImageURI(doc.uri);
+ final Bitmap cachedResult = ThumbnailCache.get(context).get(doc.uri);
+ if (cachedResult != null) {
+ icon.setImageBitmap(cachedResult);
+ } else {
+ final ThumbnailAsyncTask task = new ThumbnailAsyncTask(icon, mThumbSize);
+ icon.setImageBitmap(null);
+ icon.setTag(task);
+ task.execute(doc.uri);
+ }
} else {
icon.setImageDrawable(RootsCache.resolveDocumentIcon(
context, doc.uri.getAuthority(), doc.mimeType));
@@ -380,10 +406,11 @@ public class DirectoryFragment extends Fragment {
(summary.getVisibility() == View.VISIBLE) ? View.VISIBLE : View.GONE);
}
- // TODO: omit year from format
- date.setText(DateUtils.formatSameDayTime(
- doc.lastModified, System.currentTimeMillis(), DateFormat.SHORT,
- DateFormat.SHORT));
+ if (doc.lastModified == -1) {
+ date.setText(null);
+ } else {
+ date.setText(formatTime(context, doc.lastModified));
+ }
if (state.showSize) {
size.setVisibility(View.VISIBLE);
@@ -414,4 +441,66 @@ public class DirectoryFragment extends Fragment {
return getItem(position).uri.hashCode();
}
}
+
+ private static class ThumbnailAsyncTask extends AsyncTask<Uri, Void, Bitmap> {
+ private final ImageView mTarget;
+ private final Point mSize;
+
+ public ThumbnailAsyncTask(ImageView target, Point size) {
+ mTarget = target;
+ mSize = size;
+ }
+
+ @Override
+ protected void onPreExecute() {
+ mTarget.setTag(this);
+ }
+
+ @Override
+ protected Bitmap doInBackground(Uri... params) {
+ final Context context = mTarget.getContext();
+ final Uri uri = params[0];
+
+ Bitmap result = null;
+ try {
+ result = DocumentsContract.getThumbnail(
+ context.getContentResolver(), uri, mSize);
+ if (result != null) {
+ ThumbnailCache.get(context).put(uri, result);
+ }
+ } catch (Exception e) {
+ Log.w(TAG, "Failed to load thumbnail: " + e);
+ }
+ return result;
+ }
+
+ @Override
+ protected void onPostExecute(Bitmap result) {
+ if (mTarget.getTag() == this) {
+ mTarget.setImageBitmap(result);
+ mTarget.setTag(null);
+ }
+ }
+ }
+
+ private static String formatTime(Context context, long when) {
+ // TODO: DateUtils should make this easier
+ Time then = new Time();
+ then.set(when);
+ Time now = new Time();
+ now.setToNow();
+
+ int flags = DateUtils.FORMAT_NO_NOON | DateUtils.FORMAT_NO_MIDNIGHT
+ | DateUtils.FORMAT_ABBREV_ALL;
+
+ if (then.year != now.year) {
+ flags |= DateUtils.FORMAT_SHOW_YEAR | DateUtils.FORMAT_SHOW_DATE;
+ } else if (then.yearDay != now.yearDay) {
+ flags |= DateUtils.FORMAT_SHOW_DATE;
+ } else {
+ flags |= DateUtils.FORMAT_SHOW_TIME;
+ }
+
+ return DateUtils.formatDateTime(context, when, flags);
+ }
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/ThumbnailCache.java b/packages/DocumentsUI/src/com/android/documentsui/ThumbnailCache.java
new file mode 100644
index 0000000..bc7abeb
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/ThumbnailCache.java
@@ -0,0 +1,46 @@
+/*
+ * 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.ActivityManager;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.util.LruCache;
+
+public class ThumbnailCache extends LruCache<Uri, Bitmap> {
+ private static ThumbnailCache sCache;
+
+ public static ThumbnailCache get(Context context) {
+ if (sCache == null) {
+ final ActivityManager am = (ActivityManager) context.getSystemService(
+ Context.ACTIVITY_SERVICE);
+ final int memoryClassBytes = am.getMemoryClass() * 1024 * 1024;
+ sCache = new ThumbnailCache(memoryClassBytes / 4);
+ }
+ return sCache;
+ }
+
+ public ThumbnailCache(int maxSizeBytes) {
+ super(maxSizeBytes);
+ }
+
+ @Override
+ protected int sizeOf(Uri key, Bitmap value) {
+ return value.getByteCount();
+ }
+}