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