DirectoryLoader.java revision 3e1189b3590aefb65a2af720ae2ba959bbd4188d
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
17package com.android.documentsui;
18
19import static com.android.documentsui.DocumentsActivity.TAG;
20import static com.android.documentsui.DocumentsActivity.State.MODE_UNKNOWN;
21import 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;
24import static com.android.documentsui.DocumentsActivity.State.SORT_ORDER_UNKNOWN;
25import static com.android.documentsui.model.DocumentInfo.getCursorInt;
26
27import android.content.AsyncTaskLoader;
28import android.content.ContentProviderClient;
29import android.content.ContentResolver;
30import android.content.Context;
31import android.database.Cursor;
32import android.net.Uri;
33import android.os.CancellationSignal;
34import android.os.OperationCanceledException;
35import android.provider.DocumentsContract;
36import android.provider.DocumentsContract.Document;
37import 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;
43
44import libcore.io.IoUtils;
45
46import java.io.FileNotFoundException;
47
48class 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
57    public void close() {
58        IoUtils.closeQuietly(cursor);
59        ContentProviderClient.closeQuietly(client);
60        cursor = null;
61        client = null;
62    }
63}
64
65public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> {
66    private final ForceLoadContentObserver mObserver = new ForceLoadContentObserver();
67
68    private final int mType;
69    private final RootInfo mRoot;
70    private DocumentInfo mDoc;
71    private final Uri mUri;
72    private final int mUserSortOrder;
73
74    private CancellationSignal mSignal;
75    private DirectoryResult mResult;
76
77    public DirectoryLoader(Context context, int type, RootInfo root, DocumentInfo doc, Uri uri,
78            int userSortOrder) {
79        super(context);
80        mType = type;
81        mRoot = root;
82        mDoc = doc;
83        mUri = uri;
84        mUserSortOrder = userSortOrder;
85    }
86
87    @Override
88    public final DirectoryResult loadInBackground() {
89        synchronized (this) {
90            if (isLoadInBackgroundCanceled()) {
91                throw new OperationCanceledException();
92            }
93            mSignal = new CancellationSignal();
94        }
95
96        final ContentResolver resolver = getContext().getContentResolver();
97        final String authority = mUri.getAuthority();
98
99        final DirectoryResult result = new DirectoryResult();
100
101        int userMode = State.MODE_UNKNOWN;
102
103        // Use default document when searching
104        if (mType == DirectoryFragment.TYPE_SEARCH) {
105            final Uri docUri = DocumentsContract.buildDocumentUri(
106                    mRoot.authority, mRoot.documentId);
107            try {
108                mDoc = DocumentInfo.fromUri(resolver, docUri);
109            } catch (FileNotFoundException e) {
110                Log.w(TAG, "Failed to query", e);
111                result.exception = e;
112                return result;
113            }
114        }
115
116        // Pick up any custom modes requested by user
117        Cursor cursor = null;
118        try {
119            final Uri stateUri = RecentsProvider.buildState(
120                    mRoot.authority, mRoot.rootId, mDoc.documentId);
121            cursor = resolver.query(stateUri, null, null, null, null);
122            if (cursor.moveToFirst()) {
123                userMode = getCursorInt(cursor, StateColumns.MODE);
124            }
125        } finally {
126            IoUtils.closeQuietly(cursor);
127        }
128
129        if (userMode != State.MODE_UNKNOWN) {
130            result.mode = userMode;
131        } else {
132            if ((mDoc.flags & Document.FLAG_DIR_PREFERS_GRID) != 0) {
133                result.mode = State.MODE_GRID;
134            } else {
135                result.mode = State.MODE_LIST;
136            }
137        }
138
139        if (mUserSortOrder != State.SORT_ORDER_UNKNOWN) {
140            result.sortOrder = mUserSortOrder;
141        } else {
142            if ((mDoc.flags & Document.FLAG_DIR_PREFERS_LAST_MODIFIED) != 0) {
143                result.sortOrder = State.SORT_ORDER_LAST_MODIFIED;
144            } else {
145                result.sortOrder = State.SORT_ORDER_DISPLAY_NAME;
146            }
147        }
148
149        // Search always uses ranking from provider
150        if (mType == DirectoryFragment.TYPE_SEARCH) {
151            result.sortOrder = State.SORT_ORDER_UNKNOWN;
152        }
153
154        Log.d(TAG, "userMode=" + userMode + ", userSortOrder=" + mUserSortOrder + " --> mode="
155                + result.mode + ", sortOrder=" + result.sortOrder);
156
157        try {
158            result.client = resolver.acquireUnstableContentProviderClient(authority);
159            cursor = result.client.query(
160                    mUri, null, null, null, getQuerySortOrder(result.sortOrder), mSignal);
161            cursor.registerContentObserver(mObserver);
162
163            cursor = new RootCursorWrapper(mUri.getAuthority(), mRoot.rootId, cursor, -1);
164
165            if (mType == DirectoryFragment.TYPE_SEARCH) {
166                // Filter directories out of search results, for now
167                cursor = new FilteringCursorWrapper(cursor, null, new String[] {
168                        Document.MIME_TYPE_DIR });
169            } else {
170                // Normal directories should have sorting applied
171                cursor = new SortingCursorWrapper(cursor, result.sortOrder);
172            }
173
174            result.cursor = cursor;
175        } catch (Exception e) {
176            Log.w(TAG, "Failed to query", e);
177            result.exception = e;
178            ContentProviderClient.closeQuietly(result.client);
179        } finally {
180            synchronized (this) {
181                mSignal = null;
182            }
183        }
184
185        return result;
186    }
187
188    @Override
189    public void cancelLoadInBackground() {
190        super.cancelLoadInBackground();
191
192        synchronized (this) {
193            if (mSignal != null) {
194                mSignal.cancel();
195            }
196        }
197    }
198
199    @Override
200    public void deliverResult(DirectoryResult result) {
201        if (isReset()) {
202            IoUtils.closeQuietly(result);
203            return;
204        }
205        DirectoryResult oldResult = mResult;
206        mResult = result;
207
208        if (isStarted()) {
209            super.deliverResult(result);
210        }
211
212        if (oldResult != null && oldResult != result) {
213            IoUtils.closeQuietly(oldResult);
214        }
215    }
216
217    @Override
218    protected void onStartLoading() {
219        if (mResult != null) {
220            deliverResult(mResult);
221        }
222        if (takeContentChanged() || mResult == null) {
223            forceLoad();
224        }
225    }
226
227    @Override
228    protected void onStopLoading() {
229        cancelLoad();
230    }
231
232    @Override
233    public void onCanceled(DirectoryResult result) {
234        IoUtils.closeQuietly(result);
235    }
236
237    @Override
238    protected void onReset() {
239        super.onReset();
240
241        // Ensure the loader is stopped
242        onStopLoading();
243
244        IoUtils.closeQuietly(mResult);
245        mResult = null;
246
247        getContext().getContentResolver().unregisterContentObserver(mObserver);
248    }
249
250    public static String getQuerySortOrder(int sortOrder) {
251        switch (sortOrder) {
252            case SORT_ORDER_DISPLAY_NAME:
253                return Document.COLUMN_DISPLAY_NAME + " ASC";
254            case SORT_ORDER_LAST_MODIFIED:
255                return Document.COLUMN_LAST_MODIFIED + " DESC";
256            case SORT_ORDER_SIZE:
257                return Document.COLUMN_SIZE + " DESC";
258            default:
259                return null;
260        }
261    }
262}
263