Merge "New recents behavior to match spec." into klp-dev

This commit is contained in:
Jeff Sharkey
2013-09-03 03:50:27 +00:00
committed by Android (Google) Code Review
11 changed files with 504 additions and 54 deletions

View File

@@ -20865,6 +20865,7 @@ package android.provider {
field public static final int FLAG_PROVIDES_IMAGES = 32; // 0x20
field public static final int FLAG_PROVIDES_VIDEO = 16; // 0x10
field public static final int FLAG_SUPPORTS_CREATE = 1; // 0x1
field public static final int FLAG_SUPPORTS_RECENTS = 64; // 0x40
field public static final int ROOT_TYPE_DEVICE = 3; // 0x3
field public static final int ROOT_TYPE_SERVICE = 1; // 0x1
field public static final int ROOT_TYPE_SHORTCUT = 2; // 0x2

View File

@@ -411,6 +411,15 @@ public final class DocumentsContract {
* @see Intent#EXTRA_MIME_TYPES
*/
public static final int FLAG_PROVIDES_IMAGES = 1 << 5;
/**
* Flag indicating that this root can report recently modified
* documents.
*
* @see #COLUMN_FLAGS
* @see DocumentsContract#buildRecentDocumentsUri(String, String)
*/
public static final int FLAG_SUPPORTS_RECENTS = 1 << 6;
}
/**

View File

@@ -5,7 +5,7 @@ LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4
LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 guava
LOCAL_PACKAGE_NAME := DocumentsUI
LOCAL_CERTIFICATE := platform

View File

@@ -64,6 +64,7 @@ import android.widget.Toast;
import com.android.documentsui.DocumentsActivity.State;
import com.android.documentsui.model.DocumentInfo;
import com.android.documentsui.model.RootInfo;
import com.android.internal.util.Predicate;
import com.google.android.collect.Lists;
@@ -86,6 +87,7 @@ 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;
@@ -95,7 +97,10 @@ public class DirectoryFragment extends Fragment {
private LoaderCallbacks<DirectoryResult> mCallbacks;
private static final String EXTRA_TYPE = "type";
private static final String EXTRA_URI = "uri";
private static final String EXTRA_AUTHORITY = "authority";
private static final String EXTRA_ROOT_ID = "rootId";
private static final String EXTRA_DOC_ID = "docId";
private static final String EXTRA_QUERY = "query";
private static AtomicInteger sLoaderId = new AtomicInteger(4000);
@@ -104,24 +109,26 @@ public class DirectoryFragment extends Fragment {
private final int mLoaderId = sLoaderId.incrementAndGet();
public static void showNormal(FragmentManager fm, Uri uri) {
show(fm, TYPE_NORMAL, uri);
show(fm, TYPE_NORMAL, uri.getAuthority(), null, DocumentsContract.getDocumentId(uri), null);
}
public static void showSearch(FragmentManager fm, Uri uri, String query) {
final Uri searchUri = DocumentsContract.buildSearchDocumentsUri(
uri.getAuthority(), DocumentsContract.getDocumentId(uri), query);
show(fm, TYPE_SEARCH, searchUri);
show(fm, TYPE_SEARCH, uri.getAuthority(), null, DocumentsContract.getDocumentId(uri),
query);
}
@Deprecated
public static void showRecentsOpen(FragmentManager fm) {
// TODO: new recents behavior
show(fm, TYPE_RECENT_OPEN, null, null, null, null);
}
private static void show(FragmentManager fm, int type, Uri uri) {
private static void show(FragmentManager fm, int type, String authority, String rootId,
String docId, String query) {
final Bundle args = new Bundle();
args.putInt(EXTRA_TYPE, type);
args.putParcelable(EXTRA_URI, uri);
args.putString(EXTRA_AUTHORITY, authority);
args.putString(EXTRA_ROOT_ID, rootId);
args.putString(EXTRA_DOC_ID, docId);
args.putString(EXTRA_QUERY, query);
final DirectoryFragment fragment = new DirectoryFragment();
fragment.setArguments(args);
@@ -160,9 +167,8 @@ public class DirectoryFragment extends Fragment {
super.onActivityCreated(savedInstanceState);
final Context context = getActivity();
final Uri uri = getArguments().getParcelable(EXTRA_URI);
mAdapter = new DocumentsAdapter(uri.getAuthority());
mAdapter = new DocumentsAdapter();
mType = getArguments().getInt(EXTRA_TYPE);
mCallbacks = new LoaderCallbacks<DirectoryResult>() {
@@ -170,15 +176,26 @@ public class DirectoryFragment extends Fragment {
public Loader<DirectoryResult> onCreateLoader(int id, Bundle args) {
final State state = getDisplayState(DirectoryFragment.this);
Uri contentsUri;
if (mType == TYPE_NORMAL) {
contentsUri = DocumentsContract.buildChildDocumentsUri(
uri.getAuthority(), DocumentsContract.getDocumentId(uri));
} else {
contentsUri = uri;
}
final String authority = getArguments().getString(EXTRA_AUTHORITY);
final String rootId = getArguments().getString(EXTRA_ROOT_ID);
final String docId = getArguments().getString(EXTRA_DOC_ID);
final String query = getArguments().getString(EXTRA_QUERY);
return new DirectoryLoader(context, contentsUri, state.sortOrder);
Uri contentsUri;
switch (mType) {
case TYPE_NORMAL:
contentsUri = DocumentsContract.buildChildDocumentsUri(authority, docId);
return new DirectoryLoader(context, rootId, contentsUri, state.sortOrder);
case TYPE_SEARCH:
contentsUri = DocumentsContract.buildSearchDocumentsUri(
authority, docId, query);
return new DirectoryLoader(context, rootId, contentsUri, state.sortOrder);
case TYPE_RECENT_OPEN:
return new RecentLoader(context);
default:
throw new IllegalStateException("Unknown type " + mType);
}
}
@Override
@@ -246,8 +263,7 @@ public class DirectoryFragment extends Fragment {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
final Cursor cursor = mAdapter.getItem(position);
final Uri uri = getArguments().getParcelable(EXTRA_URI);
final DocumentInfo doc = DocumentInfo.fromDirectoryCursor(uri, cursor);
final DocumentInfo doc = DocumentInfo.fromDirectoryCursor(cursor);
if (mFilter.apply(doc)) {
((DocumentsActivity) getActivity()).onDocumentPicked(doc);
}
@@ -285,8 +301,7 @@ public class DirectoryFragment extends Fragment {
for (int i = 0; i < size; i++) {
if (checked.valueAt(i)) {
final Cursor cursor = mAdapter.getItem(checked.keyAt(i));
final Uri uri = getArguments().getParcelable(EXTRA_URI);
final DocumentInfo doc = DocumentInfo.fromDirectoryCursor(uri, cursor);
final DocumentInfo doc = DocumentInfo.fromDirectoryCursor(cursor);
docs.add(doc);
}
}
@@ -401,14 +416,8 @@ public class DirectoryFragment extends Fragment {
}
private class DocumentsAdapter extends BaseAdapter {
private final String mAuthority;
private Cursor mCursor;
public DocumentsAdapter(String authority) {
mAuthority = authority;
}
public void swapCursor(Cursor cursor) {
mCursor = cursor;
@@ -443,6 +452,8 @@ public class DirectoryFragment extends Fragment {
final Cursor cursor = getItem(position);
final String docAuthority = getCursorString(cursor, RootCursorWrapper.COLUMN_AUTHORITY);
final String docRootId = getCursorString(cursor, RootCursorWrapper.COLUMN_ROOT_ID);
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);
@@ -466,7 +477,7 @@ public class DirectoryFragment extends Fragment {
}
if ((docFlags & Document.FLAG_SUPPORTS_THUMBNAIL) != 0) {
final Uri uri = DocumentsContract.buildDocumentUri(mAuthority, docId);
final Uri uri = DocumentsContract.buildDocumentUri(docAuthority, docId);
final Bitmap cachedResult = thumbs.get(uri);
if (cachedResult != null) {
icon.setImageBitmap(cachedResult);
@@ -477,19 +488,27 @@ public class DirectoryFragment extends Fragment {
task.execute(uri);
}
} else if (docIcon != 0) {
icon.setImageDrawable(DocumentInfo.loadIcon(context, mAuthority, docIcon));
icon.setImageDrawable(DocumentInfo.loadIcon(context, docAuthority, docIcon));
} else {
icon.setImageDrawable(RootsCache.resolveDocumentIcon(context, docMimeType));
}
title.setText(docDisplayName);
icon1.setVisibility(View.GONE);
if (docSummary != null) {
summary.setText(docSummary);
if (mType == TYPE_RECENT_OPEN) {
final RootInfo root = roots.getRoot(docAuthority, docRootId);
icon1.setVisibility(View.VISIBLE);
icon1.setImageDrawable(root.loadIcon(context));
summary.setText(root.getDirectoryString());
summary.setVisibility(View.VISIBLE);
} else {
summary.setVisibility(View.INVISIBLE);
icon1.setVisibility(View.GONE);
if (docSummary != null) {
summary.setText(docSummary);
summary.setVisibility(View.VISIBLE);
} else {
summary.setVisibility(View.INVISIBLE);
}
}
if (summaryGrid != null) {

View File

@@ -48,14 +48,16 @@ class DirectoryResult implements AutoCloseable {
public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> {
private final ForceLoadContentObserver mObserver = new ForceLoadContentObserver();
private final String mRootId;
private final Uri mUri;
private final int mSortOrder;
private CancellationSignal mSignal;
private DirectoryResult mResult;
public DirectoryLoader(Context context, Uri uri, int sortOrder) {
public DirectoryLoader(Context context, String rootId, Uri uri, int sortOrder) {
super(context);
mRootId = rootId;
mUri = uri;
mSortOrder = sortOrder;
}
@@ -69,12 +71,16 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> {
mSignal = new CancellationSignal();
}
final DirectoryResult result = new DirectoryResult();
final String authority = mUri.getAuthority();
try {
result.client = getContext()
.getContentResolver().acquireUnstableContentProviderClient(mUri.getAuthority());
.getContentResolver().acquireUnstableContentProviderClient(authority);
final Cursor cursor = result.client.query(
mUri, null, null, null, getQuerySortOrder(), mSignal);
result.cursor = new SortingCursorWrapper(cursor, mSortOrder);
mUri, null, null, null, getQuerySortOrder(mSortOrder), mSignal);
final Cursor withRoot = new RootCursorWrapper(mUri.getAuthority(), mRootId, cursor, -1);
final Cursor sorted = new SortingCursorWrapper(withRoot, mSortOrder);
result.cursor = sorted;
result.cursor.registerContentObserver(mObserver);
} catch (Exception e) {
result.exception = e;
@@ -149,8 +155,8 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> {
getContext().getContentResolver().unregisterContentObserver(mObserver);
}
private String getQuerySortOrder() {
switch (mSortOrder) {
public static String getQuerySortOrder(int sortOrder) {
switch (sortOrder) {
case SORT_ORDER_DISPLAY_NAME:
return Document.COLUMN_DISPLAY_NAME + " ASC";
case SORT_ORDER_LAST_MODIFIED:

View File

@@ -0,0 +1,253 @@
/*
* 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 static com.android.documentsui.DocumentsActivity.TAG;
import android.content.AsyncTaskLoader;
import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.database.MergeCursor;
import android.net.Uri;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Root;
import android.util.Log;
import com.android.documentsui.DocumentsActivity.State;
import com.android.documentsui.model.RootInfo;
import com.google.android.collect.Maps;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.AbstractFuture;
import libcore.io.IoUtils;
import java.io.Closeable;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class RecentLoader extends AsyncTaskLoader<DirectoryResult> {
public static final int MAX_OUTSTANDING_RECENTS = 2;
/**
* Time to wait for first pass to complete before returning partial results.
*/
public static final int MAX_FIRST_PASS_WAIT_MILLIS = 500;
/**
* Maximum documents from a single root.
*/
public static final int MAX_DOCS_FROM_ROOT = 24;
private static final ExecutorService sExecutor = buildExecutor();
/**
* Create a bounded thread pool for fetching recents; it creates threads as
* needed (up to maximum) and reclaims them when finished.
*/
private static ExecutorService buildExecutor() {
// Create a bounded thread pool for fetching recents; it creates
// threads as needed (up to maximum) and reclaims them when finished.
final ThreadPoolExecutor executor = new ThreadPoolExecutor(
MAX_OUTSTANDING_RECENTS, MAX_OUTSTANDING_RECENTS, 10, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>());
executor.allowCoreThreadTimeOut(true);
return executor;
}
private final HashMap<RootInfo, RecentTask> mTasks = Maps.newHashMap();
private final int mSortOrder = State.SORT_ORDER_LAST_MODIFIED;
private CountDownLatch mFirstPassLatch;
private volatile boolean mFirstPassDone;
private DirectoryResult mResult;
// TODO: create better transfer of ownership around cursor to ensure its
// closed in all edge cases.
public class RecentTask extends AbstractFuture<Cursor> implements Runnable, Closeable {
public final String authority;
public final String rootId;
private Cursor mWithRoot;
public RecentTask(String authority, String rootId) {
this.authority = authority;
this.rootId = rootId;
}
@Override
public void run() {
if (isCancelled()) return;
final ContentResolver resolver = getContext().getContentResolver();
final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
authority);
try {
final Uri uri = DocumentsContract.buildRecentDocumentsUri(authority, rootId);
final Cursor cursor = client.query(
uri, null, null, null, DirectoryLoader.getQuerySortOrder(mSortOrder));
mWithRoot = new RootCursorWrapper(authority, rootId, cursor, MAX_DOCS_FROM_ROOT);
set(mWithRoot);
mFirstPassLatch.countDown();
if (mFirstPassDone) {
onContentChanged();
}
} catch (Exception e) {
setException(e);
} finally {
ContentProviderClient.closeQuietly(client);
}
}
@Override
public void close() throws IOException {
IoUtils.closeQuietly(mWithRoot);
}
}
public RecentLoader(Context context) {
super(context);
}
@Override
public DirectoryResult loadInBackground() {
if (mFirstPassLatch == null) {
// First time through we kick off all the recent tasks, and wait
// around to see if everyone finishes quickly.
final RootsCache roots = DocumentsApplication.getRootsCache(getContext());
for (RootInfo root : roots.getRoots()) {
if ((root.flags & Root.FLAG_SUPPORTS_RECENTS) != 0) {
final RecentTask task = new RecentTask(root.authority, root.rootId);
mTasks.put(root, task);
}
}
mFirstPassLatch = new CountDownLatch(mTasks.size());
for (RecentTask task : mTasks.values()) {
sExecutor.execute(task);
}
try {
mFirstPassLatch.await(MAX_FIRST_PASS_WAIT_MILLIS, TimeUnit.MILLISECONDS);
mFirstPassDone = true;
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
// Collect all finished tasks
List<Cursor> cursors = Lists.newArrayList();
for (RecentTask task : mTasks.values()) {
if (task.isDone()) {
try {
cursors.add(task.get());
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (ExecutionException e) {
Log.w(TAG, "Failed to load " + task.authority + ", " + task.rootId, e);
}
}
}
final DirectoryResult result = new DirectoryResult();
if (cursors.size() > 0) {
final MergeCursor merged = new MergeCursor(cursors.toArray(new Cursor[cursors.size()]));
final SortingCursorWrapper sorted = new SortingCursorWrapper(
merged, State.SORT_ORDER_LAST_MODIFIED) {
@Override
public void close() {
// Ignored, since we manage cursor lifecycle internally
}
};
result.cursor = sorted;
}
return result;
}
@Override
public void cancelLoadInBackground() {
super.cancelLoadInBackground();
}
@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();
for (RecentTask task : mTasks.values()) {
IoUtils.closeQuietly(task);
}
IoUtils.closeQuietly(mResult);
mResult = null;
}
}

View File

@@ -0,0 +1,132 @@
/*
* 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;
/**
* Cursor wrapper that adds columns to identify which root a document came from.
*/
public class RootCursorWrapper extends AbstractCursor {
private final String mAuthority;
private final String mRootId;
private final Cursor mCursor;
private final int mCount;
private final String[] mColumnNames;
private final int mAuthorityIndex;
private final int mRootIdIndex;
public static final String COLUMN_AUTHORITY = "android:authority";
public static final String COLUMN_ROOT_ID = "android:rootId";
public RootCursorWrapper(String authority, String rootId, Cursor cursor, int maxCount) {
mAuthority = authority;
mRootId = rootId;
mCursor = cursor;
final int count = cursor.getCount();
if (maxCount > 0 && count > maxCount) {
mCount = maxCount;
} else {
mCount = count;
}
if (cursor.getColumnIndex(COLUMN_AUTHORITY) != -1
|| cursor.getColumnIndex(COLUMN_ROOT_ID) != -1) {
throw new IllegalArgumentException("Cursor contains internal columns!");
}
final String[] before = cursor.getColumnNames();
mColumnNames = new String[before.length + 2];
System.arraycopy(before, 0, mColumnNames, 0, before.length);
mAuthorityIndex = before.length;
mRootIdIndex = before.length + 1;
mColumnNames[mAuthorityIndex] = COLUMN_AUTHORITY;
mColumnNames[mRootIdIndex] = COLUMN_ROOT_ID;
}
@Override
public void close() {
super.close();
mCursor.close();
}
@Override
public boolean onMove(int oldPosition, int newPosition) {
return mCursor.moveToPosition(newPosition);
}
@Override
public String[] getColumnNames() {
return mColumnNames;
}
@Override
public int getCount() {
return mCount;
}
@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) {
if (column == mAuthorityIndex) {
return mAuthority;
} else if (column == mRootIdIndex) {
return mRootId;
} else {
return mCursor.getString(column);
}
}
@Override
public int getType(int column) {
return mCursor.getType(column);
}
@Override
public boolean isNull(int column) {
return mCursor.isNull(column);
}
}

View File

@@ -50,7 +50,7 @@ public class RootsCache {
// TODO: cache roots in local provider to avoid spinning up backends
// TODO: root updates should trigger UI refresh
private static final boolean RECENTS_ENABLED = false;
private static final boolean RECENTS_ENABLED = true;
private final Context mContext;
@@ -125,6 +125,16 @@ public class RootsCache {
return null;
}
@GuardedBy("ActivityThread")
public RootInfo getRoot(String authority, String rootId) {
for (RootInfo root : mRoots) {
if (Objects.equal(root.authority, authority) && Objects.equal(root.rootId, rootId)) {
return root;
}
}
return null;
}
@GuardedBy("ActivityThread")
public RootInfo getRecentsRoot() {
return mRecentsRoot;

View File

@@ -54,11 +54,6 @@ public class SortingCursorWrapper extends AbstractCursor {
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();
@@ -66,8 +61,10 @@ public class SortingCursorWrapper extends AbstractCursor {
switch (sortOrder) {
case SORT_ORDER_DISPLAY_NAME:
final String mimeType = cursor.getString(mimeTypeIndex);
final String displayName = cursor.getString(displayNameIndex);
final String mimeType = cursor.getString(
cursor.getColumnIndex(Document.COLUMN_MIME_TYPE));
final String displayName = cursor.getString(
cursor.getColumnIndex(Document.COLUMN_DISPLAY_NAME));
if (Document.MIME_TYPE_DIR.equals(mimeType)) {
mValueString[i] = '\001' + displayName;
} else {
@@ -75,10 +72,11 @@ public class SortingCursorWrapper extends AbstractCursor {
}
break;
case SORT_ORDER_LAST_MODIFIED:
mValueLong[i] = cursor.getLong(lastModifiedIndex);
mValueLong[i] = cursor.getLong(
cursor.getColumnIndex(Document.COLUMN_LAST_MODIFIED));
break;
case SORT_ORDER_SIZE:
mValueLong[i] = cursor.getLong(sizeIndex);
mValueLong[i] = cursor.getLong(cursor.getColumnIndex(Document.COLUMN_SIZE));
break;
}
}

View File

@@ -27,6 +27,7 @@ import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
import com.android.documentsui.RecentsProvider;
import com.android.documentsui.RootCursorWrapper;
import libcore.io.IoUtils;
@@ -101,9 +102,9 @@ public class DocumentInfo implements Durable {
out.writeInt(icon);
}
public static DocumentInfo fromDirectoryCursor(Uri parent, Cursor cursor) {
public static DocumentInfo fromDirectoryCursor(Cursor cursor) {
final DocumentInfo doc = new DocumentInfo();
final String authority = parent.getAuthority();
final String authority = getCursorString(cursor, RootCursorWrapper.COLUMN_AUTHORITY);
final String docId = getCursorString(cursor, Document.COLUMN_DOCUMENT_ID);
doc.uri = DocumentsContract.buildDocumentUri(authority, docId);
doc.mimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);

View File

@@ -25,6 +25,8 @@ import android.database.Cursor;
import android.graphics.drawable.Drawable;
import android.provider.DocumentsContract.Root;
import java.util.Objects;
/**
* Representation of a {@link Root}.
*/
@@ -56,4 +58,23 @@ public class RootInfo {
public Drawable loadIcon(Context context) {
return DocumentInfo.loadIcon(context, authority, icon);
}
@Override
public boolean equals(Object o) {
if (o instanceof RootInfo) {
final RootInfo root = (RootInfo) o;
return Objects.equals(authority, root.authority) && Objects.equals(rootId, root.rootId);
} else {
return false;
}
}
@Override
public int hashCode() {
return Objects.hash(authority, rootId);
}
public String getDirectoryString() {
return (summary != null) ? summary : title;
}
}