1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
|
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.mail.content;
import com.android.mail.utils.LogTag;
import android.content.AsyncTaskLoader;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Arrays;
/**
* A copy of the framework's {@link android.content.CursorLoader} class. Copied because
* CursorLoader is not parameterized, and we want to parameterize over the underlying cursor type.
* @param <T>
*/
public class ObjectCursorLoader<T> extends AsyncTaskLoader<ObjectCursor<T>> {
final ForceLoadContentObserver mObserver;
protected static final String LOG_TAG = LogTag.getLogTag();
private Uri mUri;
final String[] mProjection;
// Copied over from CursorLoader, but none of our uses specify this. So these are hardcoded to
// null right here.
final String mSelection = null;
final String[] mSelectionArgs = null;
final String mSortOrder = null;
/** The underlying cursor that contains the data. */
ObjectCursor<T> mCursor;
/** The factory that knows how to create T objects from cursors: one object per row. */
private final CursorCreator<T> mFactory;
private int mDebugDelayMs = 0;
public ObjectCursorLoader(Context context, Uri uri, String[] projection,
CursorCreator<T> factory) {
super(context);
/*
* If these are null, it's going to crash anyway in loadInBackground(), but this stack trace
* is much more useful.
*/
if (factory == null) {
throw new NullPointerException("The factory cannot be null");
}
mObserver = new ForceLoadContentObserver();
setUri(uri);
mProjection = projection;
mFactory = factory;
}
/* Runs on a worker thread */
@Override
public ObjectCursor<T> loadInBackground() {
final Cursor inner = getContext().getContentResolver().query(mUri, mProjection,
mSelection, mSelectionArgs, mSortOrder);
if (inner == null) {
// If there's no underlying cursor, there's nothing to do.
return null;
}
// Ensure the cursor window is filled
inner.getCount();
inner.registerContentObserver(mObserver);
// Modifications to the ObjectCursor, create an Object Cursor and fill the cache.
final ObjectCursor<T> cursor = getObjectCursor(inner);
cursor.fillCache();
try {
if (mDebugDelayMs > 0) {
Thread.sleep(mDebugDelayMs);
}
} catch (InterruptedException e) {}
return cursor;
}
protected ObjectCursor<T> getObjectCursor(Cursor inner) {
return new ObjectCursor<T>(inner, mFactory);
}
/* Runs on the UI thread */
@Override
public void deliverResult(ObjectCursor<T> cursor) {
if (isReset()) {
// An async query came in while the loader is stopped
if (cursor != null) {
cursor.close();
}
return;
}
final Cursor oldCursor = mCursor;
mCursor = cursor;
if (isStarted()) {
super.deliverResult(cursor);
}
if (oldCursor != null && oldCursor != cursor && !oldCursor.isClosed()) {
oldCursor.close();
}
}
/**
* Starts an asynchronous load of the contacts list data. When the result is ready the callbacks
* will be called on the UI thread. If a previous load has been completed and is still valid
* the result may be passed to the callbacks immediately.
*
* Must be called from the UI thread
*/
@Override
protected void onStartLoading() {
if (mCursor != null) {
deliverResult(mCursor);
}
if (takeContentChanged() || mCursor == null) {
forceLoad();
}
}
/**
* Must be called from the UI thread
*/
@Override
protected void onStopLoading() {
// Attempt to cancel the current load task if possible.
cancelLoad();
}
@Override
public void onCanceled(ObjectCursor<T> cursor) {
if (cursor != null && !cursor.isClosed()) {
cursor.close();
}
}
@Override
protected void onReset() {
super.onReset();
// Ensure the loader is stopped
onStopLoading();
if (mCursor != null && !mCursor.isClosed()) {
mCursor.close();
}
mCursor = null;
}
@Override
public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
super.dump(prefix, fd, writer, args);
writer.print(prefix); writer.print("mUri="); writer.println(mUri);
writer.print(prefix); writer.print("mProjection=");
writer.println(Arrays.toString(mProjection));
writer.print(prefix); writer.print("mSelection="); writer.println(mSelection);
writer.print(prefix); writer.print("mSelectionArgs=");
writer.println(Arrays.toString(mSelectionArgs));
writer.print(prefix); writer.print("mSortOrder="); writer.println(mSortOrder);
writer.print(prefix); writer.print("mCursor="); writer.println(mCursor);
}
/**
* For debugging loader-related race conditions. Delays the background thread load. The delay is
* currently run after the query is complete.
*
* @param delayMs additional delay (in ms) to add to the background load operation
* @return this object itself, for fluent chaining
*/
public ObjectCursorLoader<T> setDebugDelay(int delayMs) {
mDebugDelayMs = delayMs;
return this;
}
public final Uri getUri() {
return mUri;
}
public final void setUri(Uri uri) {
if (uri == null) {
throw new NullPointerException("The uri cannot be null");
}
mUri = uri;
}
}
|