switch to alsa.omap3 module
[android_pandora.git] / apps / AndroidSupportV2 / src / android / support / v2 / content / AsyncTaskLoader.java
1 /*
2  * Copyright (C) 2011 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
17 package android.support.v2.content;
18
19 import android.content.Context;
20 import android.os.AsyncTask;
21 import android.os.Handler;
22 import android.os.SystemClock;
23 import android.support.v2.util.TimeUtils;
24 import android.util.Log;
25
26 import java.io.FileDescriptor;
27 import java.io.PrintWriter;
28 import java.util.concurrent.CountDownLatch;
29
30 /**
31  * Static library support version of the framework's {@link android.content.AsyncTaskLoader}.
32  * Used to write apps that run on platforms prior to Android 3.0.  When running
33  * on Android 3.0 or above, this implementation is still used; it does not try
34  * to switch to the framework's implementation.  See the framework SDK
35  * documentation for a class overview.
36  */
37 public abstract class AsyncTaskLoader<D> extends Loader<D> {
38     static final String TAG = "AsyncTaskLoader";
39     static final boolean DEBUG = false;
40
41     final class LoadTask extends AsyncTask<Void, Void, D> implements Runnable {
42
43         D result;
44         boolean waiting;
45
46         private CountDownLatch done = new CountDownLatch(1);
47
48         /* Runs on a worker thread */
49         @Override
50         protected D doInBackground(Void... params) {
51             if (DEBUG) Log.v(TAG, this + " >>> doInBackground");
52             result = AsyncTaskLoader.this.onLoadInBackground();
53             if (DEBUG) Log.v(TAG, this + "  <<< doInBackground");
54             return result;
55         }
56
57         /* Runs on the UI thread */
58         @Override
59         protected void onPostExecute(D data) {
60             if (DEBUG) Log.v(TAG, this + " onPostExecute");
61             try {
62                 AsyncTaskLoader.this.dispatchOnLoadComplete(this, data);
63             } finally {
64                 done.countDown();
65             }
66         }
67
68         @Override
69         protected void onCancelled() {
70             if (DEBUG) Log.v(TAG, this + " onCancelled");
71             try {
72                 AsyncTaskLoader.this.dispatchOnCancelled(this, result);
73             } finally {
74                 done.countDown();
75             }
76         }
77
78         @Override
79         public void run() {
80             waiting = false;
81             AsyncTaskLoader.this.executePendingTask();
82         }
83     }
84
85     volatile LoadTask mTask;
86     volatile LoadTask mCancellingTask;
87
88     long mUpdateThrottle;
89     long mLastLoadCompleteTime = -10000;
90     Handler mHandler;
91
92     public AsyncTaskLoader(Context context) {
93         super(context);
94     }
95
96     /**
97      * Set amount to throttle updates by.  This is the minimum time from
98      * when the last {@link #onLoadInBackground()} call has completed until
99      * a new load is scheduled.
100      *
101      * @param delayMS Amount of delay, in milliseconds.
102      */
103     public void setUpdateThrottle(long delayMS) {
104         mUpdateThrottle = delayMS;
105         if (delayMS != 0) {
106             mHandler = new Handler();
107         }
108     }
109
110     @Override
111     protected void onForceLoad() {
112         super.onForceLoad();
113         cancelLoad();
114         mTask = new LoadTask();
115         if (DEBUG) Log.v(TAG, "Preparing load: mTask=" + mTask);
116         executePendingTask();
117     }
118
119     /**
120      * Attempt to cancel the current load task. See {@link AsyncTask#cancel(boolean)}
121      * for more info.  Must be called on the main thread of the process.
122      *
123      * <p>Cancelling is not an immediate operation, since the load is performed
124      * in a background thread.  If there is currently a load in progress, this
125      * method requests that the load be cancelled, and notes this is the case;
126      * once the background thread has completed its work its remaining state
127      * will be cleared.  If another load request comes in during this time,
128      * it will be held until the cancelled load is complete.
129      *
130      * @return Returns <tt>false</tt> if the task could not be cancelled,
131      *         typically because it has already completed normally, or
132      *         because {@link #startLoading()} hasn't been called; returns
133      *         <tt>true</tt> otherwise.
134      */
135     public boolean cancelLoad() {
136         if (DEBUG) Log.v(TAG, "cancelLoad: mTask=" + mTask);
137         if (mTask != null) {
138             if (mCancellingTask != null) {
139                 // There was a pending task already waiting for a previous
140                 // one being canceled; just drop it.
141                 if (DEBUG) Log.v(TAG,
142                         "cancelLoad: still waiting for cancelled task; dropping next");
143                 if (mTask.waiting) {
144                     mTask.waiting = false;
145                     mHandler.removeCallbacks(mTask);
146                 }
147                 mTask = null;
148                 return false;
149             } else if (mTask.waiting) {
150                 // There is a task, but it is waiting for the time it should
151                 // execute.  We can just toss it.
152                 if (DEBUG) Log.v(TAG, "cancelLoad: task is waiting, dropping it");
153                 mTask.waiting = false;
154                 mHandler.removeCallbacks(mTask);
155                 mTask = null;
156                 return false;
157             } else {
158                 boolean cancelled = mTask.cancel(false);
159                 if (DEBUG) Log.v(TAG, "cancelLoad: cancelled=" + cancelled);
160                 if (cancelled) {
161                     mCancellingTask = mTask;
162                 }
163                 mTask = null;
164                 return cancelled;
165             }
166         }
167         return false;
168     }
169
170     /**
171      * Called if the task was canceled before it was completed.  Gives the class a chance
172      * to properly dispose of the result.
173      */
174     public void onCanceled(D data) {
175     }
176
177     void executePendingTask() {
178         if (mCancellingTask == null && mTask != null) {
179             if (mTask.waiting) {
180                 mTask.waiting = false;
181                 mHandler.removeCallbacks(mTask);
182             }
183             if (mUpdateThrottle > 0) {
184                 long now = SystemClock.uptimeMillis();
185                 if (now < (mLastLoadCompleteTime+mUpdateThrottle)) {
186                     // Not yet time to do another load.
187                     if (DEBUG) Log.v(TAG, "Waiting until "
188                             + (mLastLoadCompleteTime+mUpdateThrottle)
189                             + " to execute: " + mTask);
190                     mTask.waiting = true;
191                     mHandler.postAtTime(mTask, mLastLoadCompleteTime+mUpdateThrottle);
192                     return;
193                 }
194             }
195             if (DEBUG) Log.v(TAG, "Executing: " + mTask);
196             mTask.execute((Void[]) null);
197             // XXX TO DO: use reflection to call this version.
198             //mTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
199         }
200     }
201
202     void dispatchOnCancelled(LoadTask task, D data) {
203         onCanceled(data);
204         if (mCancellingTask == task) {
205             if (DEBUG) Log.v(TAG, "Cancelled task is now canceled!");
206             mLastLoadCompleteTime = SystemClock.uptimeMillis();
207             mCancellingTask = null;
208             executePendingTask();
209         }
210     }
211
212     void dispatchOnLoadComplete(LoadTask task, D data) {
213         if (mTask != task) {
214             if (DEBUG) Log.v(TAG, "Load complete of old task, trying to cancel");
215             dispatchOnCancelled(task, data);
216         } else {
217             if (isAbandoned()) {
218                 // This cursor has been abandoned; just cancel the new data.
219                 onCanceled(data);
220             } else {
221                 mLastLoadCompleteTime = SystemClock.uptimeMillis();
222                 mTask = null;
223                 if (DEBUG) Log.v(TAG, "Delivering result");
224                 deliverResult(data);
225             }
226         }
227     }
228
229     /**
230      */
231     public abstract D loadInBackground();
232
233     /**
234      * Called on a worker thread to perform the actual load. Implementations should not deliver the
235      * result directly, but should return them from this method, which will eventually end up
236      * calling {@link #deliverResult} on the UI thread. If implementations need to process
237      * the results on the UI thread they may override {@link #deliverResult} and do so
238      * there.
239      *
240      * @return Implementations must return the result of their load operation.
241      */
242     protected D onLoadInBackground() {
243         return loadInBackground();
244     }
245
246     /**
247      * Locks the current thread until the loader completes the current load
248      * operation. Returns immediately if there is no load operation running.
249      * Should not be called from the UI thread: calling it from the UI
250      * thread would cause a deadlock.
251      * <p>
252      * Use for testing only.  <b>Never</b> call this from a UI thread.
253      *
254      * @hide
255      */
256     public void waitForLoader() {
257         LoadTask task = mTask;
258         if (task != null) {
259             try {
260                 task.done.await();
261             } catch (InterruptedException e) {
262                 // Ignore
263             }
264         }
265     }
266
267     @Override
268     public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
269         super.dump(prefix, fd, writer, args);
270         if (mTask != null) {
271             writer.print(prefix); writer.print("mTask="); writer.print(mTask);
272                     writer.print(" waiting="); writer.println(mTask.waiting);
273         }
274         if (mCancellingTask != null) {
275             writer.print(prefix); writer.print("mCancellingTask="); writer.print(mCancellingTask);
276                     writer.print(" waiting="); writer.println(mCancellingTask.waiting);
277         }
278         if (mUpdateThrottle != 0) {
279             writer.print(prefix); writer.print("mUpdateThrottle=");
280                     TimeUtils.formatDuration(mUpdateThrottle, writer);
281                     writer.print(" mLastLoadCompleteTime=");
282                     TimeUtils.formatDuration(mLastLoadCompleteTime,
283                             SystemClock.uptimeMillis(), writer);
284                     writer.println();
285         }
286     }
287 }