More cleanup of Loader APIs.

- Remove old method names.
- Introduce onXxx() hooks to Loader.
- Improve debugging.

Change-Id: I3fba072a05c7023aa7d2c3eb4e126feb514ab6d8
diff --git a/core/java/android/content/Loader.java b/core/java/android/content/Loader.java
index 73d7103..f425b29 100644
--- a/core/java/android/content/Loader.java
+++ b/core/java/android/content/Loader.java
@@ -18,18 +18,27 @@
 
 import android.database.ContentObserver;
 import android.os.Handler;
+import android.util.DebugUtils;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 
 /**
  * An abstract class that performs asynchronous loading of data. While Loaders are active
  * they should monitor the source of their data and deliver new results when the contents
  * change.
  *
+ * <p>Subclasses generally must implement at least {@link #onStartLoading()},
+ * {@link #onStopLoading()}, {@link #onForceLoad()}, and {@link #onReset()}.
+ *
  * @param <D> The result returned when the load is complete
  */
-public abstract class Loader<D> {
+public class Loader<D> {
     int mId;
     OnLoadCompleteListener<D> mListener;
     Context mContext;
+    boolean mStarted = false;
+    boolean mReset = true;
 
     public final class ForceLoadContentObserver extends ContentObserver {
         public ForceLoadContentObserver() {
@@ -122,28 +131,88 @@
     }
 
     /**
+     * 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 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 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. 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>Must be called from the UI thread
+     * <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 UI thread.
      */
-    public abstract void startLoading();
+    public void startLoading() {
+        mStarted = true;
+        mReset = 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.
+     * loaded data set and load a new one.  This simply calls through to the
+     * implementation's {@link #onForceLoad()}.
+     *
+     * <p>Must be called from the UI thread.
      */
-    public abstract void forceLoad();
+    public void forceLoad() {
+        onForceLoad();
+    }
 
     /**
-     * Stops delivery of updates until the next time {@link #startLoading()} is called
-     *
-     * <p>Must be called from the UI thread
+     * Subclasses must implement this to take care of requests to {@link #forceLoad()}.
      */
-    public abstract void stopLoading();
+    protected void onForceLoad() {
+    }
+
+    /**
+     * Stops delivery of updates until the next time {@link #startLoading()} is 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 UI 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()}.
+     */
+    protected void onStopLoading() {
+    }
 
     /**
      * Resets the state of the Loader.  The Loader should at this point free
@@ -151,17 +220,24 @@
      * {@link #startLoading()} may later be called at which point it must be
      * able to start running again.
      *
-     * <p>Must be called from the UI thread
+     * <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 UI thread.
      */
     public void reset() {
-        destroy();
+        onReset();
+        mReset = true;
+        mStarted = false;
     }
 
     /**
-     * @deprecated Old API, implement reset() now.
+     * 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()}.
      */
-    @Deprecated
-    public void destroy() {
+    protected void onReset() {
     }
 
     /**
@@ -173,4 +249,40 @@
     public void onContentChanged() {
         forceLoad();
     }
+
+    /**
+     * 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(" mReset="); writer.println(mReset);
+    }
 }
\ No newline at end of file