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