1/* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.documentsui; 18 19import static com.android.documentsui.BaseActivity.State.SORT_ORDER_DISPLAY_NAME; 20import static com.android.documentsui.BaseActivity.State.SORT_ORDER_LAST_MODIFIED; 21import static com.android.documentsui.BaseActivity.State.SORT_ORDER_SIZE; 22import static com.android.documentsui.model.DocumentInfo.getCursorLong; 23import static com.android.documentsui.model.DocumentInfo.getCursorString; 24 25import android.database.AbstractCursor; 26import android.database.Cursor; 27import android.os.Bundle; 28import android.provider.DocumentsContract.Document; 29 30import com.android.documentsui.model.DocumentInfo; 31 32/** 33 * Cursor wrapper that presents a sorted view of the underlying cursor. Handles 34 * common {@link Document} sorting modes, such as ordering directories first. 35 */ 36public class SortingCursorWrapper extends AbstractCursor { 37 private final Cursor mCursor; 38 39 private final int[] mPosition; 40 private final String[] mValueString; 41 private final long[] mValueLong; 42 43 public SortingCursorWrapper(Cursor cursor, int sortOrder) { 44 mCursor = cursor; 45 46 final int count = cursor.getCount(); 47 mPosition = new int[count]; 48 switch (sortOrder) { 49 case SORT_ORDER_DISPLAY_NAME: 50 mValueString = new String[count]; 51 mValueLong = null; 52 break; 53 case SORT_ORDER_LAST_MODIFIED: 54 case SORT_ORDER_SIZE: 55 mValueString = null; 56 mValueLong = new long[count]; 57 break; 58 default: 59 throw new IllegalArgumentException(); 60 } 61 62 cursor.moveToPosition(-1); 63 for (int i = 0; i < count; i++) { 64 cursor.moveToNext(); 65 mPosition[i] = i; 66 67 switch (sortOrder) { 68 case SORT_ORDER_DISPLAY_NAME: 69 final String mimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE); 70 final String displayName = getCursorString( 71 cursor, Document.COLUMN_DISPLAY_NAME); 72 if (Document.MIME_TYPE_DIR.equals(mimeType)) { 73 mValueString[i] = DocumentInfo.DIR_PREFIX + displayName; 74 } else { 75 mValueString[i] = displayName; 76 } 77 break; 78 case SORT_ORDER_LAST_MODIFIED: 79 mValueLong[i] = getCursorLong(cursor, Document.COLUMN_LAST_MODIFIED); 80 break; 81 case SORT_ORDER_SIZE: 82 mValueLong[i] = getCursorLong(cursor, Document.COLUMN_SIZE); 83 break; 84 } 85 } 86 87 switch (sortOrder) { 88 case SORT_ORDER_DISPLAY_NAME: 89 synchronized (SortingCursorWrapper.class) { 90 91 binarySort(mPosition, mValueString); 92 } 93 break; 94 case SORT_ORDER_LAST_MODIFIED: 95 case SORT_ORDER_SIZE: 96 binarySort(mPosition, mValueLong); 97 break; 98 } 99 } 100 101 @Override 102 public Bundle getExtras() { 103 return mCursor.getExtras(); 104 } 105 106 @Override 107 public void close() { 108 super.close(); 109 mCursor.close(); 110 } 111 112 @Override 113 public boolean onMove(int oldPosition, int newPosition) { 114 return mCursor.moveToPosition(mPosition[newPosition]); 115 } 116 117 @Override 118 public String[] getColumnNames() { 119 return mCursor.getColumnNames(); 120 } 121 122 @Override 123 public int getCount() { 124 return mCursor.getCount(); 125 } 126 127 @Override 128 public double getDouble(int column) { 129 return mCursor.getDouble(column); 130 } 131 132 @Override 133 public float getFloat(int column) { 134 return mCursor.getFloat(column); 135 } 136 137 @Override 138 public int getInt(int column) { 139 return mCursor.getInt(column); 140 } 141 142 @Override 143 public long getLong(int column) { 144 return mCursor.getLong(column); 145 } 146 147 @Override 148 public short getShort(int column) { 149 return mCursor.getShort(column); 150 } 151 152 @Override 153 public String getString(int column) { 154 return mCursor.getString(column); 155 } 156 157 @Override 158 public int getType(int column) { 159 return mCursor.getType(column); 160 } 161 162 @Override 163 public boolean isNull(int column) { 164 return mCursor.isNull(column); 165 } 166 167 /** 168 * Borrowed from TimSort.binarySort(), but modified to sort two column 169 * dataset. 170 */ 171 private static void binarySort(int[] position, String[] value) { 172 final int count = position.length; 173 for (int start = 1; start < count; start++) { 174 final int pivotPosition = position[start]; 175 final String pivotValue = value[start]; 176 177 int left = 0; 178 int right = start; 179 180 while (left < right) { 181 int mid = (left + right) >>> 1; 182 183 final String lhs = pivotValue; 184 final String rhs = value[mid]; 185 final int compare = DocumentInfo.compareToIgnoreCaseNullable(lhs, rhs); 186 187 if (compare < 0) { 188 right = mid; 189 } else { 190 left = mid + 1; 191 } 192 } 193 194 int n = start - left; 195 switch (n) { 196 case 2: 197 position[left + 2] = position[left + 1]; 198 value[left + 2] = value[left + 1]; 199 case 1: 200 position[left + 1] = position[left]; 201 value[left + 1] = value[left]; 202 break; 203 default: 204 System.arraycopy(position, left, position, left + 1, n); 205 System.arraycopy(value, left, value, left + 1, n); 206 } 207 208 position[left] = pivotPosition; 209 value[left] = pivotValue; 210 } 211 } 212 213 /** 214 * Borrowed from TimSort.binarySort(), but modified to sort two column 215 * dataset. 216 */ 217 private static void binarySort(int[] position, long[] value) { 218 final int count = position.length; 219 for (int start = 1; start < count; start++) { 220 final int pivotPosition = position[start]; 221 final long pivotValue = value[start]; 222 223 int left = 0; 224 int right = start; 225 226 while (left < right) { 227 int mid = (left + right) >>> 1; 228 229 final long lhs = pivotValue; 230 final long rhs = value[mid]; 231 final int compare = Long.compare(lhs, rhs); 232 if (compare > 0) { 233 right = mid; 234 } else { 235 left = mid + 1; 236 } 237 } 238 239 int n = start - left; 240 switch (n) { 241 case 2: 242 position[left + 2] = position[left + 1]; 243 value[left + 2] = value[left + 1]; 244 case 1: 245 position[left + 1] = position[left]; 246 value[left + 1] = value[left]; 247 break; 248 default: 249 System.arraycopy(position, left, position, left + 1, n); 250 System.arraycopy(value, left, value, left + 1, n); 251 } 252 253 position[left] = pivotPosition; 254 value[left] = pivotValue; 255 } 256 } 257} 258