diff options
11 files changed, 503 insertions, 53 deletions
diff --git a/api/current.txt b/api/current.txt index 5bdd4482cc6b..7fbc484a4b4b 100644 --- a/api/current.txt +++ b/api/current.txt @@ -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 diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java index b97b7c069f17..f445fd5481a4 100644 --- a/core/java/android/provider/DocumentsContract.java +++ b/core/java/android/provider/DocumentsContract.java @@ -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; } /** diff --git a/packages/DocumentsUI/Android.mk b/packages/DocumentsUI/Android.mk index 853353d45fff..79009532f315 100644 --- a/packages/DocumentsUI/Android.mk +++ b/packages/DocumentsUI/Android.mk @@ -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 diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java index 549e196ea6b8..c24341e4202f 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java +++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java @@ -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); + 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); + Uri contentsUri; - if (mType == TYPE_NORMAL) { - contentsUri = DocumentsContract.buildChildDocumentsUri( - uri.getAuthority(), DocumentsContract.getDocumentId(uri)); - } else { - contentsUri = uri; - } + 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); - return new DirectoryLoader(context, contentsUri, state.sortOrder); + } } @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) { diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java index fa674d5ac092..3f016b50143f 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java +++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java @@ -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: diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java b/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java new file mode 100644 index 000000000000..5f6fd1337598 --- /dev/null +++ b/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java @@ -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; + } +} diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootCursorWrapper.java b/packages/DocumentsUI/src/com/android/documentsui/RootCursorWrapper.java new file mode 100644 index 000000000000..d0e5ff6312ff --- /dev/null +++ b/packages/DocumentsUI/src/com/android/documentsui/RootCursorWrapper.java @@ -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); + } + +} diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java index f67c309a7614..ac3b74089275 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java +++ b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java @@ -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; @@ -126,6 +126,16 @@ public class RootsCache { } @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; } diff --git a/packages/DocumentsUI/src/com/android/documentsui/SortingCursorWrapper.java b/packages/DocumentsUI/src/com/android/documentsui/SortingCursorWrapper.java index 257c106a95c6..b434a35c7f2c 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/SortingCursorWrapper.java +++ b/packages/DocumentsUI/src/com/android/documentsui/SortingCursorWrapper.java @@ -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; } } diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java index feccadc13900..7721bcc7bcdb 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java +++ b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java @@ -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); diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java index 972883888d4c..189284b2d97e 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java +++ b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java @@ -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; + } } |