1 /*
2  * This is an adapted version of Android's original CursorLoader
3  * without all the ContentProvider-specific bits.
4  *
5  * Copyright (C) 2011 The Android Open Source Project
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 
20 package org.mozilla.gecko.home;
21 
22 import android.content.Context;
23 import android.database.Cursor;
24 import android.support.v4.content.AsyncTaskLoader;
25 
26 import org.mozilla.gecko.GeckoApplication;
27 
28 /**
29  * A copy of the framework's {@link android.content.CursorLoader} that
30  * instead allows the caller to load the Cursor themselves via the abstract
31  * {@link #loadCursor()} method, rather than calling out to a ContentProvider via
32  * class methods.
33  *
34  * For new code, prefer {@link android.content.CursorLoader} (see @deprecated).
35  *
36  * This was originally created to re-use existing code which loaded Cursors manually.
37  *
38  * @deprecated since the framework provides an implementation, we'd like to eventually remove
39  *             this class to reduce maintenance burden. Originally planned for bug 1239491, but
40  *             it'd be more efficient to do this over time, rather than all at once.
41  */
42 @Deprecated
43 public abstract class SimpleCursorLoader extends AsyncTaskLoader<Cursor> {
44     final ForceLoadContentObserver mObserver;
45     Cursor mCursor;
46 
SimpleCursorLoader(Context context)47     public SimpleCursorLoader(Context context) {
48         super(context);
49         mObserver = new ForceLoadContentObserver();
50     }
51 
52     /**
53      * Loads the target cursor for this loader. This method is called
54      * on a worker thread.
55      */
loadCursor()56     protected abstract Cursor loadCursor();
57 
58     /* Runs on a worker thread */
59     @Override
loadInBackground()60     public Cursor loadInBackground() {
61         Cursor cursor = loadCursor();
62 
63         if (cursor != null) {
64             // Ensure the cursor window is filled
65             cursor.getCount();
66             cursor.registerContentObserver(mObserver);
67         }
68 
69         return cursor;
70     }
71 
72     /* Runs on the UI thread */
73     @Override
deliverResult(Cursor cursor)74     public void deliverResult(Cursor cursor) {
75         if (isReset()) {
76             // An async query came in while the loader is stopped
77             if (cursor != null) {
78                 cursor.close();
79             }
80 
81             return;
82         }
83 
84         Cursor oldCursor = mCursor;
85         mCursor = cursor;
86 
87         if (isStarted()) {
88             super.deliverResult(cursor);
89         }
90 
91         if (oldCursor != null && oldCursor != cursor && !oldCursor.isClosed()) {
92             oldCursor.close();
93 
94             // Trying to read from the closed cursor will cause crashes, hence we should make
95             // sure that no adapters/LoaderCallbacks are holding onto this cursor.
96             GeckoApplication.getRefWatcher(getContext()).watch(oldCursor);
97         }
98     }
99 
100     /**
101      * Starts an asynchronous load of the list data. When the result is ready the callbacks
102      * will be called on the UI thread. If a previous load has been completed and is still valid
103      * the result may be passed to the callbacks immediately.
104      *
105      * Must be called from the UI thread
106      */
107     @Override
onStartLoading()108     protected void onStartLoading() {
109         if (mCursor != null) {
110             deliverResult(mCursor);
111         }
112 
113         if (takeContentChanged() || mCursor == null) {
114             forceLoad();
115         }
116     }
117 
118     /**
119      * Must be called from the UI thread
120      */
121     @Override
onStopLoading()122     protected void onStopLoading() {
123         // Attempt to cancel the current load task if possible.
124         cancelLoad();
125     }
126 
127     @Override
onCanceled(Cursor cursor)128     public void onCanceled(Cursor cursor) {
129         if (cursor != null && !cursor.isClosed()) {
130             cursor.close();
131         }
132     }
133 
134     @Override
onReset()135     protected void onReset() {
136         super.onReset();
137 
138         // Ensure the loader is stopped
139         onStopLoading();
140 
141         if (mCursor != null && !mCursor.isClosed()) {
142             mCursor.close();
143         }
144 
145         mCursor = null;
146     }
147 }