Merge "New recents behavior to match spec." into klp-dev
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user