diff options
author | Jeff Sharkey <jsharkey@android.com> | 2013-08-31 21:27:44 -0700 |
---|---|---|
committer | Jeff Sharkey <jsharkey@android.com> | 2013-08-31 21:33:56 -0700 |
commit | 5dfb345df7cb17b3a7e534a80a270b4afe7934da (patch) | |
tree | ca3998076409d454e72c01f250961e72a8304839 /packages/DocumentsUI/src/com/android/documentsui | |
parent | ae9b51bfa313c51a31af30875a71255d7b6d2e61 (diff) | |
download | frameworks_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/documentsui')
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); } } |