summaryrefslogtreecommitdiffstats
path: root/packages/DocumentsUI/src/com/android
diff options
context:
space:
mode:
authorJeff Sharkey <jsharkey@android.com>2013-08-31 21:27:44 -0700
committerJeff Sharkey <jsharkey@android.com>2013-08-31 21:33:56 -0700
commit5dfb345df7cb17b3a7e534a80a270b4afe7934da (patch)
treeca3998076409d454e72c01f250961e72a8304839 /packages/DocumentsUI/src/com/android
parentae9b51bfa313c51a31af30875a71255d7b6d2e61 (diff)
downloadframeworks_base-5dfb345df7cb17b3a7e534a80a270b4afe7934da.zip
frameworks_base-5dfb345df7cb17b3a7e534a80a270b4afe7934da.tar.gz
frameworks_base-5dfb345df7cb17b3a7e534a80a270b4afe7934da.tar.bz2
Use Cursors directly when binding documents.
Instead of creating a DocumentInfo for every list item, bind the adapter against Cursor directly. Create new SortingCursorWrapper which performs sorting at query time and keeps a O(1) mapping from sorted to unsorted positions in the underlying Cursor. Suppress extra loader passes that had been kicked off. Use unstable provider when querying to guard against broken providers. Bug: 10567506, 10510851 Change-Id: I535814da6b17c38de04a1175e0afcc78c6b966ce
Diffstat (limited to 'packages/DocumentsUI/src/com/android')
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java154
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java167
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java1
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/SortingCursorWrapper.java256
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java108
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java15
6 files changed, 511 insertions, 190 deletions
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
index 79d20a4..79f846a 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
@@ -20,9 +20,9 @@ import static com.android.documentsui.DocumentsActivity.TAG;
import static com.android.documentsui.DocumentsActivity.DisplayState.ACTION_MANAGE;
import static com.android.documentsui.DocumentsActivity.DisplayState.MODE_GRID;
import static com.android.documentsui.DocumentsActivity.DisplayState.MODE_LIST;
-import static com.android.documentsui.DocumentsActivity.DisplayState.SORT_ORDER_DISPLAY_NAME;
-import static com.android.documentsui.DocumentsActivity.DisplayState.SORT_ORDER_LAST_MODIFIED;
-import static com.android.documentsui.DocumentsActivity.DisplayState.SORT_ORDER_SIZE;
+import static com.android.documentsui.model.DocumentInfo.getCursorInt;
+import static com.android.documentsui.model.DocumentInfo.getCursorLong;
+import static com.android.documentsui.model.DocumentInfo.getCursorString;
import android.app.Fragment;
import android.app.FragmentManager;
@@ -32,12 +32,14 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.Loader;
+import android.database.Cursor;
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.provider.DocumentsContract.Document;
import android.text.format.DateUtils;
import android.text.format.Formatter;
import android.text.format.Time;
@@ -66,7 +68,6 @@ import com.android.internal.util.Predicate;
import com.google.android.collect.Lists;
import java.util.ArrayList;
-import java.util.Comparator;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
@@ -85,7 +86,6 @@ public class DirectoryFragment extends Fragment {
public static final int TYPE_NORMAL = 1;
public static final int TYPE_SEARCH = 2;
- public static final int TYPE_RECENT_OPEN = 3;
private int mType = TYPE_NORMAL;
@@ -99,6 +99,8 @@ public class DirectoryFragment extends Fragment {
private static AtomicInteger sLoaderId = new AtomicInteger(4000);
+ private int mLastSortOrder = -1;
+
private final int mLoaderId = sLoaderId.incrementAndGet();
public static void showNormal(FragmentManager fm, Uri uri) {
@@ -111,8 +113,9 @@ public class DirectoryFragment extends Fragment {
show(fm, TYPE_SEARCH, searchUri);
}
+ @Deprecated
public static void showRecentsOpen(FragmentManager fm) {
- show(fm, TYPE_RECENT_OPEN, null);
+ // TODO: new recents behavior
}
private static void show(FragmentManager fm, int type, Uri uri) {
@@ -137,7 +140,6 @@ public class DirectoryFragment extends Fragment {
public View onCreateView(
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final Context context = inflater.getContext();
-
final View view = inflater.inflate(R.layout.fragment_directory, container, false);
mEmptyView = view.findViewById(android.R.id.empty);
@@ -150,80 +152,65 @@ public class DirectoryFragment extends Fragment {
mGridView.setOnItemClickListener(mItemListener);
mGridView.setMultiChoiceModeListener(mMultiListener);
- mAdapter = new DocumentsAdapter();
+ return view;
+ }
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ final Context context = getActivity();
final Uri uri = getArguments().getParcelable(EXTRA_URI);
+
+ mAdapter = new DocumentsAdapter(uri.getAuthority());
mType = getArguments().getInt(EXTRA_TYPE);
mCallbacks = new LoaderCallbacks<DirectoryResult>() {
@Override
public Loader<DirectoryResult> onCreateLoader(int id, Bundle args) {
final DisplayState state = getDisplayState(DirectoryFragment.this);
- mFilter = new MimePredicate(state.acceptMimes);
Uri contentsUri;
if (mType == TYPE_NORMAL) {
contentsUri = DocumentsContract.buildChildDocumentsUri(
uri.getAuthority(), DocumentsContract.getDocumentId(uri));
- } else if (mType == TYPE_RECENT_OPEN) {
- contentsUri = RecentsProvider.buildRecentOpen();
} else {
contentsUri = uri;
}
- final Comparator<DocumentInfo> sortOrder;
- if (state.sortOrder == SORT_ORDER_LAST_MODIFIED || mType == TYPE_RECENT_OPEN) {
- sortOrder = new DocumentInfo.LastModifiedComparator();
- } else if (state.sortOrder == SORT_ORDER_DISPLAY_NAME) {
- sortOrder = new DocumentInfo.DisplayNameComparator();
- } else if (state.sortOrder == SORT_ORDER_SIZE) {
- sortOrder = new DocumentInfo.SizeComparator();
- } else {
- throw new IllegalArgumentException("Unknown sort order " + state.sortOrder);
- }
-
- return new DirectoryLoader(context, contentsUri, mType, null, sortOrder);
+ return new DirectoryLoader(context, contentsUri, state.sortOrder);
}
@Override
public void onLoadFinished(Loader<DirectoryResult> loader, DirectoryResult result) {
- mAdapter.swapDocuments(result.contents);
+ mAdapter.swapCursor(result.cursor);
}
@Override
public void onLoaderReset(Loader<DirectoryResult> loader) {
- mAdapter.swapDocuments(null);
+ mAdapter.swapCursor(null);
}
};
updateDisplayState();
-
- return view;
- }
-
- @Override
- public void onStart() {
- super.onStart();
- getLoaderManager().restartLoader(mLoaderId, getArguments(), mCallbacks);
- }
-
- @Override
- public void onStop() {
- super.onStop();
- getLoaderManager().destroyLoader(mLoaderId);
}
public void updateDisplayState() {
final DisplayState state = getDisplayState(this);
- // TODO: avoid kicking loader when nothing changed
- getLoaderManager().restartLoader(mLoaderId, getArguments(), mCallbacks);
+ if (mLastSortOrder != state.sortOrder) {
+ getLoaderManager().restartLoader(mLoaderId, null, mCallbacks);
+ mLastSortOrder = state.sortOrder;
+ }
+
mListView.smoothScrollToPosition(0);
mGridView.smoothScrollToPosition(0);
mListView.setVisibility(state.mode == MODE_LIST ? View.VISIBLE : View.GONE);
mGridView.setVisibility(state.mode == MODE_GRID ? View.VISIBLE : View.GONE);
+ mFilter = new MimePredicate(state.acceptMimes);
+
final int choiceMode;
if (state.allowMultiple) {
choiceMode = ListView.CHOICE_MODE_MULTIPLE_MODAL;
@@ -258,7 +245,9 @@ public class DirectoryFragment extends Fragment {
private OnItemClickListener mItemListener = new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- final DocumentInfo doc = mAdapter.getItem(position);
+ final Cursor cursor = mAdapter.getItem(position);
+ final Uri uri = getArguments().getParcelable(EXTRA_URI);
+ final DocumentInfo doc = DocumentInfo.fromDirectoryCursor(uri, cursor);
if (mFilter.apply(doc)) {
((DocumentsActivity) getActivity()).onDocumentPicked(doc);
}
@@ -295,7 +284,9 @@ public class DirectoryFragment extends Fragment {
final int size = checked.size();
for (int i = 0; i < size; i++) {
if (checked.valueAt(i)) {
- final DocumentInfo doc = mAdapter.getItem(checked.keyAt(i));
+ final Cursor cursor = mAdapter.getItem(checked.keyAt(i));
+ final Uri uri = getArguments().getParcelable(EXTRA_URI);
+ final DocumentInfo doc = DocumentInfo.fromDirectoryCursor(uri, cursor);
docs.add(doc);
}
}
@@ -328,8 +319,9 @@ public class DirectoryFragment extends Fragment {
ActionMode mode, int position, long id, boolean checked) {
if (checked) {
// Directories cannot be checked
- final DocumentInfo doc = mAdapter.getItem(position);
- if (doc.isDirectory()) {
+ final Cursor cursor = mAdapter.getItem(position);
+ final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
+ if (Document.MIME_TYPE_DIR.equals(docMimeType)) {
mCurrentView.setItemChecked(position, false);
}
}
@@ -396,15 +388,18 @@ public class DirectoryFragment extends Fragment {
}
private class DocumentsAdapter extends BaseAdapter {
- private List<DocumentInfo> mDocuments;
+ private final String mAuthority;
+
+ private Cursor mCursor;
- public DocumentsAdapter() {
+ public DocumentsAdapter(String authority) {
+ mAuthority = authority;
}
- public void swapDocuments(List<DocumentInfo> documents) {
- mDocuments = documents;
+ public void swapCursor(Cursor cursor) {
+ mCursor = cursor;
- if (mDocuments != null && mDocuments.isEmpty()) {
+ if (isEmpty()) {
mEmptyView.setVisibility(View.VISIBLE);
} else {
mEmptyView.setVisibility(View.GONE);
@@ -433,7 +428,16 @@ public class DirectoryFragment extends Fragment {
}
}
- final DocumentInfo doc = getItem(position);
+ final Cursor cursor = getItem(position);
+
+ final String docId = getCursorString(cursor, Document.COLUMN_DOCUMENT_ID);
+ final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
+ final String docDisplayName = getCursorString(cursor, Document.COLUMN_DISPLAY_NAME);
+ final long docLastModified = getCursorLong(cursor, Document.COLUMN_LAST_MODIFIED);
+ final int docIcon = getCursorInt(cursor, Document.COLUMN_ICON);
+ final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS);
+ final String docSummary = getCursorString(cursor, Document.COLUMN_SUMMARY);
+ final long docSize = getCursorLong(cursor, Document.COLUMN_SIZE);
final ImageView icon = (ImageView) convertView.findViewById(android.R.id.icon);
final TextView title = (TextView) convertView.findViewById(android.R.id.title);
@@ -448,32 +452,31 @@ public class DirectoryFragment extends Fragment {
oldTask.cancel(false);
}
- if (doc.isThumbnailSupported()) {
- final Bitmap cachedResult = thumbs.get(doc.uri);
+ if ((docFlags & Document.FLAG_SUPPORTS_THUMBNAIL) != 0) {
+ final Uri uri = DocumentsContract.buildDocumentUri(mAuthority, docId);
+ final Bitmap cachedResult = thumbs.get(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);
+ task.execute(uri);
}
+ } else if (docIcon != 0) {
+ icon.setImageDrawable(DocumentInfo.loadIcon(context, mAuthority, docIcon));
} else {
- icon.setImageDrawable(RootsCache.resolveDocumentIcon(context, doc.mimeType));
+ icon.setImageDrawable(RootsCache.resolveDocumentIcon(context, docMimeType));
}
- title.setText(doc.displayName);
+ title.setText(docDisplayName);
- if (mType == TYPE_NORMAL || mType == TYPE_SEARCH) {
- icon1.setVisibility(View.GONE);
- if (doc.summary != null) {
- summary.setText(doc.summary);
- summary.setVisibility(View.VISIBLE);
- } else {
- summary.setVisibility(View.INVISIBLE);
- }
- } else if (mType == TYPE_RECENT_OPEN) {
- // TODO: resolve storage root
+ icon1.setVisibility(View.GONE);
+ if (docSummary != null) {
+ summary.setText(docSummary);
+ summary.setVisibility(View.VISIBLE);
+ } else {
+ summary.setVisibility(View.INVISIBLE);
}
if (summaryGrid != null) {
@@ -481,18 +484,18 @@ public class DirectoryFragment extends Fragment {
(summary.getVisibility() == View.VISIBLE) ? View.VISIBLE : View.GONE);
}
- if (doc.lastModified == -1) {
+ if (docLastModified == -1) {
date.setText(null);
} else {
- date.setText(formatTime(context, doc.lastModified));
+ date.setText(formatTime(context, docLastModified));
}
if (state.showSize) {
size.setVisibility(View.VISIBLE);
- if (doc.isDirectory() || doc.size == -1) {
+ if (Document.MIME_TYPE_DIR.equals(docMimeType) || docSize == -1) {
size.setText(null);
} else {
- size.setText(Formatter.formatFileSize(context, doc.size));
+ size.setText(Formatter.formatFileSize(context, docSize));
}
} else {
size.setVisibility(View.GONE);
@@ -503,17 +506,20 @@ public class DirectoryFragment extends Fragment {
@Override
public int getCount() {
- return mDocuments != null ? mDocuments.size() : 0;
+ return mCursor != null ? mCursor.getCount() : 0;
}
@Override
- public DocumentInfo getItem(int position) {
- return mDocuments.get(position);
+ public Cursor getItem(int position) {
+ if (mCursor != null) {
+ mCursor.moveToPosition(position);
+ }
+ return mCursor;
}
@Override
public long getItemId(int position) {
- return getItem(position).uri.hashCode();
+ return position;
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
index cb92d76..b2be11b 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
@@ -16,98 +16,147 @@
package com.android.documentsui;
-import static com.android.documentsui.DirectoryFragment.TYPE_NORMAL;
-import static com.android.documentsui.DirectoryFragment.TYPE_RECENT_OPEN;
-import static com.android.documentsui.DirectoryFragment.TYPE_SEARCH;
-import static com.android.documentsui.DocumentsActivity.TAG;
-
-import android.content.ContentResolver;
+import android.content.AsyncTaskLoader;
+import android.content.ContentProviderClient;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.CancellationSignal;
-import android.util.Log;
+import android.os.OperationCanceledException;
+import android.provider.DocumentsContract.Document;
-import com.android.documentsui.model.DocumentInfo;
-import com.android.internal.util.Predicate;
-import com.google.android.collect.Lists;
+import com.android.documentsui.DocumentsActivity.DisplayState;
import libcore.io.IoUtils;
-import java.io.FileNotFoundException;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-
class DirectoryResult implements AutoCloseable {
+ ContentProviderClient client;
Cursor cursor;
- List<DocumentInfo> contents = Lists.newArrayList();
- Exception e;
+ Exception exception;
@Override
- public void close() throws Exception {
+ public void close() {
IoUtils.closeQuietly(cursor);
+ ContentProviderClient.closeQuietly(client);
+ cursor = null;
+ client = null;
}
}
-public class DirectoryLoader extends UriDerivativeLoader<Uri, DirectoryResult> {
+public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> {
+ private final ForceLoadContentObserver mObserver = new ForceLoadContentObserver();
+
+ private final Uri mUri;
+ private final int mSortOrder;
- private final int mType;
- private Predicate<DocumentInfo> mFilter;
- private Comparator<DocumentInfo> mSortOrder;
+ private CancellationSignal mSignal;
+ private DirectoryResult mResult;
- public DirectoryLoader(Context context, Uri uri, int type, Predicate<DocumentInfo> filter,
- Comparator<DocumentInfo> sortOrder) {
- super(context, uri);
- mType = type;
- mFilter = filter;
+ public DirectoryLoader(Context context, Uri uri, int sortOrder) {
+ super(context);
+ mUri = uri;
mSortOrder = sortOrder;
}
@Override
- public DirectoryResult loadInBackground(Uri uri, CancellationSignal signal) {
+ public final DirectoryResult loadInBackground() {
+ synchronized (this) {
+ if (isLoadInBackgroundCanceled()) {
+ throw new OperationCanceledException();
+ }
+ mSignal = new CancellationSignal();
+ }
final DirectoryResult result = new DirectoryResult();
try {
- loadInBackgroundInternal(result, uri, signal);
+ result.client = getContext()
+ .getContentResolver().acquireUnstableContentProviderClient(mUri.getAuthority());
+ final Cursor cursor = result.client.query(
+ mUri, null, null, null, getQuerySortOrder(), mSignal);
+ result.cursor = new SortingCursorWrapper(cursor, mSortOrder);
+ result.cursor.registerContentObserver(mObserver);
} catch (Exception e) {
- result.e = e;
+ result.exception = e;
+ ContentProviderClient.closeQuietly(result.client);
+ } finally {
+ synchronized (this) {
+ mSignal = null;
+ }
}
return result;
}
- private void loadInBackgroundInternal(
- DirectoryResult result, Uri uri, CancellationSignal signal) throws RuntimeException {
- // TODO: switch to using unstable CPC
- final ContentResolver resolver = getContext().getContentResolver();
- final Cursor cursor = resolver.query(uri, null, null, null, null, signal);
- result.cursor = cursor;
- result.cursor.registerContentObserver(mObserver);
-
- while (cursor.moveToNext()) {
- DocumentInfo doc = null;
- switch (mType) {
- case TYPE_NORMAL:
- case TYPE_SEARCH:
- doc = DocumentInfo.fromDirectoryCursor(uri, cursor);
- break;
- case TYPE_RECENT_OPEN:
- try {
- doc = DocumentInfo.fromRecentOpenCursor(resolver, cursor);
- } catch (FileNotFoundException e) {
- Log.w(TAG, "Failed to find recent: " + e);
- }
- break;
- default:
- throw new IllegalArgumentException("Unknown type");
- }
+ @Override
+ public void cancelLoadInBackground() {
+ super.cancelLoadInBackground();
- if (doc != null && (mFilter == null || mFilter.apply(doc))) {
- result.contents.add(doc);
+ synchronized (this) {
+ if (mSignal != null) {
+ mSignal.cancel();
}
}
+ }
+
+ @Override
+ public void deliverResult(DirectoryResult result) {
+ if (isReset()) {
+ IoUtils.closeQuietly(result);
+ return;
+ }
+ DirectoryResult oldResult = mResult;
+ mResult = result;
+
+ if (isStarted()) {
+ super.deliverResult(result);
+ }
+
+ if (oldResult != null && oldResult != result) {
+ IoUtils.closeQuietly(oldResult);
+ }
+ }
+
+ @Override
+ protected void onStartLoading() {
+ if (mResult != null) {
+ deliverResult(mResult);
+ }
+ if (takeContentChanged() || mResult == null) {
+ forceLoad();
+ }
+ }
+
+ @Override
+ protected void onStopLoading() {
+ cancelLoad();
+ }
+
+ @Override
+ public void onCanceled(DirectoryResult result) {
+ IoUtils.closeQuietly(result);
+ }
+
+ @Override
+ protected void onReset() {
+ super.onReset();
+
+ // Ensure the loader is stopped
+ onStopLoading();
+
+ IoUtils.closeQuietly(mResult);
+ mResult = null;
+
+ getContext().getContentResolver().unregisterContentObserver(mObserver);
+ }
- if (mSortOrder != null) {
- Collections.sort(result.contents, mSortOrder);
+ private String getQuerySortOrder() {
+ switch (mSortOrder) {
+ case DisplayState.SORT_ORDER_DISPLAY_NAME:
+ return Document.COLUMN_DISPLAY_NAME + " ASC";
+ case DisplayState.SORT_ORDER_LAST_MODIFIED:
+ return Document.COLUMN_LAST_MODIFIED + " DESC";
+ case DisplayState.SORT_ORDER_SIZE:
+ return Document.COLUMN_SIZE + " DESC";
+ default:
+ return null;
}
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java b/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java
index dbcb039..0c87783 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java
@@ -61,6 +61,7 @@ public class RecentsProvider extends ContentProvider {
public static final String COL_PACKAGE_NAME = "package_name";
public static final String COL_TIMESTAMP = "timestamp";
+ @Deprecated
public static Uri buildRecentOpen() {
return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
.authority(AUTHORITY).appendPath("recent_open").build();
diff --git a/packages/DocumentsUI/src/com/android/documentsui/SortingCursorWrapper.java b/packages/DocumentsUI/src/com/android/documentsui/SortingCursorWrapper.java
new file mode 100644
index 0000000..2d73732
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/SortingCursorWrapper.java
@@ -0,0 +1,256 @@
+/*
+ * 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.database.AbstractCursor;
+import android.database.Cursor;
+import android.provider.DocumentsContract.Document;
+
+import com.android.documentsui.DocumentsActivity.DisplayState;
+
+/**
+ * Cursor wrapper that presents a sorted view of the underlying cursor. Handles
+ * common {@link Document} sorting modes, such as ordering directories first.
+ */
+public class SortingCursorWrapper extends AbstractCursor {
+ private final Cursor mCursor;
+
+ private final int[] mPosition;
+ private final String[] mValueString;
+ private final long[] mValueLong;
+
+ public SortingCursorWrapper(Cursor cursor, int sortOrder) {
+ mCursor = cursor;
+
+ final int count = cursor.getCount();
+ mPosition = new int[count];
+ switch (sortOrder) {
+ case DisplayState.SORT_ORDER_DISPLAY_NAME:
+ mValueString = new String[count];
+ mValueLong = null;
+ break;
+ case DisplayState.SORT_ORDER_LAST_MODIFIED:
+ case DisplayState.SORT_ORDER_SIZE:
+ mValueString = null;
+ mValueLong = new long[count];
+ break;
+ default:
+ throw new IllegalArgumentException();
+ }
+
+ final int mimeTypeIndex = cursor.getColumnIndex(Document.COLUMN_MIME_TYPE);
+ final int displayNameIndex = cursor.getColumnIndex(Document.COLUMN_DISPLAY_NAME);
+ final int lastModifiedIndex = cursor.getColumnIndex(Document.COLUMN_LAST_MODIFIED);
+ final int sizeIndex = cursor.getColumnIndex(Document.COLUMN_SIZE);
+
+ cursor.moveToPosition(-1);
+ for (int i = 0; i < count; i++) {
+ cursor.moveToNext();
+ mPosition[i] = i;
+
+ switch (sortOrder) {
+ case DisplayState.SORT_ORDER_DISPLAY_NAME:
+ final String mimeType = cursor.getString(mimeTypeIndex);
+ final String displayName = cursor.getString(displayNameIndex);
+ if (Document.MIME_TYPE_DIR.equals(mimeType)) {
+ mValueString[i] = '\001' + displayName;
+ } else {
+ mValueString[i] = displayName;
+ }
+ break;
+ case DisplayState.SORT_ORDER_LAST_MODIFIED:
+ mValueLong[i] = cursor.getLong(lastModifiedIndex);
+ break;
+ case DisplayState.SORT_ORDER_SIZE:
+ mValueLong[i] = cursor.getLong(sizeIndex);
+ break;
+ }
+ }
+
+ switch (sortOrder) {
+ case DisplayState.SORT_ORDER_DISPLAY_NAME:
+ synchronized (SortingCursorWrapper.class) {
+
+ binarySort(mPosition, mValueString);
+ }
+ break;
+ case DisplayState.SORT_ORDER_LAST_MODIFIED:
+ case DisplayState.SORT_ORDER_SIZE:
+ binarySort(mPosition, mValueLong);
+ break;
+ }
+ }
+
+ @Override
+ public void close() {
+ super.close();
+ mCursor.close();
+ }
+
+ @Override
+ public boolean onMove(int oldPosition, int newPosition) {
+ return mCursor.moveToPosition(mPosition[newPosition]);
+ }
+
+ @Override
+ public String[] getColumnNames() {
+ return mCursor.getColumnNames();
+ }
+
+ @Override
+ public int getCount() {
+ return mCursor.getCount();
+ }
+
+ @Override
+ public double getDouble(int column) {
+ return mCursor.getDouble(column);
+ }
+
+ @Override
+ public float getFloat(int column) {
+ return mCursor.getFloat(column);
+ }
+
+ @Override
+ public int getInt(int column) {
+ return mCursor.getInt(column);
+ }
+
+ @Override
+ public long getLong(int column) {
+ return mCursor.getLong(column);
+ }
+
+ @Override
+ public short getShort(int column) {
+ return mCursor.getShort(column);
+ }
+
+ @Override
+ public String getString(int column) {
+ return mCursor.getString(column);
+ }
+
+ @Override
+ public int getType(int column) {
+ return mCursor.getType(column);
+ }
+
+ @Override
+ public boolean isNull(int column) {
+ return mCursor.isNull(column);
+ }
+
+ /**
+ * Borrowed from TimSort.binarySort(), but modified to sort two column
+ * dataset.
+ */
+ private static void binarySort(int[] position, String[] value) {
+ final int count = position.length;
+ for (int start = 1; start < count; start++) {
+ final int pivotPosition = position[start];
+ final String pivotValue = value[start];
+
+ int left = 0;
+ int right = start;
+
+ while (left < right) {
+ int mid = (left + right) >>> 1;
+
+ final String lhs = pivotValue;
+ final String rhs = value[mid];
+ final int compare;
+ if (lhs == null) {
+ compare = -1;
+ } else if (rhs == null) {
+ compare = 1;
+ } else {
+ compare = lhs.compareToIgnoreCase(rhs);
+ }
+
+ if (compare < 0) {
+ right = mid;
+ } else {
+ left = mid + 1;
+ }
+ }
+
+ int n = start - left;
+ switch (n) {
+ case 2:
+ position[left + 2] = position[left + 1];
+ value[left + 2] = value[left + 1];
+ case 1:
+ position[left + 1] = position[left];
+ value[left + 1] = value[left];
+ break;
+ default:
+ System.arraycopy(position, left, position, left + 1, n);
+ System.arraycopy(value, left, value, left + 1, n);
+ }
+
+ position[left] = pivotPosition;
+ value[left] = pivotValue;
+ }
+ }
+
+ /**
+ * Borrowed from TimSort.binarySort(), but modified to sort two column
+ * dataset.
+ */
+ private static void binarySort(int[] position, long[] value) {
+ final int count = position.length;
+ for (int start = 1; start < count; start++) {
+ final int pivotPosition = position[start];
+ final long pivotValue = value[start];
+
+ int left = 0;
+ int right = start;
+
+ while (left < right) {
+ int mid = (left + right) >>> 1;
+
+ final long lhs = pivotValue;
+ final long rhs = value[mid];
+ final int compare = Long.compare(lhs, rhs);
+ if (compare > 0) {
+ right = mid;
+ } else {
+ left = mid + 1;
+ }
+ }
+
+ int n = start - left;
+ switch (n) {
+ case 2:
+ position[left + 2] = position[left + 1];
+ value[left + 2] = value[left + 1];
+ case 1:
+ position[left + 1] = position[left];
+ value[left + 1] = value[left];
+ break;
+ default:
+ System.arraycopy(position, left, position, left + 1, n);
+ System.arraycopy(value, left, value, left + 1, n);
+ }
+
+ position[left] = pivotPosition;
+ value[left] = pivotValue;
+ }
+ }
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
index f6e46a8..d571971 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
@@ -17,7 +17,11 @@
package com.android.documentsui.model;
import android.content.ContentResolver;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.ProviderInfo;
import android.database.Cursor;
+import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
@@ -33,38 +37,28 @@ import java.util.Comparator;
* Representation of a {@link Document}.
*/
public class DocumentInfo {
- public final Uri uri;
- public final String mimeType;
- public final String displayName;
- public final long lastModified;
- public final int flags;
- public final String summary;
- public final long size;
-
- private DocumentInfo(Uri uri, String mimeType, String displayName, long lastModified, int flags,
- String summary, long size) {
- this.uri = uri;
- this.mimeType = mimeType;
- this.displayName = displayName;
- this.lastModified = lastModified;
- this.flags = flags;
- this.summary = summary;
- this.size = size;
- }
+ public Uri uri;
+ public String mimeType;
+ public String displayName;
+ public long lastModified;
+ public int flags;
+ public String summary;
+ public long size;
+ public int icon;
public static DocumentInfo fromDirectoryCursor(Uri parent, Cursor cursor) {
+ final DocumentInfo doc = new DocumentInfo();
final String authority = parent.getAuthority();
final String docId = getCursorString(cursor, Document.COLUMN_DOCUMENT_ID);
-
- final Uri uri = DocumentsContract.buildDocumentUri(authority, docId);
- final String mimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
- final String displayName = getCursorString(cursor, Document.COLUMN_DISPLAY_NAME);
- final long lastModified = getCursorLong(cursor, Document.COLUMN_LAST_MODIFIED);
- final int flags = getCursorInt(cursor, Document.COLUMN_FLAGS);
- final String summary = getCursorString(cursor, Document.COLUMN_SUMMARY);
- final long size = getCursorLong(cursor, Document.COLUMN_SIZE);
-
- return new DocumentInfo(uri, mimeType, displayName, lastModified, flags, summary, size);
+ doc.uri = DocumentsContract.buildDocumentUri(authority, docId);
+ doc.mimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
+ doc.displayName = getCursorString(cursor, Document.COLUMN_DISPLAY_NAME);
+ doc.lastModified = getCursorLong(cursor, Document.COLUMN_LAST_MODIFIED);
+ doc.flags = getCursorInt(cursor, Document.COLUMN_FLAGS);
+ doc.summary = getCursorString(cursor, Document.COLUMN_SUMMARY);
+ doc.size = getCursorLong(cursor, Document.COLUMN_SIZE);
+ doc.icon = getCursorInt(cursor, Document.COLUMN_ICON);
+ return doc;
}
@Deprecated
@@ -79,14 +73,18 @@ public class DocumentInfo {
if (!cursor.moveToFirst()) {
throw new FileNotFoundException("Missing details for " + uri);
}
- final String mimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
- final String displayName = getCursorString(cursor, Document.COLUMN_DISPLAY_NAME);
- final int flags = getCursorInt(cursor, Document.COLUMN_FLAGS)
- & Document.FLAG_SUPPORTS_THUMBNAIL;
- final String summary = getCursorString(cursor, Document.COLUMN_SUMMARY);
- final long size = getCursorLong(cursor, Document.COLUMN_SIZE);
- return new DocumentInfo(uri, mimeType, displayName, lastModified, flags, summary, size);
+ final DocumentInfo doc = new DocumentInfo();
+ doc.uri = uri;
+ doc.mimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
+ doc.displayName = getCursorString(cursor, Document.COLUMN_DISPLAY_NAME);
+ doc.lastModified = lastModified;
+ doc.flags = getCursorInt(cursor, Document.COLUMN_FLAGS)
+ & Document.FLAG_SUPPORTS_THUMBNAIL;
+ doc.summary = getCursorString(cursor, Document.COLUMN_SUMMARY);
+ doc.size = getCursorLong(cursor, Document.COLUMN_SIZE);
+ doc.icon = getCursorInt(cursor, Document.COLUMN_ICON);
+ return doc;
} catch (Throwable t) {
throw asFileNotFoundException(t);
} finally {
@@ -101,14 +99,16 @@ public class DocumentInfo {
if (!cursor.moveToFirst()) {
throw new FileNotFoundException("Missing details for " + uri);
}
- final String mimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
- final String displayName = getCursorString(cursor, Document.COLUMN_DISPLAY_NAME);
- final long lastModified = getCursorLong(cursor, Document.COLUMN_LAST_MODIFIED);
- final int flags = getCursorInt(cursor, Document.COLUMN_FLAGS);
- final String summary = getCursorString(cursor, Document.COLUMN_SUMMARY);
- final long size = getCursorLong(cursor, Document.COLUMN_SIZE);
-
- return new DocumentInfo(uri, mimeType, displayName, lastModified, flags, summary, size);
+ final DocumentInfo doc = new DocumentInfo();
+ doc.uri = uri;
+ doc.mimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
+ doc.displayName = getCursorString(cursor, Document.COLUMN_DISPLAY_NAME);
+ doc.lastModified = getCursorLong(cursor, Document.COLUMN_LAST_MODIFIED);
+ doc.flags = getCursorInt(cursor, Document.COLUMN_FLAGS);
+ doc.summary = getCursorString(cursor, Document.COLUMN_SUMMARY);
+ doc.size = getCursorLong(cursor, Document.COLUMN_SIZE);
+ doc.icon = getCursorInt(cursor, Document.COLUMN_ICON);
+ return doc;
} catch (Throwable t) {
throw asFileNotFoundException(t);
} finally {
@@ -145,6 +145,25 @@ public class DocumentInfo {
return (flags & Document.FLAG_SUPPORTS_DELETE) != 0;
}
+ public Drawable loadIcon(Context context) {
+ return loadIcon(context, uri.getAuthority(), icon);
+ }
+
+ public static Drawable loadIcon(Context context, String authority, int icon) {
+ if (icon != 0) {
+ if (authority != null) {
+ final PackageManager pm = context.getPackageManager();
+ final ProviderInfo info = pm.resolveContentProvider(authority, 0);
+ if (info != null) {
+ return pm.getDrawable(info.packageName, icon, info.applicationInfo);
+ }
+ } else {
+ return context.getResources().getDrawable(icon);
+ }
+ }
+ return null;
+ }
+
public static String getCursorString(Cursor cursor, String columnName) {
final int index = cursor.getColumnIndex(columnName);
return (index != -1) ? cursor.getString(index) : null;
@@ -170,6 +189,7 @@ public class DocumentInfo {
return (index != -1) ? cursor.getInt(index) : 0;
}
+ @Deprecated
public static class DisplayNameComparator implements Comparator<DocumentInfo> {
@Override
public int compare(DocumentInfo lhs, DocumentInfo rhs) {
@@ -184,6 +204,7 @@ public class DocumentInfo {
}
}
+ @Deprecated
public static class LastModifiedComparator implements Comparator<DocumentInfo> {
@Override
public int compare(DocumentInfo lhs, DocumentInfo rhs) {
@@ -191,6 +212,7 @@ public class DocumentInfo {
}
}
+ @Deprecated
public static class SizeComparator implements Comparator<DocumentInfo> {
@Override
public int compare(DocumentInfo lhs, DocumentInfo rhs) {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
index f7027f3..9728838 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
@@ -21,8 +21,6 @@ import static com.android.documentsui.model.DocumentInfo.getCursorLong;
import static com.android.documentsui.model.DocumentInfo.getCursorString;
import android.content.Context;
-import android.content.pm.PackageManager;
-import android.content.pm.ProviderInfo;
import android.database.Cursor;
import android.graphics.drawable.Drawable;
import android.provider.DocumentsContract.Root;
@@ -56,17 +54,6 @@ public class RootInfo {
}
public Drawable loadIcon(Context context) {
- if (icon != 0) {
- if (authority != null) {
- final PackageManager pm = context.getPackageManager();
- final ProviderInfo info = pm.resolveContentProvider(authority, 0);
- if (info != null) {
- return pm.getDrawable(info.packageName, icon, info.applicationInfo);
- }
- } else {
- return context.getResources().getDrawable(icon);
- }
- }
- return null;
+ return DocumentInfo.loadIcon(context, authority, icon);
}
}