DirectoryLoader.java revision 3e1189b3590aefb65a2af720ae2ba959bbd4188d
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.DocumentsActivity.TAG; 20import static com.android.documentsui.DocumentsActivity.State.MODE_UNKNOWN; 21import static com.android.documentsui.DocumentsActivity.State.SORT_ORDER_DISPLAY_NAME; 22import static com.android.documentsui.DocumentsActivity.State.SORT_ORDER_LAST_MODIFIED; 23import static com.android.documentsui.DocumentsActivity.State.SORT_ORDER_SIZE; 24import static com.android.documentsui.DocumentsActivity.State.SORT_ORDER_UNKNOWN; 25import static com.android.documentsui.model.DocumentInfo.getCursorInt; 26 27import android.content.AsyncTaskLoader; 28import android.content.ContentProviderClient; 29import android.content.ContentResolver; 30import android.content.Context; 31import android.database.Cursor; 32import android.net.Uri; 33import android.os.CancellationSignal; 34import android.os.OperationCanceledException; 35import android.provider.DocumentsContract; 36import android.provider.DocumentsContract.Document; 37import android.util.Log; 38 39import com.android.documentsui.DocumentsActivity.State; 40import com.android.documentsui.RecentsProvider.StateColumns; 41import com.android.documentsui.model.DocumentInfo; 42import com.android.documentsui.model.RootInfo; 43 44import libcore.io.IoUtils; 45 46import java.io.FileNotFoundException; 47 48class DirectoryResult implements AutoCloseable { 49 ContentProviderClient client; 50 Cursor cursor; 51 Exception exception; 52 53 int mode = MODE_UNKNOWN; 54 int sortOrder = SORT_ORDER_UNKNOWN; 55 56 @Override 57 public void close() { 58 IoUtils.closeQuietly(cursor); 59 ContentProviderClient.closeQuietly(client); 60 cursor = null; 61 client = null; 62 } 63} 64 65public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> { 66 private final ForceLoadContentObserver mObserver = new ForceLoadContentObserver(); 67 68 private final int mType; 69 private final RootInfo mRoot; 70 private DocumentInfo mDoc; 71 private final Uri mUri; 72 private final int mUserSortOrder; 73 74 private CancellationSignal mSignal; 75 private DirectoryResult mResult; 76 77 public DirectoryLoader(Context context, int type, RootInfo root, DocumentInfo doc, Uri uri, 78 int userSortOrder) { 79 super(context); 80 mType = type; 81 mRoot = root; 82 mDoc = doc; 83 mUri = uri; 84 mUserSortOrder = userSortOrder; 85 } 86 87 @Override 88 public final DirectoryResult loadInBackground() { 89 synchronized (this) { 90 if (isLoadInBackgroundCanceled()) { 91 throw new OperationCanceledException(); 92 } 93 mSignal = new CancellationSignal(); 94 } 95 96 final ContentResolver resolver = getContext().getContentResolver(); 97 final String authority = mUri.getAuthority(); 98 99 final DirectoryResult result = new DirectoryResult(); 100 101 int userMode = State.MODE_UNKNOWN; 102 103 // Use default document when searching 104 if (mType == DirectoryFragment.TYPE_SEARCH) { 105 final Uri docUri = DocumentsContract.buildDocumentUri( 106 mRoot.authority, mRoot.documentId); 107 try { 108 mDoc = DocumentInfo.fromUri(resolver, docUri); 109 } catch (FileNotFoundException e) { 110 Log.w(TAG, "Failed to query", e); 111 result.exception = e; 112 return result; 113 } 114 } 115 116 // Pick up any custom modes requested by user 117 Cursor cursor = null; 118 try { 119 final Uri stateUri = RecentsProvider.buildState( 120 mRoot.authority, mRoot.rootId, mDoc.documentId); 121 cursor = resolver.query(stateUri, null, null, null, null); 122 if (cursor.moveToFirst()) { 123 userMode = getCursorInt(cursor, StateColumns.MODE); 124 } 125 } finally { 126 IoUtils.closeQuietly(cursor); 127 } 128 129 if (userMode != State.MODE_UNKNOWN) { 130 result.mode = userMode; 131 } else { 132 if ((mDoc.flags & Document.FLAG_DIR_PREFERS_GRID) != 0) { 133 result.mode = State.MODE_GRID; 134 } else { 135 result.mode = State.MODE_LIST; 136 } 137 } 138 139 if (mUserSortOrder != State.SORT_ORDER_UNKNOWN) { 140 result.sortOrder = mUserSortOrder; 141 } else { 142 if ((mDoc.flags & Document.FLAG_DIR_PREFERS_LAST_MODIFIED) != 0) { 143 result.sortOrder = State.SORT_ORDER_LAST_MODIFIED; 144 } else { 145 result.sortOrder = State.SORT_ORDER_DISPLAY_NAME; 146 } 147 } 148 149 // Search always uses ranking from provider 150 if (mType == DirectoryFragment.TYPE_SEARCH) { 151 result.sortOrder = State.SORT_ORDER_UNKNOWN; 152 } 153 154 Log.d(TAG, "userMode=" + userMode + ", userSortOrder=" + mUserSortOrder + " --> mode=" 155 + result.mode + ", sortOrder=" + result.sortOrder); 156 157 try { 158 result.client = resolver.acquireUnstableContentProviderClient(authority); 159 cursor = result.client.query( 160 mUri, null, null, null, getQuerySortOrder(result.sortOrder), mSignal); 161 cursor.registerContentObserver(mObserver); 162 163 cursor = new RootCursorWrapper(mUri.getAuthority(), mRoot.rootId, cursor, -1); 164 165 if (mType == DirectoryFragment.TYPE_SEARCH) { 166 // Filter directories out of search results, for now 167 cursor = new FilteringCursorWrapper(cursor, null, new String[] { 168 Document.MIME_TYPE_DIR }); 169 } else { 170 // Normal directories should have sorting applied 171 cursor = new SortingCursorWrapper(cursor, result.sortOrder); 172 } 173 174 result.cursor = cursor; 175 } catch (Exception e) { 176 Log.w(TAG, "Failed to query", e); 177 result.exception = e; 178 ContentProviderClient.closeQuietly(result.client); 179 } finally { 180 synchronized (this) { 181 mSignal = null; 182 } 183 } 184 185 return result; 186 } 187 188 @Override 189 public void cancelLoadInBackground() { 190 super.cancelLoadInBackground(); 191 192 synchronized (this) { 193 if (mSignal != null) { 194 mSignal.cancel(); 195 } 196 } 197 } 198 199 @Override 200 public void deliverResult(DirectoryResult result) { 201 if (isReset()) { 202 IoUtils.closeQuietly(result); 203 return; 204 } 205 DirectoryResult oldResult = mResult; 206 mResult = result; 207 208 if (isStarted()) { 209 super.deliverResult(result); 210 } 211 212 if (oldResult != null && oldResult != result) { 213 IoUtils.closeQuietly(oldResult); 214 } 215 } 216 217 @Override 218 protected void onStartLoading() { 219 if (mResult != null) { 220 deliverResult(mResult); 221 } 222 if (takeContentChanged() || mResult == null) { 223 forceLoad(); 224 } 225 } 226 227 @Override 228 protected void onStopLoading() { 229 cancelLoad(); 230 } 231 232 @Override 233 public void onCanceled(DirectoryResult result) { 234 IoUtils.closeQuietly(result); 235 } 236 237 @Override 238 protected void onReset() { 239 super.onReset(); 240 241 // Ensure the loader is stopped 242 onStopLoading(); 243 244 IoUtils.closeQuietly(mResult); 245 mResult = null; 246 247 getContext().getContentResolver().unregisterContentObserver(mObserver); 248 } 249 250 public static String getQuerySortOrder(int sortOrder) { 251 switch (sortOrder) { 252 case SORT_ORDER_DISPLAY_NAME: 253 return Document.COLUMN_DISPLAY_NAME + " ASC"; 254 case SORT_ORDER_LAST_MODIFIED: 255 return Document.COLUMN_LAST_MODIFIED + " DESC"; 256 case SORT_ORDER_SIZE: 257 return Document.COLUMN_SIZE + " DESC"; 258 default: 259 return null; 260 } 261 } 262} 263