ObjectCursorLoader.java revision 60593354f1126b9687d09c3b11a852a3459f7118
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.mail.content;
18
19import com.android.mail.utils.LogTag;
20
21import android.content.AsyncTaskLoader;
22import android.content.Context;
23import android.database.ContentObserver;
24import android.database.Cursor;
25import android.net.Uri;
26
27import java.io.FileDescriptor;
28import java.io.PrintWriter;
29import java.util.Arrays;
30
31/**
32 * A copy of the framework's {@link android.content.CursorLoader} class. Copied because
33 * CursorLoader is not parameterized, and we want to parameterize over the underlying cursor type.
34 * @param <T>
35 */
36public class ObjectCursorLoader<T> extends AsyncTaskLoader<ObjectCursor<T>> {
37    final ForceLoadContentObserver mObserver;
38    protected static final String LOG_TAG = LogTag.getLogTag();
39
40    final Uri mUri;
41    final String[] mProjection;
42    // Copied over from CursorLoader, but none of our uses specify this. So these are hardcoded to
43    // null right here.
44    final String mSelection = null;
45    final String[] mSelectionArgs = null;
46    final String mSortOrder = null;
47
48    /** The underlying cursor that contains the data. */
49    ObjectCursor<T> mCursor;
50
51    /** The factory that knows how to create T objects from cursors: one object per row. */
52    private final CursorCreator<T> mFactory;
53
54    public ObjectCursorLoader(Context context, Uri uri, String[] projection,
55            CursorCreator<T> factory) {
56        super(context);
57
58        /*
59         * If these are null, it's going to crash anyway in loadInBackground(), but this stack trace
60         * is much more useful.
61         */
62        if (uri == null) {
63            throw new NullPointerException("The uri cannot be null");
64        }
65        if (factory == null) {
66            throw new NullPointerException("The factory cannot be null");
67        }
68
69        mObserver = new ForceLoadContentObserver();
70        mUri = uri;
71        mProjection = projection;
72        mFactory = factory;
73    }
74
75    /* Runs on a worker thread */
76    @Override
77    public ObjectCursor<T> loadInBackground() {
78        final Cursor inner = getContext().getContentResolver().query(mUri, mProjection,
79                mSelection, mSelectionArgs, mSortOrder);
80        if (inner != null) {
81            // Ensure the cursor window is filled
82            inner.getCount();
83            registerContentObserver(inner, mObserver);
84        }
85        // Modifications to the ObjectCursor, create an Object Cursor and fill the cache.
86        final ObjectCursor<T> cursor = new ObjectCursor<T>(inner, mFactory);
87        cursor.fillCache();
88        return cursor;
89    }
90
91    /**
92     * Registers an observer to get notifications from the content provider
93     * when the cursor needs to be refreshed.
94     */
95    void registerContentObserver(Cursor cursor, ContentObserver observer) {
96        cursor.registerContentObserver(mObserver);
97    }
98
99    /* Runs on the UI thread */
100    @Override
101    public void deliverResult(ObjectCursor<T> cursor) {
102        if (isReset()) {
103            // An async query came in while the loader is stopped
104            if (cursor != null) {
105                cursor.close();
106            }
107            return;
108        }
109        final Cursor oldCursor = mCursor;
110        mCursor = cursor;
111
112        if (isStarted()) {
113            super.deliverResult(cursor);
114        }
115
116        if (oldCursor != null && oldCursor != cursor && !oldCursor.isClosed()) {
117            oldCursor.close();
118        }
119    }
120
121    /**
122     * Starts an asynchronous load of the contacts list data. When the result is ready the callbacks
123     * will be called on the UI thread. If a previous load has been completed and is still valid
124     * the result may be passed to the callbacks immediately.
125     *
126     * Must be called from the UI thread
127     */
128    @Override
129    protected void onStartLoading() {
130        if (mCursor != null) {
131            deliverResult(mCursor);
132        }
133        if (takeContentChanged() || mCursor == null) {
134            forceLoad();
135        }
136    }
137
138    /**
139     * Must be called from the UI thread
140     */
141    @Override
142    protected void onStopLoading() {
143        // Attempt to cancel the current load task if possible.
144        cancelLoad();
145    }
146
147    @Override
148    public boolean cancelLoad() {
149        if (mCursor != null && !mCursor.isClosed()) {
150            mCursor.close();
151        }
152        return super.cancelLoad();
153    }
154
155    @Override
156    protected void onReset() {
157        super.onReset();
158
159        // Ensure the loader is stopped
160        onStopLoading();
161
162        if (mCursor != null && !mCursor.isClosed()) {
163            mCursor.close();
164        }
165        mCursor = null;
166    }
167
168    @Override
169    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
170        super.dump(prefix, fd, writer, args);
171        writer.print(prefix); writer.print("mUri="); writer.println(mUri);
172        writer.print(prefix); writer.print("mProjection=");
173        writer.println(Arrays.toString(mProjection));
174        writer.print(prefix); writer.print("mSelection="); writer.println(mSelection);
175        writer.print(prefix); writer.print("mSelectionArgs=");
176        writer.println(Arrays.toString(mSelectionArgs));
177        writer.print(prefix); writer.print("mSortOrder="); writer.println(mSortOrder);
178        writer.print(prefix); writer.print("mCursor="); writer.println(mCursor);
179    }
180}
181