blob: a8a61d28c22246c1b7c1801ec870753f35a80a69 [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;
Steve McKayd0a2a2c2015-03-25 14:35:33 -070020import static com.android.documentsui.BaseActivity.State.MODE_UNKNOWN;
21import static com.android.documentsui.BaseActivity.State.SORT_ORDER_DISPLAY_NAME;
22import static com.android.documentsui.BaseActivity.State.SORT_ORDER_LAST_MODIFIED;
23import static com.android.documentsui.BaseActivity.State.SORT_ORDER_SIZE;
24import static com.android.documentsui.BaseActivity.State.SORT_ORDER_UNKNOWN;
Jeff Sharkeya4d1f222013-09-07 14:45:03 -070025import 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;
Makoto Onuki77778752015-07-01 14:55:14 -070034import android.os.Handler;
35import android.os.Looper;
Jeff Sharkeyac9e6272013-08-31 21:27:44 -070036import android.os.OperationCanceledException;
Makoto Onuki77778752015-07-01 14:55:14 -070037import android.os.RemoteException;
Jeff Sharkey0e8c8712013-09-12 21:59:06 -070038import android.provider.DocumentsContract;
Jeff Sharkeyac9e6272013-08-31 21:27:44 -070039import android.provider.DocumentsContract.Document;
Jeff Sharkeya4d1f222013-09-07 14:45:03 -070040import android.util.Log;
41
Steve McKayd0a2a2c2015-03-25 14:35:33 -070042import com.android.documentsui.BaseActivity.State;
Jeff Sharkeya4d1f222013-09-07 14:45:03 -070043import com.android.documentsui.RecentsProvider.StateColumns;
44import com.android.documentsui.model.DocumentInfo;
45import com.android.documentsui.model.RootInfo;
Jeff Sharkeya5defe32013-08-05 17:56:48 -070046
Jeff Sharkeya5defe32013-08-05 17:56:48 -070047import libcore.io.IoUtils;
48
Jeff Sharkey0e8c8712013-09-12 21:59:06 -070049import java.io.FileNotFoundException;
50
Jeff Sharkey46899c82013-08-18 22:26:48 -070051class DirectoryResult implements AutoCloseable {
Jeff Sharkeyac9e6272013-08-31 21:27:44 -070052 ContentProviderClient client;
Jeff Sharkey46899c82013-08-18 22:26:48 -070053 Cursor cursor;
Jeff Sharkeyac9e6272013-08-31 21:27:44 -070054 Exception exception;
Jeff Sharkey46899c82013-08-18 22:26:48 -070055
Jeff Sharkeya4d1f222013-09-07 14:45:03 -070056 int mode = MODE_UNKNOWN;
57 int sortOrder = SORT_ORDER_UNKNOWN;
58
Jeff Sharkey46899c82013-08-18 22:26:48 -070059 @Override
Jeff Sharkeyac9e6272013-08-31 21:27:44 -070060 public void close() {
Jeff Sharkey46899c82013-08-18 22:26:48 -070061 IoUtils.closeQuietly(cursor);
Jeff Sharkey3fd11772013-09-30 14:26:27 -070062 ContentProviderClient.releaseQuietly(client);
Jeff Sharkeyac9e6272013-08-31 21:27:44 -070063 cursor = null;
64 client = null;
Jeff Sharkey46899c82013-08-18 22:26:48 -070065 }
66}
67
Jeff Sharkeyac9e6272013-08-31 21:27:44 -070068public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> {
Jeff Sharkey9dd02622013-09-27 16:44:11 -070069
70 private static final String[] SEARCH_REJECT_MIMES = new String[] { Document.MIME_TYPE_DIR };
71
Jeff Sharkeyac9e6272013-08-31 21:27:44 -070072 private final ForceLoadContentObserver mObserver = new ForceLoadContentObserver();
Jeff Sharkeya5defe32013-08-05 17:56:48 -070073
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -070074 private final int mType;
Jeff Sharkeya4d1f222013-09-07 14:45:03 -070075 private final RootInfo mRoot;
Jeff Sharkey0e8c8712013-09-12 21:59:06 -070076 private DocumentInfo mDoc;
Jeff Sharkeyac9e6272013-08-31 21:27:44 -070077 private final Uri mUri;
Jeff Sharkeyd10f0492013-09-09 17:35:46 -070078 private final int mUserSortOrder;
Jeff Sharkeya5defe32013-08-05 17:56:48 -070079
Jeff Sharkeyac9e6272013-08-31 21:27:44 -070080 private CancellationSignal mSignal;
81 private DirectoryResult mResult;
82
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -070083 public DirectoryLoader(Context context, int type, RootInfo root, DocumentInfo doc, Uri uri,
84 int userSortOrder) {
Jeff Sharkeyf63b7772013-10-01 17:57:41 -070085 super(context, ProviderExecutor.forAuthority(root.authority));
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -070086 mType = type;
Jeff Sharkeya4d1f222013-09-07 14:45:03 -070087 mRoot = root;
88 mDoc = doc;
Jeff Sharkeyac9e6272013-08-31 21:27:44 -070089 mUri = uri;
Jeff Sharkeyd10f0492013-09-09 17:35:46 -070090 mUserSortOrder = userSortOrder;
Jeff Sharkeya5defe32013-08-05 17:56:48 -070091 }
92
93 @Override
Jeff Sharkeyac9e6272013-08-31 21:27:44 -070094 public final DirectoryResult loadInBackground() {
95 synchronized (this) {
96 if (isLoadInBackgroundCanceled()) {
97 throw new OperationCanceledException();
98 }
99 mSignal = new CancellationSignal();
100 }
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700101
102 final ContentResolver resolver = getContext().getContentResolver();
Jeff Sharkeyd82b26b2013-09-02 15:07:28 -0700103 final String authority = mUri.getAuthority();
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700104
105 final DirectoryResult result = new DirectoryResult();
106
107 int userMode = State.MODE_UNKNOWN;
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700108
Jeff Sharkey0e8c8712013-09-12 21:59:06 -0700109 // Use default document when searching
110 if (mType == DirectoryFragment.TYPE_SEARCH) {
111 final Uri docUri = DocumentsContract.buildDocumentUri(
112 mRoot.authority, mRoot.documentId);
113 try {
114 mDoc = DocumentInfo.fromUri(resolver, docUri);
115 } catch (FileNotFoundException e) {
116 Log.w(TAG, "Failed to query", e);
117 result.exception = e;
118 return result;
119 }
120 }
121
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700122 // Pick up any custom modes requested by user
123 Cursor cursor = null;
Jeff Sharkey1d890e02013-08-15 11:24:03 -0700124 try {
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700125 final Uri stateUri = RecentsProvider.buildState(
126 mRoot.authority, mRoot.rootId, mDoc.documentId);
127 cursor = resolver.query(stateUri, null, null, null, null);
128 if (cursor.moveToFirst()) {
129 userMode = getCursorInt(cursor, StateColumns.MODE);
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700130 }
131 } finally {
132 IoUtils.closeQuietly(cursor);
133 }
134
135 if (userMode != State.MODE_UNKNOWN) {
136 result.mode = userMode;
137 } else {
138 if ((mDoc.flags & Document.FLAG_DIR_PREFERS_GRID) != 0) {
139 result.mode = State.MODE_GRID;
140 } else {
141 result.mode = State.MODE_LIST;
142 }
143 }
144
Jeff Sharkeyd10f0492013-09-09 17:35:46 -0700145 if (mUserSortOrder != State.SORT_ORDER_UNKNOWN) {
146 result.sortOrder = mUserSortOrder;
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700147 } else {
148 if ((mDoc.flags & Document.FLAG_DIR_PREFERS_LAST_MODIFIED) != 0) {
149 result.sortOrder = State.SORT_ORDER_LAST_MODIFIED;
150 } else {
151 result.sortOrder = State.SORT_ORDER_DISPLAY_NAME;
152 }
153 }
154
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700155 // Search always uses ranking from provider
156 if (mType == DirectoryFragment.TYPE_SEARCH) {
157 result.sortOrder = State.SORT_ORDER_UNKNOWN;
158 }
159
Jeff Sharkeyd10f0492013-09-09 17:35:46 -0700160 Log.d(TAG, "userMode=" + userMode + ", userSortOrder=" + mUserSortOrder + " --> mode="
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700161 + result.mode + ", sortOrder=" + result.sortOrder);
162
Jeff Sharkeyf63b7772013-10-01 17:57:41 -0700163 ContentProviderClient client = null;
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700164 try {
Jeff Sharkeyf63b7772013-10-01 17:57:41 -0700165 client = DocumentsApplication.acquireUnstableProviderOrThrow(resolver, authority);
Jeff Sharkey3fd11772013-09-30 14:26:27 -0700166
Jeff Sharkeyf63b7772013-10-01 17:57:41 -0700167 cursor = client.query(
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700168 mUri, null, null, null, getQuerySortOrder(result.sortOrder), mSignal);
Makoto Onuki77778752015-07-01 14:55:14 -0700169 if (cursor == null) {
170 throw new RemoteException("Provider returned null");
171 }
172
Jeff Sharkey20b32272013-09-03 15:25:52 -0700173 cursor.registerContentObserver(mObserver);
174
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700175 cursor = new RootCursorWrapper(mUri.getAuthority(), mRoot.rootId, cursor, -1);
Jeff Sharkeyd82b26b2013-09-02 15:07:28 -0700176
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700177 if (mType == DirectoryFragment.TYPE_SEARCH) {
178 // Filter directories out of search results, for now
Jeff Sharkey9dd02622013-09-27 16:44:11 -0700179 cursor = new FilteringCursorWrapper(cursor, null, SEARCH_REJECT_MIMES);
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700180 } else {
181 // Normal directories should have sorting applied
182 cursor = new SortingCursorWrapper(cursor, result.sortOrder);
183 }
184
Jeff Sharkeyf63b7772013-10-01 17:57:41 -0700185 result.client = client;
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700186 result.cursor = cursor;
Jeff Sharkey1d890e02013-08-15 11:24:03 -0700187 } catch (Exception e) {
Jeff Sharkey0e8c8712013-09-12 21:59:06 -0700188 Log.w(TAG, "Failed to query", e);
Jeff Sharkeyac9e6272013-08-31 21:27:44 -0700189 result.exception = e;
Jeff Sharkeyf63b7772013-10-01 17:57:41 -0700190 ContentProviderClient.releaseQuietly(client);
Jeff Sharkeyac9e6272013-08-31 21:27:44 -0700191 } finally {
192 synchronized (this) {
193 mSignal = null;
194 }
Jeff Sharkey1d890e02013-08-15 11:24:03 -0700195 }
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700196
Jeff Sharkey46899c82013-08-18 22:26:48 -0700197 return result;
Jeff Sharkey1d890e02013-08-15 11:24:03 -0700198 }
199
Jeff Sharkeyac9e6272013-08-31 21:27:44 -0700200 @Override
201 public void cancelLoadInBackground() {
202 super.cancelLoadInBackground();
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700203
Jeff Sharkeyac9e6272013-08-31 21:27:44 -0700204 synchronized (this) {
205 if (mSignal != null) {
206 mSignal.cancel();
Jeff Sharkey46899c82013-08-18 22:26:48 -0700207 }
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700208 }
Jeff Sharkeyac9e6272013-08-31 21:27:44 -0700209 }
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700210
Jeff Sharkeyac9e6272013-08-31 21:27:44 -0700211 @Override
212 public void deliverResult(DirectoryResult result) {
213 if (isReset()) {
214 IoUtils.closeQuietly(result);
215 return;
216 }
217 DirectoryResult oldResult = mResult;
218 mResult = result;
219
220 if (isStarted()) {
221 super.deliverResult(result);
222 }
223
224 if (oldResult != null && oldResult != result) {
225 IoUtils.closeQuietly(oldResult);
226 }
227 }
228
229 @Override
230 protected void onStartLoading() {
231 if (mResult != null) {
232 deliverResult(mResult);
233 }
234 if (takeContentChanged() || mResult == null) {
235 forceLoad();
236 }
237 }
238
239 @Override
240 protected void onStopLoading() {
241 cancelLoad();
242 }
243
244 @Override
245 public void onCanceled(DirectoryResult result) {
246 IoUtils.closeQuietly(result);
247 }
248
249 @Override
250 protected void onReset() {
251 super.onReset();
252
253 // Ensure the loader is stopped
254 onStopLoading();
255
256 IoUtils.closeQuietly(mResult);
257 mResult = null;
258
259 getContext().getContentResolver().unregisterContentObserver(mObserver);
260 }
261
Jeff Sharkeyd82b26b2013-09-02 15:07:28 -0700262 public static String getQuerySortOrder(int sortOrder) {
263 switch (sortOrder) {
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700264 case SORT_ORDER_DISPLAY_NAME:
Jeff Sharkeyac9e6272013-08-31 21:27:44 -0700265 return Document.COLUMN_DISPLAY_NAME + " ASC";
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700266 case SORT_ORDER_LAST_MODIFIED:
Jeff Sharkeyac9e6272013-08-31 21:27:44 -0700267 return Document.COLUMN_LAST_MODIFIED + " DESC";
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700268 case SORT_ORDER_SIZE:
Jeff Sharkeyac9e6272013-08-31 21:27:44 -0700269 return Document.COLUMN_SIZE + " DESC";
270 default:
271 return null;
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700272 }
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700273 }
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700274}