• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.documentsui;
18 
19 import static com.android.documentsui.DocumentsActivity.TAG;
20 import static com.android.documentsui.DocumentsActivity.State.MODE_UNKNOWN;
21 import static com.android.documentsui.DocumentsActivity.State.SORT_ORDER_DISPLAY_NAME;
22 import static com.android.documentsui.DocumentsActivity.State.SORT_ORDER_LAST_MODIFIED;
23 import static com.android.documentsui.DocumentsActivity.State.SORT_ORDER_SIZE;
24 import static com.android.documentsui.DocumentsActivity.State.SORT_ORDER_UNKNOWN;
25 import static com.android.documentsui.model.DocumentInfo.getCursorInt;
26 
27 import android.content.AsyncTaskLoader;
28 import android.content.ContentProviderClient;
29 import android.content.ContentResolver;
30 import android.content.Context;
31 import android.database.Cursor;
32 import android.net.Uri;
33 import android.os.CancellationSignal;
34 import android.os.OperationCanceledException;
35 import android.provider.DocumentsContract;
36 import android.provider.DocumentsContract.Document;
37 import android.util.Log;
38 
39 import com.android.documentsui.DocumentsActivity.State;
40 import com.android.documentsui.RecentsProvider.StateColumns;
41 import com.android.documentsui.model.DocumentInfo;
42 import com.android.documentsui.model.RootInfo;
43 
44 import libcore.io.IoUtils;
45 
46 import java.io.FileNotFoundException;
47 
48 class DirectoryResult implements AutoCloseable {
49     ContentProviderClient client;
50     Cursor cursor;
51     Exception exception;
52 
53     int mode = MODE_UNKNOWN;
54     int sortOrder = SORT_ORDER_UNKNOWN;
55 
56     @Override
close()57     public void close() {
58         IoUtils.closeQuietly(cursor);
59         ContentProviderClient.releaseQuietly(client);
60         cursor = null;
61         client = null;
62     }
63 }
64 
65 public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> {
66 
67     private static final String[] SEARCH_REJECT_MIMES = new String[] { Document.MIME_TYPE_DIR };
68 
69     private final ForceLoadContentObserver mObserver = new ForceLoadContentObserver();
70 
71     private final int mType;
72     private final RootInfo mRoot;
73     private DocumentInfo mDoc;
74     private final Uri mUri;
75     private final int mUserSortOrder;
76 
77     private CancellationSignal mSignal;
78     private DirectoryResult mResult;
79 
DirectoryLoader(Context context, int type, RootInfo root, DocumentInfo doc, Uri uri, int userSortOrder)80     public DirectoryLoader(Context context, int type, RootInfo root, DocumentInfo doc, Uri uri,
81             int userSortOrder) {
82         super(context, ProviderExecutor.forAuthority(root.authority));
83         mType = type;
84         mRoot = root;
85         mDoc = doc;
86         mUri = uri;
87         mUserSortOrder = userSortOrder;
88     }
89 
90     @Override
loadInBackground()91     public final DirectoryResult loadInBackground() {
92         synchronized (this) {
93             if (isLoadInBackgroundCanceled()) {
94                 throw new OperationCanceledException();
95             }
96             mSignal = new CancellationSignal();
97         }
98 
99         final ContentResolver resolver = getContext().getContentResolver();
100         final String authority = mUri.getAuthority();
101 
102         final DirectoryResult result = new DirectoryResult();
103 
104         int userMode = State.MODE_UNKNOWN;
105 
106         // 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 
119         // Pick up any custom modes requested by user
120         Cursor cursor = null;
121         try {
122             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);
127             }
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 
142         if (mUserSortOrder != State.SORT_ORDER_UNKNOWN) {
143             result.sortOrder = mUserSortOrder;
144         } 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 
152         // Search always uses ranking from provider
153         if (mType == DirectoryFragment.TYPE_SEARCH) {
154             result.sortOrder = State.SORT_ORDER_UNKNOWN;
155         }
156 
157         Log.d(TAG, "userMode=" + userMode + ", userSortOrder=" + mUserSortOrder + " --> mode="
158                 + result.mode + ", sortOrder=" + result.sortOrder);
159 
160         ContentProviderClient client = null;
161         try {
162             client = DocumentsApplication.acquireUnstableProviderOrThrow(resolver, authority);
163 
164             cursor = client.query(
165                     mUri, null, null, null, getQuerySortOrder(result.sortOrder), mSignal);
166             cursor.registerContentObserver(mObserver);
167 
168             cursor = new RootCursorWrapper(mUri.getAuthority(), mRoot.rootId, cursor, -1);
169 
170             if (mType == DirectoryFragment.TYPE_SEARCH) {
171                 // Filter directories out of search results, for now
172                 cursor = new FilteringCursorWrapper(cursor, null, SEARCH_REJECT_MIMES);
173             } else {
174                 // Normal directories should have sorting applied
175                 cursor = new SortingCursorWrapper(cursor, result.sortOrder);
176             }
177 
178             result.client = client;
179             result.cursor = cursor;
180         } catch (Exception e) {
181             Log.w(TAG, "Failed to query", e);
182             result.exception = e;
183             ContentProviderClient.releaseQuietly(client);
184         } finally {
185             synchronized (this) {
186                 mSignal = null;
187             }
188         }
189 
190         return result;
191     }
192 
193     @Override
cancelLoadInBackground()194     public void cancelLoadInBackground() {
195         super.cancelLoadInBackground();
196 
197         synchronized (this) {
198             if (mSignal != null) {
199                 mSignal.cancel();
200             }
201         }
202     }
203 
204     @Override
deliverResult(DirectoryResult result)205     public void deliverResult(DirectoryResult result) {
206         if (isReset()) {
207             IoUtils.closeQuietly(result);
208             return;
209         }
210         DirectoryResult oldResult = mResult;
211         mResult = result;
212 
213         if (isStarted()) {
214             super.deliverResult(result);
215         }
216 
217         if (oldResult != null && oldResult != result) {
218             IoUtils.closeQuietly(oldResult);
219         }
220     }
221 
222     @Override
onStartLoading()223     protected void onStartLoading() {
224         if (mResult != null) {
225             deliverResult(mResult);
226         }
227         if (takeContentChanged() || mResult == null) {
228             forceLoad();
229         }
230     }
231 
232     @Override
onStopLoading()233     protected void onStopLoading() {
234         cancelLoad();
235     }
236 
237     @Override
onCanceled(DirectoryResult result)238     public void onCanceled(DirectoryResult result) {
239         IoUtils.closeQuietly(result);
240     }
241 
242     @Override
onReset()243     protected void onReset() {
244         super.onReset();
245 
246         // Ensure the loader is stopped
247         onStopLoading();
248 
249         IoUtils.closeQuietly(mResult);
250         mResult = null;
251 
252         getContext().getContentResolver().unregisterContentObserver(mObserver);
253     }
254 
getQuerySortOrder(int sortOrder)255     public static String getQuerySortOrder(int sortOrder) {
256         switch (sortOrder) {
257             case SORT_ORDER_DISPLAY_NAME:
258                 return Document.COLUMN_DISPLAY_NAME + " ASC";
259             case SORT_ORDER_LAST_MODIFIED:
260                 return Document.COLUMN_LAST_MODIFIED + " DESC";
261             case SORT_ORDER_SIZE:
262                 return Document.COLUMN_SIZE + " DESC";
263             default:
264                 return null;
265         }
266     }
267 }
268