blob: f425b2983662d9f46ac53dcce671f933538fd08f [file] [log] [blame]
Jeff Hamilton9911b7f2010-05-15 02:20:31 -05001/*
2 * Copyright (C) 2010 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 android.content;
18
19import android.database.ContentObserver;
20import android.os.Handler;
Dianne Hackborna2ea7472010-12-20 12:10:01 -080021import android.util.DebugUtils;
22
23import java.io.FileDescriptor;
24import java.io.PrintWriter;
Jeff Hamilton9911b7f2010-05-15 02:20:31 -050025
26/**
27 * An abstract class that performs asynchronous loading of data. While Loaders are active
28 * they should monitor the source of their data and deliver new results when the contents
Makoto Onukiec37c882010-08-02 16:57:01 -070029 * change.
Jeff Hamilton9911b7f2010-05-15 02:20:31 -050030 *
Dianne Hackborna2ea7472010-12-20 12:10:01 -080031 * <p>Subclasses generally must implement at least {@link #onStartLoading()},
32 * {@link #onStopLoading()}, {@link #onForceLoad()}, and {@link #onReset()}.
33 *
Jeff Hamilton9911b7f2010-05-15 02:20:31 -050034 * @param <D> The result returned when the load is complete
35 */
Dianne Hackborna2ea7472010-12-20 12:10:01 -080036public class Loader<D> {
Jeff Hamilton9911b7f2010-05-15 02:20:31 -050037 int mId;
38 OnLoadCompleteListener<D> mListener;
39 Context mContext;
Dianne Hackborna2ea7472010-12-20 12:10:01 -080040 boolean mStarted = false;
41 boolean mReset = true;
Jeff Hamilton9911b7f2010-05-15 02:20:31 -050042
43 public final class ForceLoadContentObserver extends ContentObserver {
44 public ForceLoadContentObserver() {
45 super(new Handler());
46 }
47
48 @Override
49 public boolean deliverSelfNotifications() {
50 return true;
51 }
52
53 @Override
54 public void onChange(boolean selfChange) {
Makoto Onukiec37c882010-08-02 16:57:01 -070055 onContentChanged();
Jeff Hamilton9911b7f2010-05-15 02:20:31 -050056 }
57 }
58
59 public interface OnLoadCompleteListener<D> {
60 /**
61 * Called on the thread that created the Loader when the load is complete.
62 *
63 * @param loader the loader that completed the load
64 * @param data the result of the load
65 */
66 public void onLoadComplete(Loader<D> loader, D data);
67 }
68
69 /**
70 * Stores away the application context associated with context. Since Loaders can be used
71 * across multiple activities it's dangerous to store the context directly.
72 *
73 * @param context used to retrieve the application context.
74 */
75 public Loader(Context context) {
76 mContext = context.getApplicationContext();
77 }
78
79 /**
80 * Sends the result of the load to the registered listener. Should only be called by subclasses.
81 *
82 * Must be called from the UI thread.
83 *
84 * @param data the result of the load
85 */
86 public void deliverResult(D data) {
87 if (mListener != null) {
88 mListener.onLoadComplete(this, data);
89 }
90 }
91
92 /**
93 * @return an application context retrieved from the Context passed to the constructor.
94 */
95 public Context getContext() {
96 return mContext;
97 }
98
99 /**
100 * @return the ID of this loader
101 */
102 public int getId() {
103 return mId;
104 }
105
106 /**
107 * Registers a class that will receive callbacks when a load is complete. The callbacks will
108 * be called on the UI thread so it's safe to pass the results to widgets.
Makoto Onukiec37c882010-08-02 16:57:01 -0700109 *
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500110 * Must be called from the UI thread
111 */
112 public void registerListener(int id, OnLoadCompleteListener<D> listener) {
113 if (mListener != null) {
114 throw new IllegalStateException("There is already a listener registered");
115 }
116 mListener = listener;
117 mId = id;
118 }
119
120 /**
121 * Must be called from the UI thread
122 */
123 public void unregisterListener(OnLoadCompleteListener<D> listener) {
124 if (mListener == null) {
125 throw new IllegalStateException("No listener register");
126 }
127 if (mListener != listener) {
128 throw new IllegalArgumentException("Attempting to unregister the wrong listener");
129 }
130 mListener = null;
131 }
132
133 /**
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800134 * Return whether this load has been started. That is, its {@link #startLoading()}
135 * has been called and no calls to {@link #stopLoading()} or
136 * {@link #reset()} have yet been made.
137 */
138 public boolean isStarted() {
139 return mStarted;
140 }
141
142 /**
143 * Return whether this load has been reset. That is, either the loader
144 * has not yet been started for the first time, or its {@link #reset()}
145 * has been called.
146 */
147 public boolean isReset() {
148 return mReset;
149 }
150
151 /**
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500152 * Starts an asynchronous load of the contacts list data. When the result is ready the callbacks
153 * will be called on the UI thread. If a previous load has been completed and is still valid
154 * the result may be passed to the callbacks immediately. The loader will monitor the source of
155 * the data set and may deliver future callbacks if the source changes. Calling
Makoto Onukiec37c882010-08-02 16:57:01 -0700156 * {@link #stopLoading} will stop the delivery of callbacks.
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500157 *
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800158 * <p>This updates the Loader's internal state so that
159 * {@link #isStarted()} and {@link #isReset()} will return the correct
160 * values, and then calls the implementation's {@link #onStartLoading()}.
161 *
162 * <p>Must be called from the UI thread.
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500163 */
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800164 public void startLoading() {
165 mStarted = true;
166 mReset = false;
167 onStartLoading();
168 }
169
170 /**
171 * Subclasses must implement this to take care of loading their data,
172 * as per {@link #startLoading()}. This is not called by clients directly,
173 * but as a result of a call to {@link #startLoading()}.
174 */
175 protected void onStartLoading() {
176 }
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500177
178 /**
179 * Force an asynchronous load. Unlike {@link #startLoading()} this will ignore a previously
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800180 * loaded data set and load a new one. This simply calls through to the
181 * implementation's {@link #onForceLoad()}.
182 *
183 * <p>Must be called from the UI thread.
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500184 */
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800185 public void forceLoad() {
186 onForceLoad();
187 }
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500188
189 /**
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800190 * Subclasses must implement this to take care of requests to {@link #forceLoad()}.
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500191 */
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800192 protected void onForceLoad() {
193 }
194
195 /**
196 * Stops delivery of updates until the next time {@link #startLoading()} is called.
197 *
198 * <p>This updates the Loader's internal state so that
199 * {@link #isStarted()} will return the correct
200 * value, and then calls the implementation's {@link #onStopLoading()}.
201 *
202 * <p>Must be called from the UI thread.
203 */
204 public void stopLoading() {
205 mStarted = false;
206 onStopLoading();
207 }
208
209 /**
210 * Subclasses must implement this to take care of stopping their loader,
211 * as per {@link #stopLoading()}. This is not called by clients directly,
212 * but as a result of a call to {@link #stopLoading()}.
213 */
214 protected void onStopLoading() {
215 }
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500216
217 /**
Dianne Hackbornc9189352010-12-15 14:57:25 -0800218 * Resets the state of the Loader. The Loader should at this point free
219 * all of its resources, since it may never be called again; however, its
220 * {@link #startLoading()} may later be called at which point it must be
221 * able to start running again.
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500222 *
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800223 * <p>This updates the Loader's internal state so that
224 * {@link #isStarted()} and {@link #isReset()} will return the correct
225 * values, and then calls the implementation's {@link #onReset()}.
226 *
227 * <p>Must be called from the UI thread.
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500228 */
Dianne Hackbornc9189352010-12-15 14:57:25 -0800229 public void reset() {
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800230 onReset();
231 mReset = true;
232 mStarted = false;
Dianne Hackbornc9189352010-12-15 14:57:25 -0800233 }
234
235 /**
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800236 * Subclasses must implement this to take care of resetting their loader,
237 * as per {@link #reset()}. This is not called by clients directly,
238 * but as a result of a call to {@link #reset()}.
Dianne Hackbornc9189352010-12-15 14:57:25 -0800239 */
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800240 protected void onReset() {
Dianne Hackbornc9189352010-12-15 14:57:25 -0800241 }
Makoto Onukiec37c882010-08-02 16:57:01 -0700242
243 /**
244 * Called when {@link ForceLoadContentObserver} detects a change. Calls {@link #forceLoad()}
245 * by default.
246 *
Dianne Hackbornc9189352010-12-15 14:57:25 -0800247 * <p>Must be called from the UI thread
Makoto Onukiec37c882010-08-02 16:57:01 -0700248 */
249 public void onContentChanged() {
250 forceLoad();
251 }
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800252
253 /**
254 * For debugging, converts an instance of the Loader's data class to
255 * a string that can be printed. Must handle a null data.
256 */
257 public String dataToString(D data) {
258 StringBuilder sb = new StringBuilder(64);
259 DebugUtils.buildShortClassTag(data, sb);
260 sb.append("}");
261 return sb.toString();
262 }
263
264 @Override
265 public String toString() {
266 StringBuilder sb = new StringBuilder(64);
267 DebugUtils.buildShortClassTag(this, sb);
268 sb.append(" id=");
269 sb.append(mId);
270 sb.append("}");
271 return sb.toString();
272 }
273
274 /**
275 * Print the Loader's state into the given stream.
276 *
277 * @param prefix Text to print at the front of each line.
278 * @param fd The raw file descriptor that the dump is being sent to.
279 * @param writer A PrintWriter to which the dump is to be set.
280 * @param args Additional arguments to the dump request.
281 */
282 public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
283 writer.print(prefix); writer.print("mId="); writer.print(mId);
284 writer.print(" mListener="); writer.println(mListener);
285 writer.print(prefix); writer.print("mStarted="); writer.print(mStarted);
286 writer.print(" mReset="); writer.println(mReset);
287 }
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500288}