add OI File Manager and AndroidSupportV2 used by it
[android_pandora.git] / apps / AndroidSupportV2 / src / android / support / v2 / content / AsyncTaskLoader.java
diff --git a/apps/AndroidSupportV2/src/android/support/v2/content/AsyncTaskLoader.java b/apps/AndroidSupportV2/src/android/support/v2/content/AsyncTaskLoader.java
new file mode 100644 (file)
index 0000000..e2a3330
--- /dev/null
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2011 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 android.support.v2.content;
+
+import android.content.Context;
+import android.os.AsyncTask;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.support.v2.util.TimeUtils;
+import android.util.Log;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * Static library support version of the framework's {@link android.content.AsyncTaskLoader}.
+ * Used to write apps that run on platforms prior to Android 3.0.  When running
+ * on Android 3.0 or above, this implementation is still used; it does not try
+ * to switch to the framework's implementation.  See the framework SDK
+ * documentation for a class overview.
+ */
+public abstract class AsyncTaskLoader<D> extends Loader<D> {
+    static final String TAG = "AsyncTaskLoader";
+    static final boolean DEBUG = false;
+
+    final class LoadTask extends AsyncTask<Void, Void, D> implements Runnable {
+
+        D result;
+        boolean waiting;
+
+        private CountDownLatch done = new CountDownLatch(1);
+
+        /* Runs on a worker thread */
+        @Override
+        protected D doInBackground(Void... params) {
+            if (DEBUG) Log.v(TAG, this + " >>> doInBackground");
+            result = AsyncTaskLoader.this.onLoadInBackground();
+            if (DEBUG) Log.v(TAG, this + "  <<< doInBackground");
+            return result;
+        }
+
+        /* Runs on the UI thread */
+        @Override
+        protected void onPostExecute(D data) {
+            if (DEBUG) Log.v(TAG, this + " onPostExecute");
+            try {
+                AsyncTaskLoader.this.dispatchOnLoadComplete(this, data);
+            } finally {
+                done.countDown();
+            }
+        }
+
+        @Override
+        protected void onCancelled() {
+            if (DEBUG) Log.v(TAG, this + " onCancelled");
+            try {
+                AsyncTaskLoader.this.dispatchOnCancelled(this, result);
+            } finally {
+                done.countDown();
+            }
+        }
+
+        @Override
+        public void run() {
+            waiting = false;
+            AsyncTaskLoader.this.executePendingTask();
+        }
+    }
+
+    volatile LoadTask mTask;
+    volatile LoadTask mCancellingTask;
+
+    long mUpdateThrottle;
+    long mLastLoadCompleteTime = -10000;
+    Handler mHandler;
+
+    public AsyncTaskLoader(Context context) {
+        super(context);
+    }
+
+    /**
+     * Set amount to throttle updates by.  This is the minimum time from
+     * when the last {@link #onLoadInBackground()} call has completed until
+     * a new load is scheduled.
+     *
+     * @param delayMS Amount of delay, in milliseconds.
+     */
+    public void setUpdateThrottle(long delayMS) {
+        mUpdateThrottle = delayMS;
+        if (delayMS != 0) {
+            mHandler = new Handler();
+        }
+    }
+
+    @Override
+    protected void onForceLoad() {
+        super.onForceLoad();
+        cancelLoad();
+        mTask = new LoadTask();
+        if (DEBUG) Log.v(TAG, "Preparing load: mTask=" + mTask);
+        executePendingTask();
+    }
+
+    /**
+     * Attempt to cancel the current load task. See {@link AsyncTask#cancel(boolean)}
+     * for more info.  Must be called on the main thread of the process.
+     *
+     * <p>Cancelling is not an immediate operation, since the load is performed
+     * in a background thread.  If there is currently a load in progress, this
+     * method requests that the load be cancelled, and notes this is the case;
+     * once the background thread has completed its work its remaining state
+     * will be cleared.  If another load request comes in during this time,
+     * it will be held until the cancelled load is complete.
+     *
+     * @return Returns <tt>false</tt> if the task could not be cancelled,
+     *         typically because it has already completed normally, or
+     *         because {@link #startLoading()} hasn't been called; returns
+     *         <tt>true</tt> otherwise.
+     */
+    public boolean cancelLoad() {
+        if (DEBUG) Log.v(TAG, "cancelLoad: mTask=" + mTask);
+        if (mTask != null) {
+            if (mCancellingTask != null) {
+                // There was a pending task already waiting for a previous
+                // one being canceled; just drop it.
+                if (DEBUG) Log.v(TAG,
+                        "cancelLoad: still waiting for cancelled task; dropping next");
+                if (mTask.waiting) {
+                    mTask.waiting = false;
+                    mHandler.removeCallbacks(mTask);
+                }
+                mTask = null;
+                return false;
+            } else if (mTask.waiting) {
+                // There is a task, but it is waiting for the time it should
+                // execute.  We can just toss it.
+                if (DEBUG) Log.v(TAG, "cancelLoad: task is waiting, dropping it");
+                mTask.waiting = false;
+                mHandler.removeCallbacks(mTask);
+                mTask = null;
+                return false;
+            } else {
+                boolean cancelled = mTask.cancel(false);
+                if (DEBUG) Log.v(TAG, "cancelLoad: cancelled=" + cancelled);
+                if (cancelled) {
+                    mCancellingTask = mTask;
+                }
+                mTask = null;
+                return cancelled;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Called if the task was canceled before it was completed.  Gives the class a chance
+     * to properly dispose of the result.
+     */
+    public void onCanceled(D data) {
+    }
+
+    void executePendingTask() {
+        if (mCancellingTask == null && mTask != null) {
+            if (mTask.waiting) {
+                mTask.waiting = false;
+                mHandler.removeCallbacks(mTask);
+            }
+            if (mUpdateThrottle > 0) {
+                long now = SystemClock.uptimeMillis();
+                if (now < (mLastLoadCompleteTime+mUpdateThrottle)) {
+                    // Not yet time to do another load.
+                    if (DEBUG) Log.v(TAG, "Waiting until "
+                            + (mLastLoadCompleteTime+mUpdateThrottle)
+                            + " to execute: " + mTask);
+                    mTask.waiting = true;
+                    mHandler.postAtTime(mTask, mLastLoadCompleteTime+mUpdateThrottle);
+                    return;
+                }
+            }
+            if (DEBUG) Log.v(TAG, "Executing: " + mTask);
+            mTask.execute((Void[]) null);
+            // XXX TO DO: use reflection to call this version.
+            //mTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
+        }
+    }
+
+    void dispatchOnCancelled(LoadTask task, D data) {
+        onCanceled(data);
+        if (mCancellingTask == task) {
+            if (DEBUG) Log.v(TAG, "Cancelled task is now canceled!");
+            mLastLoadCompleteTime = SystemClock.uptimeMillis();
+            mCancellingTask = null;
+            executePendingTask();
+        }
+    }
+
+    void dispatchOnLoadComplete(LoadTask task, D data) {
+        if (mTask != task) {
+            if (DEBUG) Log.v(TAG, "Load complete of old task, trying to cancel");
+            dispatchOnCancelled(task, data);
+        } else {
+            if (isAbandoned()) {
+                // This cursor has been abandoned; just cancel the new data.
+                onCanceled(data);
+            } else {
+                mLastLoadCompleteTime = SystemClock.uptimeMillis();
+                mTask = null;
+                if (DEBUG) Log.v(TAG, "Delivering result");
+                deliverResult(data);
+            }
+        }
+    }
+
+    /**
+     */
+    public abstract D loadInBackground();
+
+    /**
+     * Called on a worker thread to perform the actual load. Implementations should not deliver the
+     * result directly, but should return them from this method, which will eventually end up
+     * calling {@link #deliverResult} on the UI thread. If implementations need to process
+     * the results on the UI thread they may override {@link #deliverResult} and do so
+     * there.
+     *
+     * @return Implementations must return the result of their load operation.
+     */
+    protected D onLoadInBackground() {
+        return loadInBackground();
+    }
+
+    /**
+     * Locks the current thread until the loader completes the current load
+     * operation. Returns immediately if there is no load operation running.
+     * Should not be called from the UI thread: calling it from the UI
+     * thread would cause a deadlock.
+     * <p>
+     * Use for testing only.  <b>Never</b> call this from a UI thread.
+     *
+     * @hide
+     */
+    public void waitForLoader() {
+        LoadTask task = mTask;
+        if (task != null) {
+            try {
+                task.done.await();
+            } catch (InterruptedException e) {
+                // Ignore
+            }
+        }
+    }
+
+    @Override
+    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+        super.dump(prefix, fd, writer, args);
+        if (mTask != null) {
+            writer.print(prefix); writer.print("mTask="); writer.print(mTask);
+                    writer.print(" waiting="); writer.println(mTask.waiting);
+        }
+        if (mCancellingTask != null) {
+            writer.print(prefix); writer.print("mCancellingTask="); writer.print(mCancellingTask);
+                    writer.print(" waiting="); writer.println(mCancellingTask.waiting);
+        }
+        if (mUpdateThrottle != 0) {
+            writer.print(prefix); writer.print("mUpdateThrottle=");
+                    TimeUtils.formatDuration(mUpdateThrottle, writer);
+                    writer.print(" mLastLoadCompleteTime=");
+                    TimeUtils.formatDuration(mLastLoadCompleteTime,
+                            SystemClock.uptimeMillis(), writer);
+                    writer.println();
+        }
+    }
+}