Copying (and modifying) some files from packages/experimental

Change-Id: I00ba3c5b05df4320db51cc659fd84f6b37486d6d
diff --git a/src/android/app/patterns/LoaderManagingFragment.java b/src/android/app/patterns/LoaderManagingFragment.java
new file mode 100644
index 0000000..32e3bf3
--- /dev/null
+++ b/src/android/app/patterns/LoaderManagingFragment.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * 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.app.patterns;
+
+import android.app.Fragment;
+import android.os.Bundle;
+
+import java.util.HashMap;
+
+public abstract class LoaderManagingFragment<D> extends Fragment
+        implements Loader.OnLoadCompleteListener<D> {
+    private boolean mStarted = false;
+
+    static final class LoaderInfo<D> {
+        public Bundle args;
+        public Loader<D> loader;
+    }
+    private HashMap<Integer, LoaderInfo<D>> mLoaders;
+    private HashMap<Integer, LoaderInfo<D>> mInactiveLoaders;
+
+    /**
+     * Registers a loader with this activity, registers the callbacks on it, and starts it loading.
+     * If a loader with the same id has previously been started it will automatically be destroyed
+     * when the new loader completes it's work. The callback will be delivered before the old loader
+     * is destroyed.
+     */
+    protected void startLoading(int id, Bundle args) {
+        LoaderInfo<D> info = mLoaders.get(id);
+        if (info != null) {
+            // Keep track of the previous instance of this loader so we can destroy
+            // it when the new one completes.
+            mInactiveLoaders.put(id, info);
+        }
+        info = new LoaderInfo<D>();
+        info.args = args;
+        mLoaders.put(id, info);
+        Loader<D> loader = onCreateLoader(id, args);
+        info.loader = loader;
+        if (mStarted) {
+            // The activity will start all existing loaders in it's onStart(), so only start them
+            // here if we're past that point of the activitiy's life cycle
+            loader.registerListener(id, this);
+            loader.startLoading();
+        }
+    }
+
+    protected abstract Loader<D> onCreateLoader(int id, Bundle args);
+    protected abstract void onInitializeLoaders();
+    protected abstract void onLoadFinished(Loader<D> loader, D data);
+
+    public final void onLoadComplete(Loader<D> loader, D data) {
+        // Notify of the new data so the app can switch out the old data before
+        // we try to destroy it.
+        onLoadFinished(loader, data);
+
+        // Look for an inactive loader and destroy it if found
+        int id = loader.getId();
+        LoaderInfo<D> info = mInactiveLoaders.get(id);
+        if (info != null) {
+            Loader<D> oldLoader = info.loader;
+            if (oldLoader != null) {
+                oldLoader.destroy();
+            }
+            mInactiveLoaders.remove(id);
+        }
+    }
+
+    @Override
+    public void onCreate(Bundle savedState) {
+        super.onCreate(savedState);
+
+        if (mLoaders == null) {
+            // Look for a passed along loader and create a new one if it's not there
+// TODO: uncomment once getLastNonConfigurationInstance method is available
+//            mLoaders = (HashMap<Integer, LoaderInfo>) getLastNonConfigurationInstance();
+            if (mLoaders == null) {
+                mLoaders = new HashMap<Integer, LoaderInfo<D>>();
+                onInitializeLoaders();
+            }
+        }
+        if (mInactiveLoaders == null) {
+            mInactiveLoaders = new HashMap<Integer, LoaderInfo<D>>();
+        }
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+
+        // Call out to sub classes so they can start their loaders
+        // Let the existing loaders know that we want to be notified when a load is complete
+        for (HashMap.Entry<Integer, LoaderInfo<D>> entry : mLoaders.entrySet()) {
+            LoaderInfo<D> info = entry.getValue();
+            Loader<D> loader = info.loader;
+            int id = entry.getKey();
+            if (loader == null) {
+               loader = onCreateLoader(id, info.args);
+               info.loader = loader;
+            }
+            loader.registerListener(id, this);
+            loader.startLoading();
+        }
+
+        mStarted = true;
+    }
+
+    @Override
+    public void onStop() {
+        super.onStop();
+
+        for (HashMap.Entry<Integer, LoaderInfo<D>> entry : mLoaders.entrySet()) {
+            LoaderInfo<D> info = entry.getValue();
+            Loader<D> loader = info.loader;
+            if (loader == null) {
+                continue;
+            }
+
+            // Let the loader know we're done with it
+            loader.unregisterListener(this);
+
+            // The loader isn't getting passed along to the next instance so ask it to stop loading
+// TODO: uncomment once isChangingConfig method is available
+//            if (!getActivity().isChangingConfigurations()) {
+//                loader.stopLoading();
+//            }
+        }
+
+        mStarted = false;
+    }
+
+    @Override
+    public Object onRetainNonConfigurationInstance() {
+        // Pass the loader along to the next guy
+        Object result = mLoaders;
+        mLoaders = null;
+        return result;
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+
+        if (mLoaders != null) {
+            for (HashMap.Entry<Integer, LoaderInfo<D>> entry : mLoaders.entrySet()) {
+                LoaderInfo<D> info = entry.getValue();
+                Loader<D> loader = info.loader;
+                if (loader == null) {
+                    continue;
+                }
+                loader.destroy();
+            }
+        }
+    }
+}