Copying (and modifying) some files from packages/experimental
Change-Id: I00ba3c5b05df4320db51cc659fd84f6b37486d6d
diff --git a/src/android/app/patterns/AsyncTaskLoader.java b/src/android/app/patterns/AsyncTaskLoader.java
index 01b3e24..84effaf 100644
--- a/src/android/app/patterns/AsyncTaskLoader.java
+++ b/src/android/app/patterns/AsyncTaskLoader.java
@@ -21,7 +21,7 @@
/**
* Abstract Loader that provides an {@link AsyncTask} to do the work.
- *
+ *
* @param <D> the data type to be loaded.
*/
public abstract class AsyncTaskLoader<D> extends Loader<D> {
@@ -35,15 +35,48 @@
/* Runs on the UI thread */
@Override
protected void onPostExecute(D data) {
- AsyncTaskLoader.this.onLoadComplete(data);
+ AsyncTaskLoader.this.dispatchOnLoadComplete(data);
}
}
+ private LoadListTask mTask;
+
public AsyncTaskLoader(Context context) {
super(context);
}
/**
+ * Force an asynchronous load. Unlike {@link #startLoading()} this will ignore a previously
+ * loaded data set and load a new one.
+ */
+ @Override
+ public void forceLoad() {
+ mTask = new LoadListTask();
+ mTask.execute((Void[]) null);
+ }
+
+ /**
+ * Attempt to cancel the current load task. See {@link AsyncTask#cancel(boolean)}
+ * for more info.
+ *
+ * @return <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, and
+ * <tt>true</tt> otherwise
+ */
+ public boolean cancelLoad() {
+ if (mTask != null) {
+ return mTask.cancel(false);
+ }
+ return false;
+ }
+
+ private void dispatchOnLoadComplete(D data) {
+ mTask = null;
+ onLoadComplete(data);
+ }
+
+ /**
* Called on a worker thread to perform the actual load. Implementions should not deliver the
* results directly, but should return them from this this method and deliver them from
* {@link #onPostExecute()}
diff --git a/src/android/app/patterns/CursorLoader.java b/src/android/app/patterns/CursorLoader.java
index a1f8132..e7b2e41 100644
--- a/src/android/app/patterns/CursorLoader.java
+++ b/src/android/app/patterns/CursorLoader.java
@@ -5,7 +5,7 @@
* 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
+ * 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,
@@ -69,7 +69,7 @@
/**
* 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 result may be passed to the callbacks immediately.
*
* Must be called from the UI thread
*/
@@ -85,15 +85,6 @@
}
/**
- * Force an asynchronous load. Unlike {@link #startLoading()} this will ignore a previously
- * loaded data set and load a new one.
- */
- @Override
- public void forceLoad() {
- new LoadListTask().execute((Void[]) null);
- }
-
- /**
* Must be called from the UI thread
*/
@Override
@@ -103,6 +94,9 @@
mCursor = null;
}
+ // Attempt to cancel the current load task if possible.
+ cancelLoad();
+
// Make sure that any outstanding loads clean themselves up properly
mStopped = true;
}
@@ -112,4 +106,36 @@
// Ensure the loader is stopped
stopLoading();
}
+
+ public Uri getUri() {
+ return mUri;
+ }
+
+ public void setUri(Uri uri) {
+ mUri = uri;
+ }
+
+ public String[] getProjection() {
+ return mProjection;
+ }
+
+ public void setProjection(String[] projection) {
+ mProjection = projection;
+ }
+
+ public String getSelection() {
+ return mSelection;
+ }
+
+ public void setSelection(String selection) {
+ mSelection = selection;
+ }
+
+ public String[] getSelectionArgs() {
+ return mSelectionArgs;
+ }
+
+ public void setSelectionArgs(String[] selectionArgs) {
+ mSelectionArgs = selectionArgs;
+ }
}
diff --git a/src/android/app/patterns/Loader.java b/src/android/app/patterns/Loader.java
index dfc35e9..18c64f3 100644
--- a/src/android/app/patterns/Loader.java
+++ b/src/android/app/patterns/Loader.java
@@ -48,7 +48,7 @@
* @param loader the loader that completed the load
* @param data the result of the load
*/
- public void onLoadComplete(Loader loader, D data);
+ public void onLoadComplete(Loader<D> loader, D data);
}
/**
@@ -89,7 +89,7 @@
/**
* Registers a class that will receive callbacks when a load is complete. The callbacks will
* be called on the UI thread so it's safe to pass the results to widgets.
- *
+ *
* Must be called from the UI thread
*/
public void registerListener(int id, OnLoadCompleteListener<D> listener) {
@@ -118,7 +118,7 @@
* 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.
+ * {@link #stopLoading} will stop the delivery of callbacks.
*
* Must be called from the UI thread
*/
diff --git a/src/android/app/patterns/LoaderActivity.java b/src/android/app/patterns/LoaderActivity.java
index 664a8bc..bcb3692 100644
--- a/src/android/app/patterns/LoaderActivity.java
+++ b/src/android/app/patterns/LoaderActivity.java
@@ -30,12 +30,12 @@
Loader.OnLoadCompleteListener<D> {
private boolean mStarted = false;
- static final class LoaderInfo {
+ static final class LoaderInfo<D> {
public Bundle args;
- public Loader loader;
+ public Loader<D> loader;
}
- private HashMap<Integer, LoaderInfo> mLoaders;
- private HashMap<Integer, LoaderInfo> mInactiveLoaders;
+ 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.
@@ -44,16 +44,16 @@
* is destroyed.
*/
protected void startLoading(int id, Bundle args) {
- LoaderInfo info = mLoaders.get(id);
+ 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();
+ info = new LoaderInfo<D>();
info.args = args;
mLoaders.put(id, info);
- Loader loader = onCreateLoader(id, args);
+ 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
@@ -63,20 +63,20 @@
}
}
- protected abstract Loader onCreateLoader(int id, Bundle args);
+ protected abstract Loader<D> onCreateLoader(int id, Bundle args);
protected abstract void onInitializeLoaders();
- protected abstract void onLoadFinished(Loader loader, D data);
+ protected abstract void onLoadFinished(Loader<D> loader, D data);
- public final void onLoadComplete(Loader 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 info = mInactiveLoaders.get(id);
+ LoaderInfo<D> info = mInactiveLoaders.get(id);
if (info != null) {
- Loader oldLoader = info.loader;
+ Loader<D> oldLoader = info.loader;
if (oldLoader != null) {
oldLoader.destroy();
}
@@ -90,14 +90,14 @@
if (mLoaders == null) {
// Look for a passed along loader and create a new one if it's not there
- mLoaders = (HashMap<Integer, LoaderInfo>) getLastNonConfigurationInstance();
+ mLoaders = (HashMap<Integer, LoaderInfo<D>>) getLastNonConfigurationInstance();
if (mLoaders == null) {
- mLoaders = new HashMap<Integer, LoaderInfo>();
+ mLoaders = new HashMap<Integer, LoaderInfo<D>>();
onInitializeLoaders();
}
}
if (mInactiveLoaders == null) {
- mInactiveLoaders = new HashMap<Integer, LoaderInfo>();
+ mInactiveLoaders = new HashMap<Integer, LoaderInfo<D>>();
}
}
@@ -107,9 +107,9 @@
// 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> entry : mLoaders.entrySet()) {
- LoaderInfo info = entry.getValue();
- Loader loader = info.loader;
+ 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);
@@ -126,9 +126,9 @@
public void onStop() {
super.onStop();
- for (HashMap.Entry<Integer, LoaderInfo> entry : mLoaders.entrySet()) {
- LoaderInfo info = entry.getValue();
- Loader loader = info.loader;
+ for (HashMap.Entry<Integer, LoaderInfo<D>> entry : mLoaders.entrySet()) {
+ LoaderInfo<D> info = entry.getValue();
+ Loader<D> loader = info.loader;
if (loader == null) {
continue;
}
@@ -158,9 +158,9 @@
super.onDestroy();
if (mLoaders != null) {
- for (HashMap.Entry<Integer, LoaderInfo> entry : mLoaders.entrySet()) {
- LoaderInfo info = entry.getValue();
- Loader loader = info.loader;
+ for (HashMap.Entry<Integer, LoaderInfo<D>> entry : mLoaders.entrySet()) {
+ LoaderInfo<D> info = entry.getValue();
+ Loader<D> loader = info.loader;
if (loader == null) {
continue;
}
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();
+ }
+ }
+ }
+}