/* * 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.BaseActivity.State.SORT_ORDER_DISPLAY_NAME; import static com.android.documentsui.BaseActivity.State.SORT_ORDER_LAST_MODIFIED; import static com.android.documentsui.BaseActivity.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; } } }