blob: 12abd21448f342764f8454a1d7ec4e33adfc3646 [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 Sharkeyac9e6272013-08-31 21:27:44 -070019import android.content.AsyncTaskLoader;
20import android.content.ContentProviderClient;
Jeff Sharkeya4d1f222013-09-07 14:45:03 -070021import android.content.ContentResolver;
Jeff Sharkeya5defe32013-08-05 17:56:48 -070022import android.content.Context;
Ben Lin9fea3122016-10-10 18:32:26 -070023import android.database.ContentObserver;
Jeff Sharkeya5defe32013-08-05 17:56:48 -070024import android.database.Cursor;
25import android.net.Uri;
Steve McKay50b9bae2017-01-17 11:12:08 -080026import android.os.Bundle;
Jeff Sharkeya5defe32013-08-05 17:56:48 -070027import android.os.CancellationSignal;
Ben Lin9fea3122016-10-10 18:32:26 -070028import android.os.Handler;
Jeff Sharkeyac9e6272013-08-31 21:27:44 -070029import android.os.OperationCanceledException;
Makoto Onuki77778752015-07-01 14:55:14 -070030import android.os.RemoteException;
Jeff Sharkeyac9e6272013-08-31 21:27:44 -070031import android.provider.DocumentsContract.Document;
Jeff Sharkeya4d1f222013-09-07 14:45:03 -070032import android.util.Log;
33
Tomasz Mikolajewskib19061c2017-02-13 17:33:42 +090034import com.android.documentsui.archives.ArchivesProvider;
Steve McKay50b9bae2017-01-17 11:12:08 -080035import com.android.documentsui.base.DebugFlags;
Steve McKayd0805062016-09-15 14:30:38 -070036import com.android.documentsui.base.DocumentInfo;
Steve McKayd9caa6a2016-09-15 16:36:45 -070037import com.android.documentsui.base.FilteringCursorWrapper;
Steve McKayd0805062016-09-15 14:30:38 -070038import com.android.documentsui.base.RootInfo;
Garfield Tan16868832016-09-26 10:01:45 -070039import com.android.documentsui.base.Shared;
Steve McKayd9caa6a2016-09-15 16:36:45 -070040import com.android.documentsui.roots.RootCursorWrapper;
Garfield, Tan11d23482016-08-05 09:33:29 -070041import com.android.documentsui.sorting.SortModel;
Jeff Sharkeya5defe32013-08-05 17:56:48 -070042
Steve McKayc88f83c2016-08-31 12:01:43 -070043import libcore.io.IoUtils;
44
Jeff Sharkeyac9e6272013-08-31 21:27:44 -070045public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> {
Jeff Sharkey9dd02622013-09-27 16:44:11 -070046
Garfield, Tan11d23482016-08-05 09:33:29 -070047 private static final String TAG = "DirectoryLoader";
48
Jeff Sharkey9dd02622013-09-27 16:44:11 -070049 private static final String[] SEARCH_REJECT_MIMES = new String[] { Document.MIME_TYPE_DIR };
50
Ben Lin9fea3122016-10-10 18:32:26 -070051 private final LockingContentObserver mObserver;
Jeff Sharkeya4d1f222013-09-07 14:45:03 -070052 private final RootInfo mRoot;
Jeff Sharkeyac9e6272013-08-31 21:27:44 -070053 private final Uri mUri;
Garfield, Tan11d23482016-08-05 09:33:29 -070054 private final SortModel mModel;
Aga Wronskaaf5ace52016-02-17 13:50:42 -080055 private final boolean mSearchMode;
Jeff Sharkeya5defe32013-08-05 17:56:48 -070056
Steve McKay7776aa52016-01-25 19:00:22 -080057 private DocumentInfo mDoc;
Jeff Sharkeyac9e6272013-08-31 21:27:44 -070058 private CancellationSignal mSignal;
59 private DirectoryResult mResult;
60
Steve McKayc88f83c2016-08-31 12:01:43 -070061 public DirectoryLoader(
62 Context context,
63 RootInfo root,
64 DocumentInfo doc,
65 Uri uri,
66 SortModel model,
Ben Lin9fea3122016-10-10 18:32:26 -070067 DirectoryReloadLock lock,
Steve McKayc88f83c2016-08-31 12:01:43 -070068 boolean inSearchMode) {
69
Jeff Sharkeyf63b7772013-10-01 17:57:41 -070070 super(context, ProviderExecutor.forAuthority(root.authority));
Jeff Sharkeya4d1f222013-09-07 14:45:03 -070071 mRoot = root;
Jeff Sharkeyac9e6272013-08-31 21:27:44 -070072 mUri = uri;
Garfield, Tan11d23482016-08-05 09:33:29 -070073 mModel = model;
Steve McKay7776aa52016-01-25 19:00:22 -080074 mDoc = doc;
Aga Wronskaaf5ace52016-02-17 13:50:42 -080075 mSearchMode = inSearchMode;
Ben Lin9fea3122016-10-10 18:32:26 -070076 mObserver = new LockingContentObserver(lock, this::onContentChanged);
Jeff Sharkeya5defe32013-08-05 17:56:48 -070077 }
78
79 @Override
Jeff Sharkeyac9e6272013-08-31 21:27:44 -070080 public final DirectoryResult loadInBackground() {
81 synchronized (this) {
82 if (isLoadInBackgroundCanceled()) {
83 throw new OperationCanceledException();
84 }
85 mSignal = new CancellationSignal();
86 }
Jeff Sharkeya4d1f222013-09-07 14:45:03 -070087
88 final ContentResolver resolver = getContext().getContentResolver();
Jeff Sharkeyd82b26b2013-09-02 15:07:28 -070089 final String authority = mUri.getAuthority();
Jeff Sharkeya4d1f222013-09-07 14:45:03 -070090
91 final DirectoryResult result = new DirectoryResult();
Tomasz Mikolajewskie29e3412016-02-24 12:53:44 +090092 result.doc = mDoc;
Jeff Sharkeya4d1f222013-09-07 14:45:03 -070093
Jeff Sharkeyf63b7772013-10-01 17:57:41 -070094 ContentProviderClient client = null;
Garfield, Tan11d23482016-08-05 09:33:29 -070095 Cursor cursor;
Jeff Sharkeya4d1f222013-09-07 14:45:03 -070096 try {
Jeff Sharkeyf63b7772013-10-01 17:57:41 -070097 client = DocumentsApplication.acquireUnstableProviderOrThrow(resolver, authority);
Tomasz Mikolajewskib19061c2017-02-13 17:33:42 +090098 if (mDoc.isInArchive()) {
99 ArchivesProvider.acquireArchive(client, mUri);
100 }
101 result.client = client;
Steve McKay50b9bae2017-01-17 11:12:08 -0800102 Bundle queryArgs = new Bundle();
103 mModel.addQuerySortArgs(queryArgs);
104
105 // TODO: At some point we don't want forced flags to override real paging...
106 // and that point is when we have real paging.
107 DebugFlags.addForcedPagingArgs(queryArgs);
108
109 cursor = client.query(mUri, null, queryArgs, mSignal);
Makoto Onuki77778752015-07-01 14:55:14 -0700110 if (cursor == null) {
111 throw new RemoteException("Provider returned null");
112 }
113
Steve McKay50b9bae2017-01-17 11:12:08 -0800114 Bundle extras = cursor.getExtras();
115 if (extras.containsKey(ContentResolver.QUERY_RESULT_SIZE)) {
116 Log.i(TAG, "[PAGING INDICATED] Cursor extras specify recordset size of: "
117 + extras.getInt(ContentResolver.QUERY_RESULT_SIZE));
118 }
119
Jeff Sharkey20b32272013-09-03 15:25:52 -0700120 cursor.registerContentObserver(mObserver);
121
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700122 cursor = new RootCursorWrapper(mUri.getAuthority(), mRoot.rootId, cursor, -1);
Jeff Sharkeyd82b26b2013-09-02 15:07:28 -0700123
Garfield Tan16868832016-09-26 10:01:45 -0700124 if (mSearchMode && !Shared.ENABLE_OMC_API_FEATURES) {
Garfield Tanb00bbc52016-11-01 14:23:35 -0700125 // There is no findDocumentPath API. Enable filtering on folders in search mode.
Jeff Sharkey9dd02622013-09-27 16:44:11 -0700126 cursor = new FilteringCursorWrapper(cursor, null, SEARCH_REJECT_MIMES);
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700127 }
128
Garfield Tan2010ff72016-09-30 14:55:32 -0700129 cursor = mModel.sortCursor(cursor);
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700130 result.cursor = cursor;
Jeff Sharkey1d890e02013-08-15 11:24:03 -0700131 } catch (Exception e) {
Jeff Sharkey0e8c8712013-09-12 21:59:06 -0700132 Log.w(TAG, "Failed to query", e);
Jeff Sharkeyac9e6272013-08-31 21:27:44 -0700133 result.exception = e;
Jeff Sharkeyac9e6272013-08-31 21:27:44 -0700134 } finally {
135 synchronized (this) {
136 mSignal = null;
137 }
Tomasz Mikolajewskib19061c2017-02-13 17:33:42 +0900138 // TODO: Remove this call.
Ben Linf8f06e92017-01-27 17:15:48 -0800139 ContentProviderClient.releaseQuietly(client);
Jeff Sharkey1d890e02013-08-15 11:24:03 -0700140 }
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700141
Jeff Sharkey46899c82013-08-18 22:26:48 -0700142 return result;
Jeff Sharkey1d890e02013-08-15 11:24:03 -0700143 }
144
Jeff Sharkeyac9e6272013-08-31 21:27:44 -0700145 @Override
146 public void cancelLoadInBackground() {
147 super.cancelLoadInBackground();
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700148
Jeff Sharkeyac9e6272013-08-31 21:27:44 -0700149 synchronized (this) {
150 if (mSignal != null) {
151 mSignal.cancel();
Jeff Sharkey46899c82013-08-18 22:26:48 -0700152 }
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700153 }
Jeff Sharkeyac9e6272013-08-31 21:27:44 -0700154 }
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700155
Jeff Sharkeyac9e6272013-08-31 21:27:44 -0700156 @Override
157 public void deliverResult(DirectoryResult result) {
158 if (isReset()) {
159 IoUtils.closeQuietly(result);
160 return;
161 }
162 DirectoryResult oldResult = mResult;
163 mResult = result;
164
165 if (isStarted()) {
166 super.deliverResult(result);
167 }
168
169 if (oldResult != null && oldResult != result) {
170 IoUtils.closeQuietly(oldResult);
171 }
172 }
173
174 @Override
175 protected void onStartLoading() {
176 if (mResult != null) {
177 deliverResult(mResult);
178 }
179 if (takeContentChanged() || mResult == null) {
180 forceLoad();
181 }
182 }
183
184 @Override
185 protected void onStopLoading() {
186 cancelLoad();
187 }
188
189 @Override
190 public void onCanceled(DirectoryResult result) {
191 IoUtils.closeQuietly(result);
192 }
193
194 @Override
195 protected void onReset() {
196 super.onReset();
197
198 // Ensure the loader is stopped
199 onStopLoading();
200
201 IoUtils.closeQuietly(mResult);
202 mResult = null;
203
204 getContext().getContentResolver().unregisterContentObserver(mObserver);
205 }
Ben Lin9fea3122016-10-10 18:32:26 -0700206
207 private static final class LockingContentObserver extends ContentObserver {
208 private final DirectoryReloadLock mLock;
209 private final Runnable mContentChangedCallback;
210
211 public LockingContentObserver(DirectoryReloadLock lock, Runnable contentChangedCallback) {
212 super(new Handler());
213 mLock = lock;
214 mContentChangedCallback = contentChangedCallback;
215 }
216
217 @Override
218 public boolean deliverSelfNotifications() {
219 return true;
220 }
221
222 @Override
223 public void onChange(boolean selfChange) {
224 mLock.tryUpdate(mContentChangedCallback);
225 }
226 }
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700227}