Merge "Resized thumbnails; async; extend MatrixCursor." into klp-dev
This commit is contained in:
@@ -8052,6 +8052,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 {
|
||||
|
||||
@@ -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 int index;
|
||||
private final int row;
|
||||
private final int endIndex;
|
||||
|
||||
RowBuilder(int index, int endIndex) {
|
||||
this.index = index;
|
||||
this.endIndex = endIndex;
|
||||
private int index;
|
||||
|
||||
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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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() {}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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">
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user