blob: 56e76bbdf4c035a2e20b782f78378975ff09046b [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;
26import android.os.CancellationSignal;
Ben Lin9fea3122016-10-10 18:32:26 -070027import android.os.Handler;
Jeff Sharkeyac9e6272013-08-31 21:27:44 -070028import android.os.OperationCanceledException;
Makoto Onuki77778752015-07-01 14:55:14 -070029import android.os.RemoteException;
Jeff Sharkeyac9e6272013-08-31 21:27:44 -070030import android.provider.DocumentsContract.Document;
Jeff Sharkeya4d1f222013-09-07 14:45:03 -070031import android.util.Log;
32
Steve McKayd0805062016-09-15 14:30:38 -070033import com.android.documentsui.base.DocumentInfo;
Steve McKayd9caa6a2016-09-15 16:36:45 -070034import com.android.documentsui.base.FilteringCursorWrapper;
Steve McKayd0805062016-09-15 14:30:38 -070035import com.android.documentsui.base.RootInfo;
Steve McKayd9caa6a2016-09-15 16:36:45 -070036import com.android.documentsui.roots.RootCursorWrapper;
Garfield, Tan11d23482016-08-05 09:33:29 -070037import com.android.documentsui.sorting.SortModel;
Jeff Sharkeya5defe32013-08-05 17:56:48 -070038
Steve McKayc88f83c2016-08-31 12:01:43 -070039import libcore.io.IoUtils;
40
Jeff Sharkeyac9e6272013-08-31 21:27:44 -070041public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> {
Jeff Sharkey9dd02622013-09-27 16:44:11 -070042
Garfield, Tan11d23482016-08-05 09:33:29 -070043 private static final String TAG = "DirectoryLoader";
44
Jeff Sharkey9dd02622013-09-27 16:44:11 -070045 private static final String[] SEARCH_REJECT_MIMES = new String[] { Document.MIME_TYPE_DIR };
46
Ben Lin9fea3122016-10-10 18:32:26 -070047 private final LockingContentObserver mObserver;
Jeff Sharkeya4d1f222013-09-07 14:45:03 -070048 private final RootInfo mRoot;
Jeff Sharkeyac9e6272013-08-31 21:27:44 -070049 private final Uri mUri;
Garfield, Tan11d23482016-08-05 09:33:29 -070050 private final SortModel mModel;
Aga Wronskaaf5ace52016-02-17 13:50:42 -080051 private final boolean mSearchMode;
Jeff Sharkeya5defe32013-08-05 17:56:48 -070052
Steve McKay7776aa52016-01-25 19:00:22 -080053 private DocumentInfo mDoc;
Jeff Sharkeyac9e6272013-08-31 21:27:44 -070054 private CancellationSignal mSignal;
55 private DirectoryResult mResult;
56
Steve McKayc88f83c2016-08-31 12:01:43 -070057 public DirectoryLoader(
58 Context context,
59 RootInfo root,
60 DocumentInfo doc,
61 Uri uri,
62 SortModel model,
Ben Lin9fea3122016-10-10 18:32:26 -070063 DirectoryReloadLock lock,
Steve McKayc88f83c2016-08-31 12:01:43 -070064 boolean inSearchMode) {
65
Jeff Sharkeyf63b7772013-10-01 17:57:41 -070066 super(context, ProviderExecutor.forAuthority(root.authority));
Jeff Sharkeya4d1f222013-09-07 14:45:03 -070067 mRoot = root;
Jeff Sharkeyac9e6272013-08-31 21:27:44 -070068 mUri = uri;
Garfield, Tan11d23482016-08-05 09:33:29 -070069 mModel = model;
Steve McKay7776aa52016-01-25 19:00:22 -080070 mDoc = doc;
Aga Wronskaaf5ace52016-02-17 13:50:42 -080071 mSearchMode = inSearchMode;
Ben Lin9fea3122016-10-10 18:32:26 -070072 mObserver = new LockingContentObserver(lock, this::onContentChanged);
Jeff Sharkeya5defe32013-08-05 17:56:48 -070073 }
74
75 @Override
Jeff Sharkeyac9e6272013-08-31 21:27:44 -070076 public final DirectoryResult loadInBackground() {
77 synchronized (this) {
78 if (isLoadInBackgroundCanceled()) {
79 throw new OperationCanceledException();
80 }
81 mSignal = new CancellationSignal();
82 }
Jeff Sharkeya4d1f222013-09-07 14:45:03 -070083
84 final ContentResolver resolver = getContext().getContentResolver();
Jeff Sharkeyd82b26b2013-09-02 15:07:28 -070085 final String authority = mUri.getAuthority();
Jeff Sharkeya4d1f222013-09-07 14:45:03 -070086
87 final DirectoryResult result = new DirectoryResult();
Tomasz Mikolajewskie29e3412016-02-24 12:53:44 +090088 result.doc = mDoc;
Jeff Sharkeya4d1f222013-09-07 14:45:03 -070089
Jeff Sharkeyf63b7772013-10-01 17:57:41 -070090 ContentProviderClient client = null;
Garfield, Tan11d23482016-08-05 09:33:29 -070091 Cursor cursor;
Jeff Sharkeya4d1f222013-09-07 14:45:03 -070092 try {
Jeff Sharkeyf63b7772013-10-01 17:57:41 -070093 client = DocumentsApplication.acquireUnstableProviderOrThrow(resolver, authority);
Jeff Sharkeyf63b7772013-10-01 17:57:41 -070094 cursor = client.query(
Garfield, Tan11d23482016-08-05 09:33:29 -070095 mUri, null, null, null, mModel.getDocumentSortQuery(), mSignal);
Makoto Onuki77778752015-07-01 14:55:14 -070096 if (cursor == null) {
97 throw new RemoteException("Provider returned null");
98 }
99
Jeff Sharkey20b32272013-09-03 15:25:52 -0700100 cursor.registerContentObserver(mObserver);
101
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700102 cursor = new RootCursorWrapper(mUri.getAuthority(), mRoot.rootId, cursor, -1);
Jeff Sharkeyd82b26b2013-09-02 15:07:28 -0700103
Aga Wronskaaf5ace52016-02-17 13:50:42 -0800104 if (mSearchMode) {
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700105 // Filter directories out of search results, for now
Jeff Sharkey9dd02622013-09-27 16:44:11 -0700106 cursor = new FilteringCursorWrapper(cursor, null, SEARCH_REJECT_MIMES);
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700107 }
108
Garfield Tan2010ff72016-09-30 14:55:32 -0700109 cursor = mModel.sortCursor(cursor);
110
Jeff Sharkeyf63b7772013-10-01 17:57:41 -0700111 result.client = client;
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700112 result.cursor = cursor;
Jeff Sharkey1d890e02013-08-15 11:24:03 -0700113 } catch (Exception e) {
Jeff Sharkey0e8c8712013-09-12 21:59:06 -0700114 Log.w(TAG, "Failed to query", e);
Jeff Sharkeyac9e6272013-08-31 21:27:44 -0700115 result.exception = e;
Jeff Sharkeyf63b7772013-10-01 17:57:41 -0700116 ContentProviderClient.releaseQuietly(client);
Jeff Sharkeyac9e6272013-08-31 21:27:44 -0700117 } finally {
118 synchronized (this) {
119 mSignal = null;
120 }
Jeff Sharkey1d890e02013-08-15 11:24:03 -0700121 }
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700122
Jeff Sharkey46899c82013-08-18 22:26:48 -0700123 return result;
Jeff Sharkey1d890e02013-08-15 11:24:03 -0700124 }
125
Jeff Sharkeyac9e6272013-08-31 21:27:44 -0700126 @Override
127 public void cancelLoadInBackground() {
128 super.cancelLoadInBackground();
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700129
Jeff Sharkeyac9e6272013-08-31 21:27:44 -0700130 synchronized (this) {
131 if (mSignal != null) {
132 mSignal.cancel();
Jeff Sharkey46899c82013-08-18 22:26:48 -0700133 }
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700134 }
Jeff Sharkeyac9e6272013-08-31 21:27:44 -0700135 }
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700136
Jeff Sharkeyac9e6272013-08-31 21:27:44 -0700137 @Override
138 public void deliverResult(DirectoryResult result) {
139 if (isReset()) {
140 IoUtils.closeQuietly(result);
141 return;
142 }
143 DirectoryResult oldResult = mResult;
144 mResult = result;
145
146 if (isStarted()) {
147 super.deliverResult(result);
148 }
149
150 if (oldResult != null && oldResult != result) {
151 IoUtils.closeQuietly(oldResult);
152 }
153 }
154
155 @Override
156 protected void onStartLoading() {
157 if (mResult != null) {
158 deliverResult(mResult);
159 }
160 if (takeContentChanged() || mResult == null) {
161 forceLoad();
162 }
163 }
164
165 @Override
166 protected void onStopLoading() {
167 cancelLoad();
168 }
169
170 @Override
171 public void onCanceled(DirectoryResult result) {
172 IoUtils.closeQuietly(result);
173 }
174
175 @Override
176 protected void onReset() {
177 super.onReset();
178
179 // Ensure the loader is stopped
180 onStopLoading();
181
182 IoUtils.closeQuietly(mResult);
183 mResult = null;
184
185 getContext().getContentResolver().unregisterContentObserver(mObserver);
186 }
Ben Lin9fea3122016-10-10 18:32:26 -0700187
188 private static final class LockingContentObserver extends ContentObserver {
189 private final DirectoryReloadLock mLock;
190 private final Runnable mContentChangedCallback;
191
192 public LockingContentObserver(DirectoryReloadLock lock, Runnable contentChangedCallback) {
193 super(new Handler());
194 mLock = lock;
195 mContentChangedCallback = contentChangedCallback;
196 }
197
198 @Override
199 public boolean deliverSelfNotifications() {
200 return true;
201 }
202
203 @Override
204 public void onChange(boolean selfChange) {
205 mLock.tryUpdate(mContentChangedCallback);
206 }
207 }
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700208}