summaryrefslogtreecommitdiffstats
path: root/packages/DocumentsUI
diff options
context:
space:
mode:
authorJeff Sharkey <jsharkey@android.com>2013-09-09 16:51:06 -0700
committerJeff Sharkey <jsharkey@android.com>2013-09-09 16:55:35 -0700
commit3f4c205fd3110345241e690f2a2e7c1b477eac76 (patch)
tree48803386429a4bb8088dc97380df097d497e6268 /packages/DocumentsUI
parentd182bb641f228b2d28527a6aa86075f6358ab838 (diff)
downloadframeworks_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')
-rw-r--r--packages/DocumentsUI/res/drawable/item_background.xml24
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java114
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java27
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java10
-rw-r--r--packages/DocumentsUI/tests/src/com/android/documentsui/RootsCacheTest.java2
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;
}