Re-enable sorting in the DirectoryFragment.
- Move sorting from the back-end (using SortingCursorWrapper) to the the
front-end (in DocumentsAdapter). This makes it such that re-sorting
the directory contents doesn't necessitate a reload.
- Update DirectoryLoaders to just return unsorted results, and rely on
the UI to sort them.
- Remove the (now-unused) SortingCursorWrapper.
- Update Model tests to test sorting.
BUG=26024369
Change-Id: I871cc0e496267d381ae546e0309125d04649415a
diff --git a/src/com/android/documentsui/BaseActivity.java b/src/com/android/documentsui/BaseActivity.java
index edf384e..5cb3b0d 100644
--- a/src/com/android/documentsui/BaseActivity.java
+++ b/src/com/android/documentsui/BaseActivity.java
@@ -85,7 +85,7 @@
private int mLayoutId;
private DirectoryContainerView mDirectoryContainer;
- public abstract void onDocumentPicked(DocumentInfo doc, @Nullable DocumentContext siblings);
+ public abstract void onDocumentPicked(DocumentInfo doc, @Nullable SiblingProvider siblings);
public abstract void onDocumentsPicked(List<DocumentInfo> docs);
abstract void onTaskFinished(Uri... uris);
@@ -826,7 +826,7 @@
* Interface providing access to current view of documents
* even when all documents are not homed to the same parent.
*/
- public interface DocumentContext {
+ public interface SiblingProvider {
/**
* Returns the cursor for the selected document. The cursor can be used to retrieve
* details about a document and its siblings.
diff --git a/src/com/android/documentsui/DirectoryLoader.java b/src/com/android/documentsui/DirectoryLoader.java
index b0bbec3..4eacee5 100644
--- a/src/com/android/documentsui/DirectoryLoader.java
+++ b/src/com/android/documentsui/DirectoryLoader.java
@@ -156,9 +156,6 @@
if (mType == DirectoryFragment.TYPE_SEARCH) {
// Filter directories out of search results, for now
cursor = new FilteringCursorWrapper(cursor, null, SEARCH_REJECT_MIMES);
- } else {
- // Normal directories should have sorting applied
- cursor = new SortingCursorWrapper(cursor, result.sortOrder);
}
result.client = client;
diff --git a/src/com/android/documentsui/DocumentsActivity.java b/src/com/android/documentsui/DocumentsActivity.java
index 8754f68..313d303 100644
--- a/src/com/android/documentsui/DocumentsActivity.java
+++ b/src/com/android/documentsui/DocumentsActivity.java
@@ -19,8 +19,8 @@
import static com.android.documentsui.State.ACTION_CREATE;
import static com.android.documentsui.State.ACTION_GET_CONTENT;
import static com.android.documentsui.State.ACTION_OPEN;
-import static com.android.documentsui.State.ACTION_PICK_COPY_DESTINATION;
import static com.android.documentsui.State.ACTION_OPEN_TREE;
+import static com.android.documentsui.State.ACTION_PICK_COPY_DESTINATION;
import static com.android.documentsui.dirlist.DirectoryFragment.ANIM_NONE;
import android.app.Activity;
@@ -31,7 +31,6 @@
import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.ContentValues;
-import android.content.Context;
import android.content.Intent;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
@@ -419,7 +418,7 @@
}
@Override
- public void onDocumentPicked(DocumentInfo doc, DocumentContext context) {
+ public void onDocumentPicked(DocumentInfo doc, SiblingProvider siblings) {
final FragmentManager fm = getFragmentManager();
if (doc.isContainer()) {
openContainerDocument(doc);
diff --git a/src/com/android/documentsui/DownloadsActivity.java b/src/com/android/documentsui/DownloadsActivity.java
index cccbbc8..b806ced 100644
--- a/src/com/android/documentsui/DownloadsActivity.java
+++ b/src/com/android/documentsui/DownloadsActivity.java
@@ -183,7 +183,7 @@
}
@Override
- public void onDocumentPicked(DocumentInfo doc, DocumentContext context) {
+ public void onDocumentPicked(DocumentInfo doc, SiblingProvider siblings) {
final FragmentManager fm = getFragmentManager();
if (doc.isContainer()) {
openContainerDocument(doc);
diff --git a/src/com/android/documentsui/FilesActivity.java b/src/com/android/documentsui/FilesActivity.java
index 0b6a09f..dd8ccf9 100644
--- a/src/com/android/documentsui/FilesActivity.java
+++ b/src/com/android/documentsui/FilesActivity.java
@@ -323,7 +323,7 @@
}
@Override
- public void onDocumentPicked(DocumentInfo doc, @Nullable DocumentContext siblings) {
+ public void onDocumentPicked(DocumentInfo doc, @Nullable SiblingProvider siblings) {
if (doc.isContainer()) {
openContainerDocument(doc);
} else {
@@ -334,7 +334,7 @@
/**
* Launches an intent to view the specified document.
*/
- private void openDocument(DocumentInfo doc, @Nullable DocumentContext siblings) {
+ private void openDocument(DocumentInfo doc, @Nullable SiblingProvider siblings) {
Intent intent = null;
if (siblings != null) {
QuickViewIntentBuilder builder = new QuickViewIntentBuilder(
diff --git a/src/com/android/documentsui/QuickViewIntentBuilder.java b/src/com/android/documentsui/QuickViewIntentBuilder.java
index 605c530..5a80c39 100644
--- a/src/com/android/documentsui/QuickViewIntentBuilder.java
+++ b/src/com/android/documentsui/QuickViewIntentBuilder.java
@@ -34,7 +34,7 @@
import android.text.TextUtils;
import android.util.Log;
-import com.android.documentsui.BaseActivity.DocumentContext;
+import com.android.documentsui.BaseActivity.SiblingProvider;
import com.android.documentsui.model.DocumentInfo;
/**
@@ -43,7 +43,7 @@
final class QuickViewIntentBuilder {
private final DocumentInfo mDocument;
- private final DocumentContext mContext;
+ private final SiblingProvider mSiblings;
private final PackageManager mPkgManager;
private final Resources mResources;
@@ -55,12 +55,12 @@
PackageManager pkgManager,
Resources resources,
DocumentInfo doc,
- DocumentContext context) {
+ SiblingProvider siblings) {
mPkgManager = pkgManager;
mResources = resources;
mDocument = doc;
- mContext = context;
+ mSiblings = siblings;
}
/**
@@ -84,7 +84,7 @@
intent.setPackage(trustedPkg);
if (hasRegisteredHandler(intent)) {
// We have a trusted handler. Load all of the docs into the intent.
- Cursor cursor = mContext.getCursor();
+ Cursor cursor = mSiblings.getCursor();
for (int i = 0; i < cursor.getCount(); i++) {
onNextItem(i, cursor);
}
diff --git a/src/com/android/documentsui/RecentLoader.java b/src/com/android/documentsui/RecentLoader.java
index 4bd6ae6..0ee54e6 100644
--- a/src/com/android/documentsui/RecentLoader.java
+++ b/src/com/android/documentsui/RecentLoader.java
@@ -233,12 +233,6 @@
final DirectoryResult result = new DirectoryResult();
result.sortOrder = SORT_ORDER_LAST_MODIFIED;
- // Hint to UI if we're still loading
- final Bundle extras = new Bundle();
- if (!allDone) {
- extras.putBoolean(DocumentsContract.EXTRA_LOADING, true);
- }
-
final Cursor merged;
if (cursors.size() > 0) {
merged = new MergeCursor(cursors.toArray(new Cursor[cursors.size()]));
@@ -247,14 +241,13 @@
merged = new MatrixCursor(new String[0]);
}
- final SortingCursorWrapper sorted = new SortingCursorWrapper(merged, result.sortOrder) {
- @Override
- public Bundle getExtras() {
- return extras;
- }
- };
+ // Tell the UI if this is an in-progress result. When loading is complete, another update is
+ // sent with EXTRA_LOADING set to false.
+ Bundle extras = new Bundle();
+ extras.putBoolean(DocumentsContract.EXTRA_LOADING, !allDone);
+ merged.setExtras(extras);
- result.cursor = sorted;
+ result.cursor = merged;
return result;
}
diff --git a/src/com/android/documentsui/SortingCursorWrapper.java b/src/com/android/documentsui/SortingCursorWrapper.java
deleted file mode 100644
index 6698ff1..0000000
--- a/src/com/android/documentsui/SortingCursorWrapper.java
+++ /dev/null
@@ -1,257 +0,0 @@
-/*
- * 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.State.SORT_ORDER_DISPLAY_NAME;
-import static com.android.documentsui.State.SORT_ORDER_LAST_MODIFIED;
-import static com.android.documentsui.State.SORT_ORDER_SIZE;
-import static com.android.documentsui.model.DocumentInfo.getCursorLong;
-import static com.android.documentsui.model.DocumentInfo.getCursorString;
-
-import android.database.AbstractCursor;
-import android.database.Cursor;
-import android.os.Bundle;
-import android.provider.DocumentsContract.Document;
-
-import com.android.documentsui.model.DocumentInfo;
-
-/**
- * Cursor wrapper that presents a sorted view of the underlying cursor. Handles
- * common {@link Document} sorting modes, such as ordering directories first.
- */
-public class SortingCursorWrapper extends AbstractCursor {
- private final Cursor mCursor;
-
- private final int[] mPosition;
- private final String[] mValueString;
- private final long[] mValueLong;
-
- public SortingCursorWrapper(Cursor cursor, int sortOrder) {
- mCursor = cursor;
-
- final int count = cursor.getCount();
- mPosition = new int[count];
- switch (sortOrder) {
- case SORT_ORDER_DISPLAY_NAME:
- mValueString = new String[count];
- mValueLong = null;
- break;
- case SORT_ORDER_LAST_MODIFIED:
- case SORT_ORDER_SIZE:
- mValueString = null;
- mValueLong = new long[count];
- break;
- default:
- throw new IllegalArgumentException();
- }
-
- cursor.moveToPosition(-1);
- for (int i = 0; i < count; i++) {
- cursor.moveToNext();
- mPosition[i] = i;
-
- switch (sortOrder) {
- case SORT_ORDER_DISPLAY_NAME:
- final String mimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
- final String displayName = getCursorString(
- cursor, Document.COLUMN_DISPLAY_NAME);
- if (Document.MIME_TYPE_DIR.equals(mimeType)) {
- mValueString[i] = DocumentInfo.DIR_PREFIX + displayName;
- } else {
- mValueString[i] = displayName;
- }
- break;
- case SORT_ORDER_LAST_MODIFIED:
- mValueLong[i] = getCursorLong(cursor, Document.COLUMN_LAST_MODIFIED);
- break;
- case SORT_ORDER_SIZE:
- mValueLong[i] = getCursorLong(cursor, Document.COLUMN_SIZE);
- break;
- }
- }
-
- switch (sortOrder) {
- case SORT_ORDER_DISPLAY_NAME:
- synchronized (SortingCursorWrapper.class) {
-
- binarySort(mPosition, mValueString);
- }
- break;
- case SORT_ORDER_LAST_MODIFIED:
- case SORT_ORDER_SIZE:
- binarySort(mPosition, mValueLong);
- break;
- }
- }
-
- @Override
- public Bundle getExtras() {
- return mCursor.getExtras();
- }
-
- @Override
- public void close() {
- super.close();
- mCursor.close();
- }
-
- @Override
- public boolean onMove(int oldPosition, int newPosition) {
- return mCursor.moveToPosition(mPosition[newPosition]);
- }
-
- @Override
- public String[] getColumnNames() {
- return mCursor.getColumnNames();
- }
-
- @Override
- public int getCount() {
- return mCursor.getCount();
- }
-
- @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) {
- return mCursor.getString(column);
- }
-
- @Override
- public int getType(int column) {
- return mCursor.getType(column);
- }
-
- @Override
- public boolean isNull(int column) {
- return mCursor.isNull(column);
- }
-
- /**
- * Borrowed from TimSort.binarySort(), but modified to sort two column
- * dataset.
- */
- private static void binarySort(int[] position, String[] value) {
- final int count = position.length;
- for (int start = 1; start < count; start++) {
- final int pivotPosition = position[start];
- final String pivotValue = value[start];
-
- int left = 0;
- int right = start;
-
- while (left < right) {
- int mid = (left + right) >>> 1;
-
- final String lhs = pivotValue;
- final String rhs = value[mid];
- final int compare = DocumentInfo.compareToIgnoreCaseNullable(lhs, rhs);
-
- if (compare < 0) {
- right = mid;
- } else {
- left = mid + 1;
- }
- }
-
- int n = start - left;
- switch (n) {
- case 2:
- position[left + 2] = position[left + 1];
- value[left + 2] = value[left + 1];
- case 1:
- position[left + 1] = position[left];
- value[left + 1] = value[left];
- break;
- default:
- System.arraycopy(position, left, position, left + 1, n);
- System.arraycopy(value, left, value, left + 1, n);
- }
-
- position[left] = pivotPosition;
- value[left] = pivotValue;
- }
- }
-
- /**
- * Borrowed from TimSort.binarySort(), but modified to sort two column
- * dataset.
- */
- private static void binarySort(int[] position, long[] value) {
- final int count = position.length;
- for (int start = 1; start < count; start++) {
- final int pivotPosition = position[start];
- final long pivotValue = value[start];
-
- int left = 0;
- int right = start;
-
- while (left < right) {
- int mid = (left + right) >>> 1;
-
- final long lhs = pivotValue;
- final long rhs = value[mid];
- final int compare = Long.compare(lhs, rhs);
- if (compare > 0) {
- right = mid;
- } else {
- left = mid + 1;
- }
- }
-
- int n = start - left;
- switch (n) {
- case 2:
- position[left + 2] = position[left + 1];
- value[left + 2] = value[left + 1];
- case 1:
- position[left + 1] = position[left];
- value[left + 1] = value[left];
- break;
- default:
- System.arraycopy(position, left, position, left + 1, n);
- System.arraycopy(value, left, value, left + 1, n);
- }
-
- position[left] = pivotPosition;
- value[left] = pivotValue;
- }
- }
-}
diff --git a/src/com/android/documentsui/dirlist/DirectoryFragment.java b/src/com/android/documentsui/dirlist/DirectoryFragment.java
index b66a124..ef71693 100644
--- a/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -86,7 +86,6 @@
import android.widget.TextView;
import com.android.documentsui.BaseActivity;
-import com.android.documentsui.BaseActivity.DocumentContext;
import com.android.documentsui.CopyService;
import com.android.documentsui.DirectoryLoader;
import com.android.documentsui.DirectoryResult;
@@ -107,7 +106,6 @@
import com.android.documentsui.RootCursorWrapper;
import com.android.documentsui.RootsCache;
import com.android.documentsui.Shared;
-import com.android.documentsui.Shared;
import com.android.documentsui.Snackbars;
import com.android.documentsui.State;
import com.android.documentsui.ThumbnailCache;
@@ -436,6 +434,9 @@
if (container != null && !getArguments().getBoolean(EXTRA_IGNORE_STATE, false)) {
getView().restoreHierarchyState(container);
} else if (mLastSortOrder != state.derivedSortOrder) {
+ // The derived sort order takes the user sort order into account, but applies
+ // directory-specific defaults when the user doesn't explicitly set the sort
+ // order. Scroll to the top if the sort order actually changed.
mRecView.smoothScrollToPosition(0);
}
@@ -996,16 +997,14 @@
}
final class DocumentsAdapter
- extends RecyclerView.Adapter<DocumentHolder>
- implements Model.UpdateListener {
+ extends RecyclerView.Adapter<DocumentHolder>
+ implements Model.UpdateListener {
static private final String TAG = "DocumentsAdapter";
private final Context mContext;
/**
* Map of model IDs to adapter positions. This is the data structure that determines what
- * shows up in the UI, and where. Note that item positions can change if items are
- * removed/added, so this list should only be used in onBindViewHolder. See
- * {@link RecyclerView.Adapter.onCreateViewHolder} for details.
+ * shows up in the UI, and where.
*/
// TODO(stable-id): need to keep this up-to-date when items are added/removed
private List<String> mModelIds = new ArrayList<>();
@@ -1063,8 +1062,8 @@
final ThumbnailCache thumbs = DocumentsApplication.getThumbnailsCache(
context, mThumbSize);
- final String modelId = mModelIds.get(position);
- final Cursor cursor = mModel.getItem(modelId);
+ holder.modelId = mModelIds.get(position);
+ final Cursor cursor = mModel.getItem(holder.modelId);
checkNotNull(cursor, "Cursor cannot be null.");
final String docAuthority = getCursorString(cursor, RootCursorWrapper.COLUMN_AUTHORITY);
@@ -1078,10 +1077,9 @@
final String docSummary = getCursorString(cursor, Document.COLUMN_SUMMARY);
final long docSize = getCursorLong(cursor, Document.COLUMN_SIZE);
- holder.modelId = Model.createId(cursor);
final View itemView = holder.itemView;
- holder.setSelected(isSelected(modelId));
+ holder.setSelected(isSelected(holder.modelId));
final ImageView iconMime = (ImageView) itemView.findViewById(R.id.icon_mime);
final ImageView iconThumb = (ImageView) itemView.findViewById(R.id.icon_thumb);
@@ -1222,15 +1220,13 @@
@Override
public int getItemCount() {
- if (DEBUG) Log.d(TAG, "getItemCount called: " + mModelIds.size());
return mModelIds.size();
}
@Override
public void onModelUpdate(Model model) {
// TODO(stable-id): Sort model IDs, categorize by dir/file, etc
- if (DEBUG) Log.d(TAG, "onModelUpdate called");
- mModelIds = Lists.newArrayList(model.getIds());
+ mModelIds = Lists.newArrayList(model.getModelIds());
}
@Override
@@ -1285,6 +1281,16 @@
notifyItemInserted(pos);
}
}
+
+ /**
+ * Returns a list of model IDs of items currently in the adapter. Excludes items that are
+ * currently hidden (see {@link #hide(String...)}).
+ *
+ * @return A list of Model IDs.
+ */
+ public List<String> getModelIds() {
+ return mModelIds;
+ }
}
private static String formatTime(Context context, long when) {
@@ -1440,7 +1446,7 @@
Snackbars.makeSnackbar(activity,
activity.getResources().getQuantityString(
R.plurals.clipboard_files_clipped, docs.size(), docs.size()),
- Snackbar.LENGTH_SHORT).show();
+ Snackbar.LENGTH_SHORT).show();
}
}.execute(selection);
}
@@ -1476,7 +1482,8 @@
}
public void selectAllFiles() {
- boolean changed = mSelectionManager.setItemsSelected(mModel.getIds(), true);
+ // Only select things currently visible in the adapter.
+ boolean changed = mSelectionManager.setItemsSelected(mAdapter.getModelIds(), true);
if (changed) {
updateDisplayState();
}
diff --git a/src/com/android/documentsui/dirlist/Model.java b/src/com/android/documentsui/dirlist/Model.java
index fce16c6..0d4d37e 100644
--- a/src/com/android/documentsui/dirlist/Model.java
+++ b/src/com/android/documentsui/dirlist/Model.java
@@ -17,6 +17,10 @@
package com.android.documentsui.dirlist;
import static com.android.documentsui.Shared.DEBUG;
+import static com.android.documentsui.State.SORT_ORDER_DISPLAY_NAME;
+import static com.android.documentsui.State.SORT_ORDER_LAST_MODIFIED;
+import static com.android.documentsui.State.SORT_ORDER_SIZE;
+import static com.android.documentsui.model.DocumentInfo.getCursorLong;
import static com.android.documentsui.model.DocumentInfo.getCursorString;
import static com.android.internal.util.Preconditions.checkNotNull;
@@ -34,7 +38,7 @@
import android.support.v7.widget.RecyclerView;
import android.util.Log;
-import com.android.documentsui.BaseActivity.DocumentContext;
+import com.android.documentsui.BaseActivity.SiblingProvider;
import com.android.documentsui.DirectoryResult;
import com.android.documentsui.DocumentsApplication;
import com.android.documentsui.RootCursorWrapper;
@@ -44,46 +48,49 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
-import java.util.Set;
+import java.util.Map;
/**
* The data model for the current loaded directory.
*/
@VisibleForTesting
-public class Model implements DocumentContext {
+public class Model implements SiblingProvider {
private static final String TAG = "Model";
+
private Context mContext;
- private int mCursorCount;
private boolean mIsLoading;
private List<UpdateListener> mUpdateListeners = new ArrayList<>();
@Nullable private Cursor mCursor;
+ private int mCursorCount;
+ /** Maps Model ID to cursor positions, for looking up items by Model ID. */
+ private Map<String, Integer> mPositions = new HashMap<>();
+ /**
+ * A sorted array of model IDs for the files currently in the Model. Sort order is determined
+ * by {@link #mSortOrder}
+ */
+ private List<String> mIds = new ArrayList<>();
+ private int mSortOrder = SORT_ORDER_DISPLAY_NAME;
+
@Nullable String info;
@Nullable String error;
- private HashMap<String, Integer> mPositions = new HashMap<>();
Model(Context context, RecyclerView.Adapter<?> viewAdapter) {
mContext = context;
}
/**
- * Generates a Model ID for a cursor entry that refers to a document. The Model ID is a
- * unique string that can be used to identify the document referred to by the cursor.
+ * Generates a Model ID for a cursor entry that refers to a document. The Model ID is a unique
+ * string that can be used to identify the document referred to by the cursor.
*
* @param c A cursor that refers to a document.
*/
- public static String createId(Cursor c) {
+ private static String createModelId(Cursor c) {
+ // TODO: Maybe more efficient to use just the document ID, in cases where there is only one
+ // authority (which should be the majority of cases).
return getCursorString(c, RootCursorWrapper.COLUMN_AUTHORITY) +
"|" + getCursorString(c, Document.COLUMN_DOCUMENT_ID);
}
- /**
- * @return Model IDs for all known items in the model. Note that this will include items
- * pending deletion.
- */
- public Set<String> getIds() {
- return mPositions.keySet();
- }
-
private void notifyUpdateListeners() {
for (UpdateListener listener: mUpdateListeners) {
listener.onModelUpdate(this);
@@ -102,6 +109,8 @@
if (result == null) {
mCursor = null;
mCursorCount = 0;
+ mIds.clear();
+ mPositions.clear();
info = null;
error = null;
mIsLoading = false;
@@ -117,8 +126,9 @@
mCursor = result.cursor;
mCursorCount = mCursor.getCount();
+ mSortOrder = result.sortOrder;
- updatePositions();
+ updateModelData();
final Bundle extras = mCursor.getExtras();
if (extras != null) {
@@ -136,14 +146,171 @@
}
/**
- * Update the ModelId-position map.
+ * Scan over the incoming cursor data, generate Model IDs for each row, and sort the IDs
+ * according to the current sort order.
*/
- private void updatePositions() {
- mPositions.clear();
+ private void updateModelData() {
+ int[] positions = new int[mCursorCount];
+ mIds.clear();
+ String[] strings = null;
+ long[] longs = null;
+
+ switch (mSortOrder) {
+ case SORT_ORDER_DISPLAY_NAME:
+ strings = new String[mCursorCount];
+ break;
+ case SORT_ORDER_LAST_MODIFIED:
+ case SORT_ORDER_SIZE:
+ longs = new long[mCursorCount];
+ break;
+ }
+
mCursor.moveToPosition(-1);
for (int pos = 0; pos < mCursorCount; ++pos) {
mCursor.moveToNext();
- mPositions.put(Model.createId(mCursor), pos);
+ positions[pos] = pos;
+ mIds.add(createModelId(mCursor));
+
+ switch(mSortOrder) {
+ case SORT_ORDER_DISPLAY_NAME:
+ final String mimeType = getCursorString(mCursor, Document.COLUMN_MIME_TYPE);
+ final String displayName = getCursorString(
+ mCursor, Document.COLUMN_DISPLAY_NAME);
+ if (Document.MIME_TYPE_DIR.equals(mimeType)) {
+ strings[pos] = DocumentInfo.DIR_PREFIX + displayName;
+ } else {
+ strings[pos] = displayName;
+ }
+ break;
+ case SORT_ORDER_LAST_MODIFIED:
+ longs[pos] = getCursorLong(mCursor, Document.COLUMN_LAST_MODIFIED);
+ break;
+ case SORT_ORDER_SIZE:
+ longs[pos] = getCursorLong(mCursor, Document.COLUMN_SIZE);
+ break;
+ }
+ }
+
+ switch (mSortOrder) {
+ case SORT_ORDER_DISPLAY_NAME:
+ binarySort(positions, strings, mIds);
+ break;
+ case SORT_ORDER_LAST_MODIFIED:
+ case SORT_ORDER_SIZE:
+ binarySort(positions, longs, mIds);
+ break;
+ }
+
+ // Populate the positions.
+ mPositions.clear();
+ for (int i = 0; i < mCursorCount; ++i) {
+ mPositions.put(mIds.get(i), positions[i]);
+ }
+ }
+
+ /**
+ * Borrowed from TimSort.binarySort(), but modified to sort three-column data set.
+ */
+ private static void binarySort(int[] positions, String[] strings, List<String> ids) {
+ final int count = positions.length;
+ for (int start = 1; start < count; start++) {
+ final int pivotPosition = positions[start];
+ final String pivotValue = strings[start];
+ final String pivotId = ids.get(start);
+
+ int left = 0;
+ int right = start;
+
+ while (left < right) {
+ int mid = (left + right) >>> 1;
+
+ final String lhs = pivotValue;
+ final String rhs = strings[mid];
+ final int compare = DocumentInfo.compareToIgnoreCaseNullable(lhs, rhs);
+
+ if (compare < 0) {
+ right = mid;
+ } else {
+ left = mid + 1;
+ }
+ }
+
+ int n = start - left;
+ switch (n) {
+ case 2:
+ positions[left + 2] = positions[left + 1];
+ strings[left + 2] = strings[left + 1];
+ ids.set(left + 2, ids.get(left + 1));
+ case 1:
+ positions[left + 1] = positions[left];
+ strings[left + 1] = strings[left];
+ ids.set(left + 1, ids.get(left));
+ break;
+ default:
+ System.arraycopy(positions, left, positions, left + 1, n);
+ System.arraycopy(strings, left, strings, left + 1, n);
+ for (int i = n; i >= 1; --i) {
+ ids.set(left + i, ids.get(left + i - 1));
+ }
+ }
+
+ positions[left] = pivotPosition;
+ strings[left] = pivotValue;
+ ids.set(left, pivotId);
+ }
+ }
+
+ /**
+ * Borrowed from TimSort.binarySort(), but modified to sort three-column data set.
+ */
+ private static void binarySort(int[] positions, long[] longs, List<String> ids) {
+ final int count = positions.length;
+ for (int start = 1; start < count; start++) {
+ final int pivotPosition = positions[start];
+ final long pivotValue = longs[start];
+ final String pivotId = ids.get(start);
+
+ int left = 0;
+ int right = start;
+
+ while (left < right) {
+ int mid = (left + right) >>> 1;
+
+ final long lhs = pivotValue;
+ final long rhs = longs[mid];
+ // Sort in descending numerical order. This matches legacy behaviour, which yields
+ // largest or most recent items on top.
+ final int compare = -Long.compare(lhs, rhs);
+
+ if (compare < 0) {
+ right = mid;
+ } else {
+ left = mid + 1;
+ }
+ }
+
+ int n = start - left;
+ switch (n) {
+ case 2:
+ positions[left + 2] = positions[left + 1];
+ longs[left + 2] = longs[left + 1];
+ ids.set(left + 2, ids.get(left + 1));
+ case 1:
+ positions[left + 1] = positions[left];
+ longs[left + 1] = longs[left];
+ ids.set(left + 1, ids.get(left));
+ break;
+ default:
+ System.arraycopy(positions, left, positions, left + 1, n);
+ System.arraycopy(longs, left, longs, left + 1, n);
+ for (int i = n; i >= 1; --i) {
+ ids.set(left + i, ids.get(left + i - 1));
+ }
+ }
+
+ positions[left] = pivotPosition;
+ longs[left] = pivotValue;
+ ids.set(left, pivotId);
}
}
@@ -279,4 +446,12 @@
*/
void onModelUpdateFailed(Exception e);
}
+
+ /**
+ * @return An ordered array of model IDs representing the documents in the model. It is sorted
+ * according to the current sort order, which was set by the last model update.
+ */
+ public List<String> getModelIds() {
+ return mIds;
+ }
}