summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/current.txt1
-rw-r--r--core/java/android/database/MatrixCursor.java47
-rw-r--r--core/java/android/provider/DocumentsContract.java22
-rw-r--r--core/java/android/provider/MediaStore.java6
-rw-r--r--core/tests/coretests/src/android/database/MatrixCursorTest.java50
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java101
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/ThumbnailCache.java46
-rw-r--r--packages/ExternalStorageProvider/AndroidManifest.xml2
-rw-r--r--packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java100
9 files changed, 306 insertions, 69 deletions
diff --git a/api/current.txt b/api/current.txt
index 5a3c7db..2a81469 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -8051,6 +8051,7 @@ package android.database {
public class MatrixCursor.RowBuilder {
method public android.database.MatrixCursor.RowBuilder add(java.lang.Object);
+ method public android.database.MatrixCursor.RowBuilder offer(java.lang.String, java.lang.Object);
}
public class MergeCursor extends android.database.AbstractCursor {
diff --git a/core/java/android/database/MatrixCursor.java b/core/java/android/database/MatrixCursor.java
index 6e68b6b..2a0d9b9 100644
--- a/core/java/android/database/MatrixCursor.java
+++ b/core/java/android/database/MatrixCursor.java
@@ -83,11 +83,10 @@ public class MatrixCursor extends AbstractCursor {
* row
*/
public RowBuilder newRow() {
- rowCount++;
- int endIndex = rowCount * columnCount;
+ final int row = rowCount++;
+ final int endIndex = rowCount * columnCount;
ensureCapacity(endIndex);
- int start = endIndex - columnCount;
- return new RowBuilder(start, endIndex);
+ return new RowBuilder(row);
}
/**
@@ -180,18 +179,29 @@ public class MatrixCursor extends AbstractCursor {
}
/**
- * Builds a row, starting from the left-most column and adding one column
- * value at a time. Follows the same ordering as the column names specified
- * at cursor construction time.
+ * Builds a row of values using either of these approaches:
+ * <ul>
+ * <li>Values can be added with explicit column ordering using
+ * {@link #add(Object)}, which starts from the left-most column and adds one
+ * column value at a time. This follows the same ordering as the column
+ * names specified at cursor construction time.
+ * <li>Column and value pairs can be offered for possible inclusion using
+ * {@link #offer(String, Object)}. If the cursor includes the given column,
+ * the value will be set for that column, otherwise the value is ignored.
+ * This approach is useful when matching data to a custom projection.
+ * </ul>
+ * Undefined values are left as {@code null}.
*/
public class RowBuilder {
+ private final int row;
+ private final int endIndex;
private int index;
- private final int endIndex;
- RowBuilder(int index, int endIndex) {
- this.index = index;
- this.endIndex = endIndex;
+ RowBuilder(int row) {
+ this.row = row;
+ this.index = row * columnCount;
+ this.endIndex = index + columnCount;
}
/**
@@ -210,6 +220,21 @@ public class MatrixCursor extends AbstractCursor {
data[index++] = columnValue;
return this;
}
+
+ /**
+ * Offer value for possible inclusion if this cursor defines the given
+ * column. Columns not defined by the cursor are silently ignored.
+ *
+ * @return this builder to support chaining
+ */
+ public RowBuilder offer(String columnName, Object value) {
+ for (int i = 0; i < columnNames.length; i++) {
+ if (columnName.equals(columnNames[i])) {
+ data[(row * columnCount) + i] = value;
+ }
+ }
+ return this;
+ }
}
// AbstractCursor implementation.
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index e1810ca..91d349a 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -36,6 +36,7 @@ import com.google.android.collect.Lists;
import libcore.io.IoUtils;
+import java.io.FileDescriptor;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
@@ -461,16 +462,27 @@ public final class DocumentsContract {
final Bundle opts = new Bundle();
opts.putParcelable(EXTRA_THUMBNAIL_SIZE, size);
- InputStream is = null;
+ AssetFileDescriptor afd = null;
try {
- is = new AssetFileDescriptor.AutoCloseInputStream(
- resolver.openTypedAssetFileDescriptor(documentUri, "image/*", opts));
- return BitmapFactory.decodeStream(is);
+ afd = resolver.openTypedAssetFileDescriptor(documentUri, "image/*", opts);
+
+ final FileDescriptor fd = afd.getFileDescriptor();
+ final BitmapFactory.Options bitmapOpts = new BitmapFactory.Options();
+
+ bitmapOpts.inJustDecodeBounds = true;
+ BitmapFactory.decodeFileDescriptor(fd, null, bitmapOpts);
+
+ final int widthSample = bitmapOpts.outWidth / size.x;
+ final int heightSample = bitmapOpts.outHeight / size.y;
+
+ bitmapOpts.inJustDecodeBounds = false;
+ bitmapOpts.inSampleSize = Math.min(widthSample, heightSample);
+ return BitmapFactory.decodeFileDescriptor(fd, null, bitmapOpts);
} catch (IOException e) {
Log.w(TAG, "Failed to load thumbnail for " + documentUri + ": " + e);
return null;
} finally {
- IoUtils.closeQuietly(is);
+ IoUtils.closeQuietly(afd);
}
}
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index cb6300f..ad6839b 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -19,8 +19,8 @@ package android.provider;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.content.ContentResolver;
-import android.content.ContentValues;
import android.content.ContentUris;
+import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.DatabaseUtils;
@@ -532,7 +532,8 @@ public final class MediaStore {
private static final Object sThumbBufLock = new Object();
private static byte[] sThumbBuf;
- private static Bitmap getMiniThumbFromFile(Cursor c, Uri baseUri, ContentResolver cr, BitmapFactory.Options options) {
+ private static Bitmap getMiniThumbFromFile(
+ Cursor c, Uri baseUri, ContentResolver cr, BitmapFactory.Options options) {
Bitmap bitmap = null;
Uri thumbUri = null;
try {
@@ -577,6 +578,7 @@ public final class MediaStore {
if (c != null) c.close();
}
}
+
/**
* This method ensure thumbnails associated with origId are generated and decode the byte
* stream from database (MICRO_KIND) or file (MINI_KIND).
diff --git a/core/tests/coretests/src/android/database/MatrixCursorTest.java b/core/tests/coretests/src/android/database/MatrixCursorTest.java
index cdab638..fc48c17 100644
--- a/core/tests/coretests/src/android/database/MatrixCursorTest.java
+++ b/core/tests/coretests/src/android/database/MatrixCursorTest.java
@@ -128,6 +128,56 @@ public class MatrixCursorTest extends TestCase {
} catch (IllegalArgumentException e) { /* expected */ }
}
+ public void testRowBuilderOffer() {
+ MatrixCursor cursor = newMatrixCursor();
+
+ cursor.newRow()
+ .offer("float", 4.2f)
+ .offer("string", "foobar")
+ .offer("blob", new byte[] {(byte) 0xaa, (byte) 0x55})
+ .offer("lolwat", "kittens");
+
+ cursor.newRow();
+
+ cursor.newRow()
+ .offer("string", "zero")
+ .offer("string", "one")
+ .offer("string", "two")
+ .offer("lolwat", "kittens");
+
+ assertTrue(cursor.moveToFirst());
+ assertEquals("foobar", cursor.getString(0));
+ assertEquals(null, cursor.getString(1));
+ assertEquals(0, cursor.getShort(1));
+ assertEquals(0, cursor.getInt(2));
+ assertEquals(0, cursor.getLong(3));
+ assertEquals(4.2f, cursor.getFloat(4));
+ assertEquals(0.0d, cursor.getDouble(5));
+ MoreAsserts.assertEquals(new byte[] {(byte) 0xaa, (byte) 0x55}, cursor.getBlob(6));
+
+ assertTrue(cursor.moveToNext());
+ assertEquals(null, cursor.getString(0));
+ assertEquals(0, cursor.getShort(1));
+ assertEquals(0, cursor.getInt(2));
+ assertEquals(0, cursor.getLong(3));
+ assertEquals(0.0f, cursor.getFloat(4));
+ assertEquals(0.0d, cursor.getDouble(5));
+ assertEquals(null, cursor.getBlob(6));
+
+ assertTrue(cursor.moveToNext());
+ assertEquals("two", cursor.getString(0));
+ assertEquals(0, cursor.getShort(1));
+ assertEquals(0, cursor.getInt(2));
+ assertEquals(0, cursor.getLong(3));
+ assertEquals(0.0f, cursor.getFloat(4));
+ assertEquals(0.0d, cursor.getDouble(5));
+ assertEquals(null, cursor.getBlob(6));
+
+ assertTrue(cursor.isLast());
+ assertFalse(cursor.moveToNext());
+ assertTrue(cursor.isAfterLast());
+ }
+
static class NonIterableArrayList<T> extends ArrayList<T> {
NonIterableArrayList() {}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
index ac5629e..fbdb3a7 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
@@ -16,17 +16,24 @@
package com.android.documentsui;
+import static com.android.documentsui.DocumentsActivity.TAG;
+
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.app.LoaderManager.LoaderCallbacks;
import android.content.Context;
import android.content.Loader;
+import android.graphics.Bitmap;
+import android.graphics.Point;
import android.net.Uri;
+import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.DocumentsContract;
import android.text.format.DateUtils;
import android.text.format.Formatter;
+import android.text.format.Time;
+import android.util.Log;
import android.util.SparseBooleanArray;
import android.view.ActionMode;
import android.view.LayoutInflater;
@@ -75,6 +82,8 @@ public class DirectoryFragment extends Fragment {
private int mType = TYPE_NORMAL;
+ private Point mThumbSize;
+
private DocumentsAdapter mAdapter;
private LoaderCallbacks<List<Document>> mCallbacks;
@@ -217,7 +226,9 @@ public class DirectoryFragment extends Fragment {
choiceMode = ListView.CHOICE_MODE_NONE;
}
+ final int thumbSize;
if (state.mode == DisplayState.MODE_GRID) {
+ thumbSize = getResources().getDimensionPixelSize(R.dimen.grid_width);
mListView.setAdapter(null);
mListView.setChoiceMode(ListView.CHOICE_MODE_NONE);
mGridView.setAdapter(mAdapter);
@@ -226,6 +237,7 @@ public class DirectoryFragment extends Fragment {
mGridView.setChoiceMode(choiceMode);
mCurrentView = mGridView;
} else if (state.mode == DisplayState.MODE_LIST) {
+ thumbSize = getResources().getDimensionPixelSize(android.R.dimen.app_icon_size);
mGridView.setAdapter(null);
mGridView.setChoiceMode(ListView.CHOICE_MODE_NONE);
mListView.setAdapter(mAdapter);
@@ -234,6 +246,8 @@ public class DirectoryFragment extends Fragment {
} else {
throw new IllegalStateException();
}
+
+ mThumbSize = new Point(thumbSize, thumbSize);
}
private OnItemClickListener mItemListener = new OnItemClickListener() {
@@ -349,9 +363,21 @@ public class DirectoryFragment extends Fragment {
final TextView date = (TextView) convertView.findViewById(R.id.date);
final TextView size = (TextView) convertView.findViewById(R.id.size);
+ final ThumbnailAsyncTask oldTask = (ThumbnailAsyncTask) icon.getTag();
+ if (oldTask != null) {
+ oldTask.cancel(false);
+ }
+
if (doc.isThumbnailSupported()) {
- // TODO: load thumbnails async
- icon.setImageURI(doc.uri);
+ final Bitmap cachedResult = ThumbnailCache.get(context).get(doc.uri);
+ if (cachedResult != null) {
+ icon.setImageBitmap(cachedResult);
+ } else {
+ final ThumbnailAsyncTask task = new ThumbnailAsyncTask(icon, mThumbSize);
+ icon.setImageBitmap(null);
+ icon.setTag(task);
+ task.execute(doc.uri);
+ }
} else {
icon.setImageDrawable(RootsCache.resolveDocumentIcon(
context, doc.uri.getAuthority(), doc.mimeType));
@@ -380,10 +406,11 @@ public class DirectoryFragment extends Fragment {
(summary.getVisibility() == View.VISIBLE) ? View.VISIBLE : View.GONE);
}
- // TODO: omit year from format
- date.setText(DateUtils.formatSameDayTime(
- doc.lastModified, System.currentTimeMillis(), DateFormat.SHORT,
- DateFormat.SHORT));
+ if (doc.lastModified == -1) {
+ date.setText(null);
+ } else {
+ date.setText(formatTime(context, doc.lastModified));
+ }
if (state.showSize) {
size.setVisibility(View.VISIBLE);
@@ -414,4 +441,66 @@ public class DirectoryFragment extends Fragment {
return getItem(position).uri.hashCode();
}
}
+
+ private static class ThumbnailAsyncTask extends AsyncTask<Uri, Void, Bitmap> {
+ private final ImageView mTarget;
+ private final Point mSize;
+
+ public ThumbnailAsyncTask(ImageView target, Point size) {
+ mTarget = target;
+ mSize = size;
+ }
+
+ @Override
+ protected void onPreExecute() {
+ mTarget.setTag(this);
+ }
+
+ @Override
+ protected Bitmap doInBackground(Uri... params) {
+ final Context context = mTarget.getContext();
+ final Uri uri = params[0];
+
+ Bitmap result = null;
+ try {
+ result = DocumentsContract.getThumbnail(
+ context.getContentResolver(), uri, mSize);
+ if (result != null) {
+ ThumbnailCache.get(context).put(uri, result);
+ }
+ } catch (Exception e) {
+ Log.w(TAG, "Failed to load thumbnail: " + e);
+ }
+ return result;
+ }
+
+ @Override
+ protected void onPostExecute(Bitmap result) {
+ if (mTarget.getTag() == this) {
+ mTarget.setImageBitmap(result);
+ mTarget.setTag(null);
+ }
+ }
+ }
+
+ private static String formatTime(Context context, long when) {
+ // TODO: DateUtils should make this easier
+ Time then = new Time();
+ then.set(when);
+ Time now = new Time();
+ now.setToNow();
+
+ int flags = DateUtils.FORMAT_NO_NOON | DateUtils.FORMAT_NO_MIDNIGHT
+ | DateUtils.FORMAT_ABBREV_ALL;
+
+ if (then.year != now.year) {
+ flags |= DateUtils.FORMAT_SHOW_YEAR | DateUtils.FORMAT_SHOW_DATE;
+ } else if (then.yearDay != now.yearDay) {
+ flags |= DateUtils.FORMAT_SHOW_DATE;
+ } else {
+ flags |= DateUtils.FORMAT_SHOW_TIME;
+ }
+
+ return DateUtils.formatDateTime(context, when, flags);
+ }
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/ThumbnailCache.java b/packages/DocumentsUI/src/com/android/documentsui/ThumbnailCache.java
new file mode 100644
index 0000000..bc7abeb
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/ThumbnailCache.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.documentsui;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.util.LruCache;
+
+public class ThumbnailCache extends LruCache<Uri, Bitmap> {
+ private static ThumbnailCache sCache;
+
+ public static ThumbnailCache get(Context context) {
+ if (sCache == null) {
+ final ActivityManager am = (ActivityManager) context.getSystemService(
+ Context.ACTIVITY_SERVICE);
+ final int memoryClassBytes = am.getMemoryClass() * 1024 * 1024;
+ sCache = new ThumbnailCache(memoryClassBytes / 4);
+ }
+ return sCache;
+ }
+
+ public ThumbnailCache(int maxSizeBytes) {
+ super(maxSizeBytes);
+ }
+
+ @Override
+ protected int sizeOf(Uri key, Bitmap value) {
+ return value.getByteCount();
+ }
+}
diff --git a/packages/ExternalStorageProvider/AndroidManifest.xml b/packages/ExternalStorageProvider/AndroidManifest.xml
index afdb6bb..5272166 100644
--- a/packages/ExternalStorageProvider/AndroidManifest.xml
+++ b/packages/ExternalStorageProvider/AndroidManifest.xml
@@ -7,7 +7,7 @@
<application android:label="@string/app_label">
<provider
android:name=".ExternalStorageProvider"
- android:authorities="com.android.externalstorage"
+ android:authorities="com.android.externalstorage.documents"
android:grantUriPermissions="true"
android:exported="true"
android:permission="android.permission.MANAGE_DOCUMENTS">
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 659139d..b4bf563 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -22,10 +22,10 @@ import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.MatrixCursor;
+import android.database.MatrixCursor.RowBuilder;
import android.net.Uri;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
-import android.provider.BaseColumns;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.DocumentColumns;
import android.provider.DocumentsContract.Documents;
@@ -45,7 +45,7 @@ import java.util.LinkedList;
public class ExternalStorageProvider extends ContentProvider {
private static final String TAG = "ExternalStorage";
- private static final String AUTHORITY = "com.android.externalstorage";
+ private static final String AUTHORITY = "com.android.externalstorage.documents";
// TODO: support multiple storage devices
@@ -57,6 +57,14 @@ public class ExternalStorageProvider extends ContentProvider {
private static final int URI_DOCS_ID_CONTENTS = 4;
private static final int URI_DOCS_ID_SEARCH = 5;
+ static {
+ sMatcher.addURI(AUTHORITY, "roots", URI_ROOTS);
+ sMatcher.addURI(AUTHORITY, "roots/*", URI_ROOTS_ID);
+ sMatcher.addURI(AUTHORITY, "roots/*/docs/*", URI_DOCS_ID);
+ sMatcher.addURI(AUTHORITY, "roots/*/docs/*/contents", URI_DOCS_ID_CONTENTS);
+ sMatcher.addURI(AUTHORITY, "roots/*/docs/*/search", URI_DOCS_ID_SEARCH);
+ }
+
private HashMap<String, Root> mRoots = Maps.newHashMap();
private static class Root {
@@ -68,13 +76,15 @@ public class ExternalStorageProvider extends ContentProvider {
public File path;
}
- static {
- sMatcher.addURI(AUTHORITY, "roots", URI_ROOTS);
- sMatcher.addURI(AUTHORITY, "roots/*", URI_ROOTS_ID);
- sMatcher.addURI(AUTHORITY, "roots/*/docs/*", URI_DOCS_ID);
- sMatcher.addURI(AUTHORITY, "roots/*/docs/*/contents", URI_DOCS_ID_CONTENTS);
- sMatcher.addURI(AUTHORITY, "roots/*/docs/*/search", URI_DOCS_ID_SEARCH);
- }
+ private static final String[] ALL_ROOTS_COLUMNS = new String[] {
+ RootColumns.ROOT_ID, RootColumns.ROOT_TYPE, RootColumns.ICON, RootColumns.TITLE,
+ RootColumns.SUMMARY, RootColumns.AVAILABLE_BYTES
+ };
+
+ private static final String[] ALL_DOCUMENTS_COLUMNS = new String[] {
+ DocumentColumns.DOC_ID, DocumentColumns.DISPLAY_NAME, DocumentColumns.SIZE,
+ DocumentColumns.MIME_TYPE, DocumentColumns.LAST_MODIFIED, DocumentColumns.FLAGS
+ };
@Override
public boolean onCreate() {
@@ -93,64 +103,59 @@ public class ExternalStorageProvider extends ContentProvider {
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
-
- // TODO: support custom projections
- final String[] rootsProjection = new String[] {
- BaseColumns._ID, RootColumns.ROOT_ID, RootColumns.ROOT_TYPE, RootColumns.ICON,
- RootColumns.TITLE, RootColumns.SUMMARY, RootColumns.AVAILABLE_BYTES };
- final String[] docsProjection = new String[] {
- BaseColumns._ID, DocumentColumns.DISPLAY_NAME, DocumentColumns.SIZE,
- DocumentColumns.DOC_ID, DocumentColumns.MIME_TYPE, DocumentColumns.LAST_MODIFIED,
- DocumentColumns.FLAGS };
-
switch (sMatcher.match(uri)) {
case URI_ROOTS: {
- final MatrixCursor cursor = new MatrixCursor(rootsProjection);
+ final MatrixCursor result = new MatrixCursor(
+ projection != null ? projection : ALL_ROOTS_COLUMNS);
for (Root root : mRoots.values()) {
- includeRoot(cursor, root);
+ includeRoot(result, root);
}
- return cursor;
+ return result;
}
case URI_ROOTS_ID: {
final Root root = mRoots.get(DocumentsContract.getRootId(uri));
- final MatrixCursor cursor = new MatrixCursor(rootsProjection);
- includeRoot(cursor, root);
- return cursor;
+ final MatrixCursor result = new MatrixCursor(
+ projection != null ? projection : ALL_ROOTS_COLUMNS);
+ includeRoot(result, root);
+ return result;
}
case URI_DOCS_ID: {
final Root root = mRoots.get(DocumentsContract.getRootId(uri));
final String docId = DocumentsContract.getDocId(uri);
- final MatrixCursor cursor = new MatrixCursor(docsProjection);
+ final MatrixCursor result = new MatrixCursor(
+ projection != null ? projection : ALL_DOCUMENTS_COLUMNS);
final File file = docIdToFile(root, docId);
- includeFile(cursor, root, file);
- return cursor;
+ includeFile(result, root, file);
+ return result;
}
case URI_DOCS_ID_CONTENTS: {
final Root root = mRoots.get(DocumentsContract.getRootId(uri));
final String docId = DocumentsContract.getDocId(uri);
- final MatrixCursor cursor = new MatrixCursor(docsProjection);
+ final MatrixCursor result = new MatrixCursor(
+ projection != null ? projection : ALL_DOCUMENTS_COLUMNS);
final File parent = docIdToFile(root, docId);
for (File file : parent.listFiles()) {
- includeFile(cursor, root, file);
+ includeFile(result, root, file);
}
- return cursor;
+ return result;
}
case URI_DOCS_ID_SEARCH: {
final Root root = mRoots.get(DocumentsContract.getRootId(uri));
final String docId = DocumentsContract.getDocId(uri);
final String query = DocumentsContract.getSearchQuery(uri).toLowerCase();
- final MatrixCursor cursor = new MatrixCursor(docsProjection);
+ final MatrixCursor result = new MatrixCursor(
+ projection != null ? projection : ALL_DOCUMENTS_COLUMNS);
final File parent = docIdToFile(root, docId);
final LinkedList<File> pending = new LinkedList<File>();
pending.add(parent);
- while (!pending.isEmpty() && cursor.getCount() < 20) {
+ while (!pending.isEmpty() && result.getCount() < 20) {
final File file = pending.removeFirst();
if (file.isDirectory()) {
for (File child : file.listFiles()) {
@@ -158,12 +163,12 @@ public class ExternalStorageProvider extends ContentProvider {
}
} else {
if (file.getName().toLowerCase().contains(query)) {
- includeFile(cursor, root, file);
+ includeFile(result, root, file);
}
}
}
- return cursor;
+ return result;
}
default: {
throw new UnsupportedOperationException("Unsupported Uri " + uri);
@@ -196,13 +201,17 @@ public class ExternalStorageProvider extends ContentProvider {
}
}
- private void includeRoot(MatrixCursor cursor, Root root) {
- cursor.addRow(new Object[] {
- root.name.hashCode(), root.name, root.rootType, root.icon, root.title, root.summary,
- root.path.getFreeSpace() });
+ private void includeRoot(MatrixCursor result, Root root) {
+ final RowBuilder row = result.newRow();
+ row.offer(RootColumns.ROOT_ID, root.name);
+ row.offer(RootColumns.ROOT_TYPE, root.rootType);
+ row.offer(RootColumns.ICON, root.icon);
+ row.offer(RootColumns.TITLE, root.title);
+ row.offer(RootColumns.SUMMARY, root.summary);
+ row.offer(RootColumns.AVAILABLE_BYTES, root.path.getFreeSpace());
}
- private void includeFile(MatrixCursor cursor, Root root, File file) {
+ private void includeFile(MatrixCursor result, Root root, File file) {
int flags = 0;
if (file.isDirectory()) {
@@ -223,8 +232,6 @@ public class ExternalStorageProvider extends ContentProvider {
}
final String docId = fileToDocId(root, file);
- final long id = docId.hashCode();
-
final String displayName;
if (Documents.DOC_ID_ROOT.equals(docId)) {
displayName = root.title;
@@ -232,8 +239,13 @@ public class ExternalStorageProvider extends ContentProvider {
displayName = file.getName();
}
- cursor.addRow(new Object[] {
- id, displayName, file.length(), docId, mimeType, file.lastModified(), flags });
+ final RowBuilder row = result.newRow();
+ row.offer(DocumentColumns.DOC_ID, docId);
+ row.offer(DocumentColumns.DISPLAY_NAME, displayName);
+ row.offer(DocumentColumns.SIZE, file.length());
+ row.offer(DocumentColumns.MIME_TYPE, mimeType);
+ row.offer(DocumentColumns.LAST_MODIFIED, file.lastModified());
+ row.offer(DocumentColumns.FLAGS, flags);
}
@Override