diff options
author | Jeff Sharkey <jsharkey@android.com> | 2013-05-07 12:41:33 -0700 |
---|---|---|
committer | Jeff Sharkey <jsharkey@android.com> | 2013-08-17 19:05:07 -0700 |
commit | 9d0843df7e3984293dc4ab6ee2f9502e898b63aa (patch) | |
tree | 9487940fefdc3034d1e13838b0cca47ed5bce35b /packages/DocumentsUI/src | |
parent | a5599ef636e37cb0b6474349936999be1afe6987 (diff) | |
download | frameworks_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.java | 101 | ||||
-rw-r--r-- | packages/DocumentsUI/src/com/android/documentsui/ThumbnailCache.java | 46 |
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(); + } +} |