diff options
author | Jeff Sharkey <jsharkey@android.com> | 2013-09-09 16:51:06 -0700 |
---|---|---|
committer | Jeff Sharkey <jsharkey@android.com> | 2013-09-09 16:55:35 -0700 |
commit | 3f4c205fd3110345241e690f2a2e7c1b477eac76 (patch) | |
tree | 48803386429a4bb8088dc97380df097d497e6268 /packages/DocumentsUI | |
parent | d182bb641f228b2d28527a6aa86075f6358ab838 (diff) | |
download | frameworks_base-3f4c205fd3110345241e690f2a2e7c1b477eac76.zip frameworks_base-3f4c205fd3110345241e690f2a2e7c1b477eac76.tar.gz frameworks_base-3f4c205fd3110345241e690f2a2e7c1b477eac76.tar.bz2 |
Disabled states, more UX work, bug fixes.
Fix drawable state to correctly show dimmed disabled state. Update
disabled state for all children to grey out text.
Block multi-selection of documents not matching MIME filter. Load
thumbnails in parallel. Show thumbnails in list mode based on MIME
type to match spec.
Give each footer a unique view type to avoid recycler crashes.
Show breadcrumb icons in recent create paths. Fix timestamp bug when
querying/updating recent paths.
Make ContentProviderClient.closeQuietly() really be quiet.
Bug: 10668364, 10510022, 10668701, 10534224, 10667726
Change-Id: I3c705412fb211519f15ad41a273a7533b878e9e5
Diffstat (limited to 'packages/DocumentsUI')
5 files changed, 132 insertions, 45 deletions
diff --git a/packages/DocumentsUI/res/drawable/item_background.xml b/packages/DocumentsUI/res/drawable/item_background.xml index 4fb32fc..7447aa8 100644 --- a/packages/DocumentsUI/res/drawable/item_background.xml +++ b/packages/DocumentsUI/res/drawable/item_background.xml @@ -15,12 +15,20 @@ --> <selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:state_activated="true" android:state_pressed="true" android:drawable="@*android:drawable/list_activated_holo" /> - <item android:state_activated="true" android:drawable="@*android:drawable/list_activated_holo" /> - <item android:state_focused="true" android:state_enabled="false" android:state_pressed="true" android:drawable="@*android:drawable/list_selector_disabled_holo_light" /> - <item android:state_focused="true" android:state_enabled="false" android:drawable="@*android:drawable/list_selector_disabled_holo_light" /> - <item android:state_focused="true" android:state_pressed="true" android:drawable="@*android:drawable/list_selector_background_transition_holo_light" /> - <item android:state_focused="false" android:state_pressed="true" android:drawable="@*android:drawable/list_selector_background_transition_holo_light" /> - <item android:state_focused="true" android:drawable="@*android:drawable/list_focused_holo" /> - <item android:drawable="@android:color/transparent" /> + + <item android:state_enabled="false" android:state_selected="true" android:drawable="@*android:drawable/list_selector_disabled_holo_light" /> + <item android:state_enabled="false" android:state_focused="true" android:drawable="@*android:drawable/list_selector_disabled_holo_light" /> + <item android:state_enabled="false" android:state_pressed="true" android:drawable="@*android:drawable/list_selector_disabled_holo_light" /> + + <item android:state_activated="true" android:state_pressed="true" android:drawable="@*android:drawable/list_activated_holo" /> + <item android:state_activated="true" android:drawable="@*android:drawable/list_activated_holo" /> + + <item android:state_focused="true" android:drawable="@*android:drawable/list_focused_holo" /> + <item android:state_selected="true" android:drawable="@*android:drawable/list_focused_holo" /> + + <item android:state_pressed="true" android:state_focused="true" android:drawable="@*android:drawable/list_selector_background_transition_holo_light" /> + <item android:state_pressed="true" android:drawable="@*android:drawable/list_selector_background_transition_holo_light" /> + + <item android:drawable="@android:color/transparent" /> + </selector> diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java index a13beba..f9ac3f3 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java +++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java @@ -106,6 +106,11 @@ public class DirectoryFragment extends Fragment { private static final String EXTRA_DOC = "doc"; private static final String EXTRA_QUERY = "query"; + /** + * MIME types that should always show thumbnails in list mode. + */ + private static final String[] LIST_THUMBNAIL_MIMES = new String[] { "image/*", "video/*" }; + private static AtomicInteger sLoaderId = new AtomicInteger(4000); private final int mLoaderId = sLoaderId.incrementAndGet(); @@ -294,9 +299,11 @@ public class DirectoryFragment extends Fragment { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { final Cursor cursor = mAdapter.getItem(position); - final DocumentInfo doc = DocumentInfo.fromDirectoryCursor(cursor); - if (mFilter.apply(doc)) { - ((DocumentsActivity) getActivity()).onDocumentPicked(doc); + if (cursor != null) { + final DocumentInfo doc = DocumentInfo.fromDirectoryCursor(cursor); + if (mFilter.apply(doc)) { + ((DocumentsActivity) getActivity()).onDocumentPicked(doc); + } } } }; @@ -367,10 +374,20 @@ public class DirectoryFragment extends Fragment { public void onItemCheckedStateChanged( ActionMode mode, int position, long id, boolean checked) { if (checked) { - // Directories cannot be checked + // Directories and footer items cannot be checked + boolean valid = false; + final Cursor cursor = mAdapter.getItem(position); - final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE); - if (Document.MIME_TYPE_DIR.equals(docMimeType)) { + if (cursor != null) { + final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE); + + // Only valid if non-directory matches filter + final State state = getDisplayState(DirectoryFragment.this); + valid = !Document.MIME_TYPE_DIR.equals(docMimeType) + && MimePredicate.mimeMatches(state.acceptMimes, docMimeType); + } + + if (!valid) { mCurrentView.setItemChecked(position, false); } } @@ -441,11 +458,25 @@ public class DirectoryFragment extends Fragment { return ((DocumentsActivity) fragment.getActivity()).getDisplayState(); } - private interface Footer { - public View getView(View convertView, ViewGroup parent); + private static abstract class Footer { + private final int mItemViewType; + + public Footer(int itemViewType) { + mItemViewType = itemViewType; + } + + public abstract View getView(View convertView, ViewGroup parent); + + public int getItemViewType() { + return mItemViewType; + } } - private static class LoadingFooter implements Footer { + private static class LoadingFooter extends Footer { + public LoadingFooter() { + super(1); + } + @Override public View getView(View convertView, ViewGroup parent) { final Context context = parent.getContext(); @@ -457,11 +488,12 @@ public class DirectoryFragment extends Fragment { } } - private class MessageFooter implements Footer { + private class MessageFooter extends Footer { private final int mIcon; private final String mMessage; - public MessageFooter(int icon, String message) { + public MessageFooter(int itemViewType, int icon, String message) { + super(itemViewType); mIcon = icon; mMessage = message; } @@ -506,11 +538,11 @@ public class DirectoryFragment extends Fragment { if (extras != null) { final String info = extras.getString(DocumentsContract.EXTRA_INFO); if (info != null) { - mFooters.add(new MessageFooter(R.drawable.ic_dialog_alert, info)); + mFooters.add(new MessageFooter(2, R.drawable.ic_dialog_alert, info)); } final String error = extras.getString(DocumentsContract.EXTRA_ERROR); if (error != null) { - mFooters.add(new MessageFooter(R.drawable.ic_dialog_alert, error)); + mFooters.add(new MessageFooter(3, R.drawable.ic_dialog_alert, error)); } if (extras.getBoolean(DocumentsContract.EXTRA_LOADING, false)) { mFooters.add(new LoadingFooter()); @@ -532,7 +564,11 @@ public class DirectoryFragment extends Fragment { return getDocumentView(position, convertView, parent); } else { position -= mCursorCount; - return mFooters.get(position).getView(convertView, parent); + convertView = mFooters.get(position).getView(convertView, parent); + // Only the view itself is disabled; contents inside shouldn't + // be dimmed. + convertView.setEnabled(false); + return convertView; } } @@ -581,7 +617,11 @@ public class DirectoryFragment extends Fragment { oldTask.cancel(false); } - if ((docFlags & Document.FLAG_SUPPORTS_THUMBNAIL) != 0) { + final boolean supportsThumbnail = (docFlags & Document.FLAG_SUPPORTS_THUMBNAIL) != 0; + final boolean allowThumbnail = (state.mode == MODE_GRID) + || MimePredicate.mimeMatches(LIST_THUMBNAIL_MIMES, docMimeType); + + if (supportsThumbnail && allowThumbnail) { final Uri uri = DocumentsContract.buildDocumentUri(docAuthority, docId); final Bitmap cachedResult = thumbs.get(uri); if (cachedResult != null) { @@ -590,7 +630,7 @@ public class DirectoryFragment extends Fragment { final ThumbnailAsyncTask task = new ThumbnailAsyncTask(icon, mThumbSize); icon.setImageBitmap(null); icon.setTag(task); - task.execute(uri); + task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, uri); } } else if (docIcon != 0) { icon.setImageDrawable(IconUtils.loadPackageIcon(context, docAuthority, docIcon)); @@ -642,6 +682,18 @@ public class DirectoryFragment extends Fragment { line2.setVisibility(hasLine2 ? View.VISIBLE : View.GONE); + final boolean enabled = Document.MIME_TYPE_DIR.equals(docMimeType) + || MimePredicate.mimeMatches(state.acceptMimes, docMimeType); + if (enabled) { + setEnabledRecursive(convertView, true); + icon.setAlpha(1f); + icon1.setAlpha(1f); + } else { + setEnabledRecursive(convertView, false); + icon.setAlpha(0.5f); + icon1.setAlpha(0.5f); + } + return convertView; } @@ -666,23 +718,19 @@ public class DirectoryFragment extends Fragment { } @Override + public int getViewTypeCount() { + return 4; + } + + @Override public int getItemViewType(int position) { if (position < mCursorCount) { return 0; } else { - return IGNORE_ITEM_VIEW_TYPE; + position -= mCursorCount; + return mFooters.get(position).getItemViewType(); } } - - @Override - public boolean areAllItemsEnabled() { - return false; - } - - @Override - public boolean isEnabled(int position) { - return position < mCursorCount; - } } private static class ThumbnailAsyncTask extends AsyncTask<Uri, Void, Bitmap> { @@ -772,4 +820,16 @@ public class DirectoryFragment extends Fragment { return commonType[0] + "/" + commonType[1]; } + + private void setEnabledRecursive(View v, boolean enabled) { + if (v.isEnabled() == enabled) return; + v.setEnabled(enabled); + + if (v instanceof ViewGroup) { + final ViewGroup vg = (ViewGroup) v; + for (int i = vg.getChildCount() - 1; i >= 0; i--) { + setEnabledRecursive(vg.getChildAt(i), enabled); + } + } + } } diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java index 9391ca9..3642478 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java +++ b/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java @@ -26,10 +26,14 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Loader; import android.database.Cursor; +import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; import android.os.CancellationSignal; +import android.text.Spannable; +import android.text.SpannableStringBuilder; import android.text.TextUtils.TruncateAt; +import android.text.style.ImageSpan; import android.util.Log; import android.view.LayoutInflater; import android.view.View; @@ -181,20 +185,29 @@ public class RecentsCreateFragment extends Fragment { final ImageView icon = (ImageView) convertView.findViewById(android.R.id.icon); final TextView title = (TextView) convertView.findViewById(android.R.id.title); + final View line2 = convertView.findViewById(R.id.line2); final DocumentStack stack = getItem(position); icon.setImageDrawable(stack.root.loadIcon(context)); - final StringBuilder builder = new StringBuilder(); - for (int i = stack.size() - 1; i >= 0; i--) { + final Drawable crumb = context.getResources() + .getDrawable(R.drawable.ic_breadcrumb_arrow); + crumb.setBounds(0, 0, crumb.getIntrinsicWidth(), crumb.getIntrinsicHeight()); + + final SpannableStringBuilder builder = new SpannableStringBuilder(); + builder.append(stack.root.title); + appendDrawable(builder, crumb); + for (int i = stack.size() - 2; i >= 0; i--) { builder.append(stack.get(i).displayName); if (i > 0) { - builder.append(" \u232a "); + appendDrawable(builder, crumb); } } - title.setText(builder.toString()); + title.setText(builder); title.setEllipsize(TruncateAt.MIDDLE); + line2.setVisibility(View.GONE); + return convertView; } @@ -213,4 +226,10 @@ public class RecentsCreateFragment extends Fragment { return getItem(position).hashCode(); } } + + private static void appendDrawable(SpannableStringBuilder b, Drawable d) { + final int length = b.length(); + b.append("\u232a"); + b.setSpan(new ImageSpan(d), length, b.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } } diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java b/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java index df7ed4a..1fe5d54 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java +++ b/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java @@ -149,9 +149,9 @@ public class RecentsProvider extends ContentProvider { final SQLiteDatabase db = mHelper.getReadableDatabase(); switch (sMatcher.match(uri)) { case URI_RECENT: - return db.query(TABLE_RECENT, projection, - RecentColumns.TIMESTAMP + "<" + MAX_HISTORY_IN_MILLIS, null, null, null, - null); + final long cutoff = System.currentTimeMillis() - MAX_HISTORY_IN_MILLIS; + return db.query(TABLE_RECENT, projection, RecentColumns.TIMESTAMP + ">" + cutoff, + null, null, null, null); case URI_STATE: final String authority = uri.getPathSegments().get(1); final String rootId = uri.getPathSegments().get(2); @@ -180,8 +180,8 @@ public class RecentsProvider extends ContentProvider { case URI_RECENT: values.put(RecentColumns.TIMESTAMP, System.currentTimeMillis()); db.insert(TABLE_RECENT, null, values); - db.delete( - TABLE_RECENT, RecentColumns.TIMESTAMP + ">" + MAX_HISTORY_IN_MILLIS, null); + final long cutoff = System.currentTimeMillis() - MAX_HISTORY_IN_MILLIS; + db.delete(TABLE_RECENT, RecentColumns.TIMESTAMP + "<" + cutoff, null); return uri; case URI_STATE: final String authority = uri.getPathSegments().get(1); diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/RootsCacheTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/RootsCacheTest.java index f53e60d..cdb6b33 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/RootsCacheTest.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/RootsCacheTest.java @@ -30,7 +30,7 @@ public class RootsCacheTest extends AndroidTestCase { private static RootInfo buildForMimeTypes(String... mimeTypes) { final RootInfo root = new RootInfo(); - root.mimeTypes = mimeTypes; + root.derivedMimeTypes = mimeTypes; return root; } |