add OI File Manager and AndroidSupportV2 used by it
[android_pandora.git] / apps / AndroidSupportV2 / src / android / support / v2 / content / Loader.java
diff --git a/apps/AndroidSupportV2/src/android/support/v2/content/Loader.java b/apps/AndroidSupportV2/src/android/support/v2/content/Loader.java
new file mode 100644 (file)
index 0000000..63c0005
--- /dev/null
@@ -0,0 +1,358 @@
+/*
+ * 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.database.ContentObserver;
+import android.os.Handler;
+import android.support.v2.util.DebugUtils;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * Static library support version of the framework's {@link android.content.Loader}.
+ * 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 class Loader<D> {
+    int mId;
+    OnLoadCompleteListener<D> mListener;
+    Context mContext;
+    boolean mStarted = false;
+    boolean mAbandoned = false;
+    boolean mReset = true;
+    boolean mContentChanged = false;
+
+    public final class ForceLoadContentObserver extends ContentObserver {
+        public ForceLoadContentObserver() {
+            super(new Handler());
+        }
+
+        @Override
+        public boolean deliverSelfNotifications() {
+            return true;
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            onContentChanged();
+        }
+    }
+
+    public interface OnLoadCompleteListener<D> {
+        /**
+         * Called on the thread that created the Loader when the load is complete.
+         *
+         * @param loader the loader that completed the load
+         * @param data the result of the load
+         */
+        public void onLoadComplete(Loader<D> loader, D data);
+    }
+
+    /**
+     * Stores away the application context associated with context. Since Loaders can be used
+     * across multiple activities it's dangerous to store the context directly.
+     *
+     * @param context used to retrieve the application context.
+     */
+    public Loader(Context context) {
+        mContext = context.getApplicationContext();
+    }
+
+    /**
+     * Sends the result of the load to the registered listener. Should only be called by subclasses.
+     *
+     * Must be called from the process's main thread.
+     *
+     * @param data the result of the load
+     */
+    public void deliverResult(D data) {
+        if (mListener != null) {
+            mListener.onLoadComplete(this, data);
+        }
+    }
+
+    /**
+     * @return an application context retrieved from the Context passed to the constructor.
+     */
+    public Context getContext() {
+        return mContext;
+    }
+
+    /**
+     * @return the ID of this loader
+     */
+    public int getId() {
+        return mId;
+    }
+
+    /**
+     * Registers a class that will receive callbacks when a load is complete.
+     * The callback will be called on the process's main thread so it's safe to
+     * pass the results to widgets.
+     *
+     * <p>Must be called from the process's main thread.
+     */
+    public void registerListener(int id, OnLoadCompleteListener<D> listener) {
+        if (mListener != null) {
+            throw new IllegalStateException("There is already a listener registered");
+        }
+        mListener = listener;
+        mId = id;
+    }
+
+    /**
+     * Remove a listener that was previously added with {@link #registerListener}.
+     *
+     * Must be called from the process's main thread.
+     */
+    public void unregisterListener(OnLoadCompleteListener<D> listener) {
+        if (mListener == null) {
+            throw new IllegalStateException("No listener register");
+        }
+        if (mListener != listener) {
+            throw new IllegalArgumentException("Attempting to unregister the wrong listener");
+        }
+        mListener = null;
+    }
+
+    /**
+     * Return whether this load has been started.  That is, its {@link #startLoading()}
+     * has been called and no calls to {@link #stopLoading()} or
+     * {@link #reset()} have yet been made.
+     */
+    public boolean isStarted() {
+        return mStarted;
+    }
+
+    /**
+     * Return whether this loader has been abandoned.  In this state, the
+     * loader <em>must not</em> report any new data, and <em>must</em> keep
+     * its last reported data valid until it is finally reset.
+     */
+    public boolean isAbandoned() {
+        return mAbandoned;
+    }
+
+    /**
+     * Return whether this load has been reset.  That is, either the loader
+     * has not yet been started for the first time, or its {@link #reset()}
+     * has been called.
+     */
+    public boolean isReset() {
+        return mReset;
+    }
+
+    /**
+     * Starts an asynchronous load of the Loader's data. When the result
+     * is ready the callbacks will be called on the process's main thread.
+     * If a previous load has been completed and is still valid
+     * the result may be passed to the callbacks immediately.
+     * The loader will monitor the source of
+     * the data set and may deliver future callbacks if the source changes.
+     * Calling {@link #stopLoading} will stop the delivery of callbacks.
+     *
+     * <p>This updates the Loader's internal state so that
+     * {@link #isStarted()} and {@link #isReset()} will return the correct
+     * values, and then calls the implementation's {@link #onStartLoading()}.
+     *
+     * <p>Must be called from the process's main thread.
+     */
+    public final void startLoading() {
+        mStarted = true;
+        mReset = false;
+        mAbandoned = false;
+        onStartLoading();
+    }
+
+    /**
+     * Subclasses must implement this to take care of loading their data,
+     * as per {@link #startLoading()}.  This is not called by clients directly,
+     * but as a result of a call to {@link #startLoading()}.
+     */
+    protected void onStartLoading() {
+    }
+
+    /**
+     * Force an asynchronous load. Unlike {@link #startLoading()} this will ignore a previously
+     * loaded data set and load a new one.  This simply calls through to the
+     * implementation's {@link #onForceLoad()}.  You generally should only call this
+     * when the loader is started -- that is, {@link #isStarted()} returns true.
+     *
+     * <p>Must be called from the process's main thread.
+     */
+    public void forceLoad() {
+        onForceLoad();
+    }
+
+    /**
+     * Subclasses must implement this to take care of requests to {@link #forceLoad()}.
+     * This will always be called from the process's main thread.
+     */
+    protected void onForceLoad() {
+    }
+
+    /**
+     * Stops delivery of updates until the next time {@link #startLoading()} is called.
+     * Implementations should <em>not</em> invalidate their data at this point --
+     * clients are still free to use the last data the loader reported.  They will,
+     * however, typically stop reporting new data if the data changes; they can
+     * still monitor for changes, but must not report them to the client until and
+     * if {@link #startLoading()} is later called.
+     *
+     * <p>This updates the Loader's internal state so that
+     * {@link #isStarted()} will return the correct
+     * value, and then calls the implementation's {@link #onStopLoading()}.
+     *
+     * <p>Must be called from the process's main thread.
+     */
+    public void stopLoading() {
+        mStarted = false;
+        onStopLoading();
+    }
+
+    /**
+     * Subclasses must implement this to take care of stopping their loader,
+     * as per {@link #stopLoading()}.  This is not called by clients directly,
+     * but as a result of a call to {@link #stopLoading()}.
+     * This will always be called from the process's main thread.
+     */
+    protected void onStopLoading() {
+    }
+
+    /**
+     * Tell the Loader that it is being abandoned.  This is called prior
+     * to {@link #reset} to have it retain its current data but not report
+     * any new data.
+     */
+    public void abandon() {
+        mAbandoned = true;
+        onAbandon();
+    }
+    
+    /**
+     * Subclasses implement this to take care of being abandoned.  This is
+     * an optional intermediate state prior to {@link #onReset()} -- it means that
+     * the client is no longer interested in any new data from the loader,
+     * so the loader must not report any further updates.  However, the
+     * loader <em>must</em> keep its last reported data valid until the final
+     * {@link #onReset()} happens.  You can retrieve the current abandoned
+     * state with {@link #isAbandoned}.
+     */
+    protected void onAbandon() {        
+    }
+    
+    /**
+     * Resets the state of the Loader.  The Loader should at this point free
+     * all of its resources, since it may never be called again; however, its
+     * {@link #startLoading()} may later be called at which point it must be
+     * able to start running again.
+     *
+     * <p>This updates the Loader's internal state so that
+     * {@link #isStarted()} and {@link #isReset()} will return the correct
+     * values, and then calls the implementation's {@link #onReset()}.
+     *
+     * <p>Must be called from the process's main thread.
+     */
+    public void reset() {
+        onReset();
+        mReset = true;
+        mStarted = false;
+        mAbandoned = false;
+        mContentChanged = false;
+    }
+
+    /**
+     * Subclasses must implement this to take care of resetting their loader,
+     * as per {@link #reset()}.  This is not called by clients directly,
+     * but as a result of a call to {@link #reset()}.
+     * This will always be called from the process's main thread.
+     */
+    protected void onReset() {
+    }
+
+    /**
+     * Take the current flag indicating whether the loader's content had
+     * changed while it was stopped.  If it had, true is returned and the
+     * flag is cleared.
+     */
+    public boolean takeContentChanged() {
+        boolean res = mContentChanged;
+        mContentChanged = false;
+        return res;
+    }
+    
+    /**
+     * Called when {@link ForceLoadContentObserver} detects a change.  The
+     * default implementation checks to see if the loader is currently started;
+     * if so, it simply calls {@link #forceLoad()}; otherwise, it sets a flag
+     * so that {@link #takeContentChanged()} returns true.
+     *
+     * <p>Must be called from the process's main thread.
+     */
+    public void onContentChanged() {
+        if (mStarted) {
+            forceLoad();
+        } else {
+            // This loader has been stopped, so we don't want to load
+            // new data right now...  but keep track of it changing to
+            // refresh later if we start again.
+            mContentChanged = true;
+        }
+    }
+
+    /**
+     * For debugging, converts an instance of the Loader's data class to
+     * a string that can be printed.  Must handle a null data.
+     */
+    public String dataToString(D data) {
+        StringBuilder sb = new StringBuilder(64);
+        DebugUtils.buildShortClassTag(data, sb);
+        sb.append("}");
+        return sb.toString();
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder(64);
+        DebugUtils.buildShortClassTag(this, sb);
+        sb.append(" id=");
+        sb.append(mId);
+        sb.append("}");
+        return sb.toString();
+    }
+
+    /**
+     * Print the Loader's state into the given stream.
+     *
+     * @param prefix Text to print at the front of each line.
+     * @param fd The raw file descriptor that the dump is being sent to.
+     * @param writer A PrintWriter to which the dump is to be set.
+     * @param args Additional arguments to the dump request.
+     */
+    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+        writer.print(prefix); writer.print("mId="); writer.print(mId);
+                writer.print(" mListener="); writer.println(mListener);
+        writer.print(prefix); writer.print("mStarted="); writer.print(mStarted);
+                writer.print(" mContentChanged="); writer.print(mContentChanged);
+                writer.print(" mAbandoned="); writer.print(mAbandoned);
+                writer.print(" mReset="); writer.println(mReset);
+    }
+}
\ No newline at end of file