blob: 0b3ecf84900e96f87336b656bee113401137705d [file] [log] [blame]
Jeff Sharkeya5defe32013-08-05 17:56:48 -07001/*
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
Jeff Sharkeya4d1f222013-09-07 14:45:03 -070019import static com.android.documentsui.DocumentsActivity.TAG;
20import static com.android.documentsui.DocumentsActivity.State.MODE_UNKNOWN;
Jeff Sharkeyb3620442013-09-01 18:41:04 -070021import 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;
Jeff Sharkeya4d1f222013-09-07 14:45:03 -070024import static com.android.documentsui.DocumentsActivity.State.SORT_ORDER_UNKNOWN;
25import static com.android.documentsui.model.DocumentInfo.getCursorInt;
Jeff Sharkeyb3620442013-09-01 18:41:04 -070026
Jeff Sharkeyac9e6272013-08-31 21:27:44 -070027import android.content.AsyncTaskLoader;
28import android.content.ContentProviderClient;
Jeff Sharkeya4d1f222013-09-07 14:45:03 -070029import android.content.ContentResolver;
Jeff Sharkeya5defe32013-08-05 17:56:48 -070030import android.content.Context;
31import android.database.Cursor;
32import android.net.Uri;
33import android.os.CancellationSignal;
Jeff Sharkeyac9e6272013-08-31 21:27:44 -070034import android.os.OperationCanceledException;
Jeff Sharkey0e8c8712013-09-12 21:59:06 -070035import android.provider.DocumentsContract;
Jeff Sharkeyac9e6272013-08-31 21:27:44 -070036import android.provider.DocumentsContract.Document;
Jeff Sharkeya4d1f222013-09-07 14:45:03 -070037import 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;
Jeff Sharkeya5defe32013-08-05 17:56:48 -070043
Jeff Sharkeya5defe32013-08-05 17:56:48 -070044import libcore.io.IoUtils;
45
Jeff Sharkey0e8c8712013-09-12 21:59:06 -070046import java.io.FileNotFoundException;
47
Jeff Sharkey46899c82013-08-18 22:26:48 -070048class DirectoryResult implements AutoCloseable {
Jeff Sharkeyac9e6272013-08-31 21:27:44 -070049 ContentProviderClient client;
Jeff Sharkey46899c82013-08-18 22:26:48 -070050 Cursor cursor;
Jeff Sharkeyac9e6272013-08-31 21:27:44 -070051 Exception exception;
Jeff Sharkey46899c82013-08-18 22:26:48 -070052
Jeff Sharkeya4d1f222013-09-07 14:45:03 -070053 int mode = MODE_UNKNOWN;
54 int sortOrder = SORT_ORDER_UNKNOWN;
55
Jeff Sharkey46899c82013-08-18 22:26:48 -070056 @Override
Jeff Sharkeyac9e6272013-08-31 21:27:44 -070057 public void close() {
Jeff Sharkey46899c82013-08-18 22:26:48 -070058 IoUtils.closeQuietly(cursor);
Jeff Sharkeyac9e6272013-08-31 21:27:44 -070059 ContentProviderClient.closeQuietly(client);
60 cursor = null;
61 client = null;
Jeff Sharkey46899c82013-08-18 22:26:48 -070062 }
63}
64
Jeff Sharkeyac9e6272013-08-31 21:27:44 -070065public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> {
Jeff Sharkey9dd02622013-09-27 16:44:11 -070066
67 private static final String[] SEARCH_REJECT_MIMES = new String[] { Document.MIME_TYPE_DIR };
68
Jeff Sharkeyac9e6272013-08-31 21:27:44 -070069 private final ForceLoadContentObserver mObserver = new ForceLoadContentObserver();
Jeff Sharkeya5defe32013-08-05 17:56:48 -070070
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -070071 private final int mType;
Jeff Sharkeya4d1f222013-09-07 14:45:03 -070072 private final RootInfo mRoot;
Jeff Sharkey0e8c8712013-09-12 21:59:06 -070073 private DocumentInfo mDoc;
Jeff Sharkeyac9e6272013-08-31 21:27:44 -070074 private final Uri mUri;
Jeff Sharkeyd10f0492013-09-09 17:35:46 -070075 private final int mUserSortOrder;
Jeff Sharkeya5defe32013-08-05 17:56:48 -070076
Jeff Sharkeyac9e6272013-08-31 21:27:44 -070077 private CancellationSignal mSignal;
78 private DirectoryResult mResult;
79
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -070080 public DirectoryLoader(Context context, int type, RootInfo root, DocumentInfo doc, Uri uri,
81 int userSortOrder) {
Jeff Sharkeyac9e6272013-08-31 21:27:44 -070082 super(context);
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -070083 mType = type;
Jeff Sharkeya4d1f222013-09-07 14:45:03 -070084 mRoot = root;
85 mDoc = doc;
Jeff Sharkeyac9e6272013-08-31 21:27:44 -070086 mUri = uri;
Jeff Sharkeyd10f0492013-09-09 17:35:46 -070087 mUserSortOrder = userSortOrder;
Jeff Sharkeya5defe32013-08-05 17:56:48 -070088 }
89
90 @Override
Jeff Sharkeyac9e6272013-08-31 21:27:44 -070091 public final DirectoryResult loadInBackground() {
92 synchronized (this) {
93 if (isLoadInBackgroundCanceled()) {
94 throw new OperationCanceledException();
95 }
96 mSignal = new CancellationSignal();
97 }
Jeff Sharkeya4d1f222013-09-07 14:45:03 -070098
99 final ContentResolver resolver = getContext().getContentResolver();
Jeff Sharkeyd82b26b2013-09-02 15:07:28 -0700100 final String authority = mUri.getAuthority();
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700101
102 final DirectoryResult result = new DirectoryResult();
103
104 int userMode = State.MODE_UNKNOWN;
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700105
Jeff Sharkey0e8c8712013-09-12 21:59:06 -0700106 // Use default document when searching
107 if (mType == DirectoryFragment.TYPE_SEARCH) {
108 final Uri docUri = DocumentsContract.buildDocumentUri(
109 mRoot.authority, mRoot.documentId);
110 try {
111 mDoc = DocumentInfo.fromUri(resolver, docUri);
112 } catch (FileNotFoundException e) {
113 Log.w(TAG, "Failed to query", e);
114 result.exception = e;
115 return result;
116 }
117 }
118
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700119 // Pick up any custom modes requested by user
120 Cursor cursor = null;
Jeff Sharkey1d890e02013-08-15 11:24:03 -0700121 try {
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700122 final Uri stateUri = RecentsProvider.buildState(
123 mRoot.authority, mRoot.rootId, mDoc.documentId);
124 cursor = resolver.query(stateUri, null, null, null, null);
125 if (cursor.moveToFirst()) {
126 userMode = getCursorInt(cursor, StateColumns.MODE);
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700127 }
128 } finally {
129 IoUtils.closeQuietly(cursor);
130 }
131
132 if (userMode != State.MODE_UNKNOWN) {
133 result.mode = userMode;
134 } else {
135 if ((mDoc.flags & Document.FLAG_DIR_PREFERS_GRID) != 0) {
136 result.mode = State.MODE_GRID;
137 } else {
138 result.mode = State.MODE_LIST;
139 }
140 }
141
Jeff Sharkeyd10f0492013-09-09 17:35:46 -0700142 if (mUserSortOrder != State.SORT_ORDER_UNKNOWN) {
143 result.sortOrder = mUserSortOrder;
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700144 } else {
145 if ((mDoc.flags & Document.FLAG_DIR_PREFERS_LAST_MODIFIED) != 0) {
146 result.sortOrder = State.SORT_ORDER_LAST_MODIFIED;
147 } else {
148 result.sortOrder = State.SORT_ORDER_DISPLAY_NAME;
149 }
150 }
151
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700152 // Search always uses ranking from provider
153 if (mType == DirectoryFragment.TYPE_SEARCH) {
154 result.sortOrder = State.SORT_ORDER_UNKNOWN;
155 }
156
Jeff Sharkeyd10f0492013-09-09 17:35:46 -0700157 Log.d(TAG, "userMode=" + userMode + ", userSortOrder=" + mUserSortOrder + " --> mode="
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700158 + result.mode + ", sortOrder=" + result.sortOrder);
159
160 try {
161 result.client = resolver.acquireUnstableContentProviderClient(authority);
162 cursor = result.client.query(
163 mUri, null, null, null, getQuerySortOrder(result.sortOrder), mSignal);
Jeff Sharkey20b32272013-09-03 15:25:52 -0700164 cursor.registerContentObserver(mObserver);
165
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700166 cursor = new RootCursorWrapper(mUri.getAuthority(), mRoot.rootId, cursor, -1);
Jeff Sharkeyd82b26b2013-09-02 15:07:28 -0700167
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700168 if (mType == DirectoryFragment.TYPE_SEARCH) {
169 // Filter directories out of search results, for now
Jeff Sharkey9dd02622013-09-27 16:44:11 -0700170 cursor = new FilteringCursorWrapper(cursor, null, SEARCH_REJECT_MIMES);
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700171 } else {
172 // Normal directories should have sorting applied
173 cursor = new SortingCursorWrapper(cursor, result.sortOrder);
174 }
175
176 result.cursor = cursor;
Jeff Sharkey1d890e02013-08-15 11:24:03 -0700177 } catch (Exception e) {
Jeff Sharkey0e8c8712013-09-12 21:59:06 -0700178 Log.w(TAG, "Failed to query", e);
Jeff Sharkeyac9e6272013-08-31 21:27:44 -0700179 result.exception = e;
180 ContentProviderClient.closeQuietly(result.client);
181 } finally {
182 synchronized (this) {
183 mSignal = null;
184 }
Jeff Sharkey1d890e02013-08-15 11:24:03 -0700185 }
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700186
Jeff Sharkey46899c82013-08-18 22:26:48 -0700187 return result;
Jeff Sharkey1d890e02013-08-15 11:24:03 -0700188 }
189
Jeff Sharkeyac9e6272013-08-31 21:27:44 -0700190 @Override
191 public void cancelLoadInBackground() {
192 super.cancelLoadInBackground();
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700193
Jeff Sharkeyac9e6272013-08-31 21:27:44 -0700194 synchronized (this) {
195 if (mSignal != null) {
196 mSignal.cancel();
Jeff Sharkey46899c82013-08-18 22:26:48 -0700197 }
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700198 }
Jeff Sharkeyac9e6272013-08-31 21:27:44 -0700199 }
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700200
Jeff Sharkeyac9e6272013-08-31 21:27:44 -0700201 @Override
202 public void deliverResult(DirectoryResult result) {
203 if (isReset()) {
204 IoUtils.closeQuietly(result);
205 return;
206 }
207 DirectoryResult oldResult = mResult;
208 mResult = result;
209
210 if (isStarted()) {
211 super.deliverResult(result);
212 }
213
214 if (oldResult != null && oldResult != result) {
215 IoUtils.closeQuietly(oldResult);
216 }
217 }
218
219 @Override
220 protected void onStartLoading() {
221 if (mResult != null) {
222 deliverResult(mResult);
223 }
224 if (takeContentChanged() || mResult == null) {
225 forceLoad();
226 }
227 }
228
229 @Override
230 protected void onStopLoading() {
231 cancelLoad();
232 }
233
234 @Override
235 public void onCanceled(DirectoryResult result) {
236 IoUtils.closeQuietly(result);
237 }
238
239 @Override
240 protected void onReset() {
241 super.onReset();
242
243 // Ensure the loader is stopped
244 onStopLoading();
245
246 IoUtils.closeQuietly(mResult);
247 mResult = null;
248
249 getContext().getContentResolver().unregisterContentObserver(mObserver);
250 }
251
Jeff Sharkeyd82b26b2013-09-02 15:07:28 -0700252 public static String getQuerySortOrder(int sortOrder) {
253 switch (sortOrder) {
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700254 case SORT_ORDER_DISPLAY_NAME:
Jeff Sharkeyac9e6272013-08-31 21:27:44 -0700255 return Document.COLUMN_DISPLAY_NAME + " ASC";
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700256 case SORT_ORDER_LAST_MODIFIED:
Jeff Sharkeyac9e6272013-08-31 21:27:44 -0700257 return Document.COLUMN_LAST_MODIFIED + " DESC";
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700258 case SORT_ORDER_SIZE:
Jeff Sharkeyac9e6272013-08-31 21:27:44 -0700259 return Document.COLUMN_SIZE + " DESC";
260 default:
261 return null;
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700262 }
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700263 }
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700264}