Update to new MVC framework
Change-Id: I792037f910de24b30f5b33e1b3f789c69a854ff5
diff --git a/src/com/android/contacts/activities/ContactDetailActivity.java b/src/com/android/contacts/activities/ContactDetailActivity.java
index 5dabdc7..bc2d39d 100644
--- a/src/com/android/contacts/activities/ContactDetailActivity.java
+++ b/src/com/android/contacts/activities/ContactDetailActivity.java
@@ -19,11 +19,10 @@
import com.android.contacts.ContactsSearchManager;
import com.android.contacts.R;
import com.android.contacts.mvcframework.DialogManager;
+import com.android.contacts.mvcframework.LoaderActivity;
import com.android.contacts.views.detail.ContactDetailView;
import com.android.contacts.views.detail.ContactLoader;
-import com.android.contacts.views.detail.ContactLoader.Result;
-import android.app.Activity;
import android.app.Dialog;
import android.net.Uri;
import android.os.Bundle;
@@ -32,11 +31,10 @@
import android.view.Menu;
import android.view.MenuItem;
-public class ContactDetailActivity extends Activity implements ContactLoader.Callbacks,
+public class ContactDetailActivity extends LoaderActivity<ContactLoader.Result> implements
DialogManager.DialogShowingViewActivity {
+ private static final int LOADER_DETAILS = 1;
private ContactDetailView mDetails;
- private ContactLoader mLoader;
- private Uri mUri;
private DialogManager mDialogManager;
private static final String TAG = "ContactDetailActivity";
@@ -54,66 +52,39 @@
mDetails = (ContactDetailView) findViewById(R.id.contact_details);
mDetails.setCallbacks(new ContactDetailView.DefaultCallbacks(this));
-
- mUri = getIntent().getData();
}
@Override
- public void onStart() {
- super.onStart();
+ public void onInitializeLoaders() {
+ startLoading(LOADER_DETAILS, null);
+ }
- if (mLoader == null) {
- // Look for a passed along loader and create a new one if it's not there
- mLoader = (ContactLoader) getLastNonConfigurationInstance();
- if (mLoader == null) {
- mLoader = new ContactLoader(this, mUri);
+ @Override
+ protected ContactLoader onCreateLoader(int id, Bundle args) {
+ switch (id) {
+ case LOADER_DETAILS: {
+ return new ContactLoader(this, getIntent().getData());
}
}
- mLoader.registerCallbacks(this);
- mLoader.startLoading();
+ return null;
}
- @Override
- public void onStop() {
- super.onStop();
-
- // Let the loader know we're done with it
- mLoader.unregisterCallbacks(this);
-
- // The loader isn't getting passed along to the next instance so ask it to stop loading
- // TODO: Readd this once we have framework support
- /*if (!isChangingConfigurations()) {
- mLoader.stopLoading();
- }*/
- }
@Override
- public Object onRetainNonConfigurationInstance() {
- // Pass the loader along to the next guy
- Object result = mLoader;
- mLoader = null;
- return result;
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
-
- if (mLoader != null) {
- mLoader.destroy();
+ public void onLoadComplete(int id, ContactLoader.Result data) {
+ switch (id) {
+ case LOADER_DETAILS:
+ if (data == ContactLoader.Result.NOT_FOUND) {
+ // Item has been deleted
+ Log.i(TAG, "No contact found. Closing activity");
+ finish();
+ return;
+ }
+ mDetails.setData(data);
+ break;
}
}
- public void onContactLoaded(Result contact) {
- if (contact == ContactLoader.Result.NOT_FOUND) {
- // Item has been deleted
- Log.i(TAG, "No contact found. Closing activity");
- finish();
- return;
- }
- mDetails.setData(contact);
- }
-
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// TODO: This is too hardwired.
diff --git a/src/com/android/contacts/mvcframework/CursorLoader.java b/src/com/android/contacts/mvcframework/CursorLoader.java
index 89ae997..25585be 100644
--- a/src/com/android/contacts/mvcframework/CursorLoader.java
+++ b/src/com/android/contacts/mvcframework/CursorLoader.java
@@ -20,19 +20,10 @@
import android.database.Cursor;
import android.os.AsyncTask;
-public abstract class CursorLoader extends Loader<CursorLoader.Callbacks> {
- private Context mContext;
- private Cursor mCursor;
- private ForceLoadContentObserver mObserver;
- private boolean mClosed;
-
- public interface Callbacks {
- public void onCursorLoaded(Cursor cursor);
- }
-
- protected Context getContext() {
- return mContext;
- }
+public abstract class CursorLoader extends Loader<Cursor> {
+ Cursor mCursor;
+ ForceLoadContentObserver mObserver;
+ boolean mClosed;
final class LoadListTask extends AsyncTask<Void, Void, Cursor> {
/* Runs on a worker thread */
@@ -56,15 +47,12 @@
return;
}
mCursor = cursor;
- if (mCallbacks != null) {
- // A listener is register, notify them of the result
- mCallbacks.onCursorLoaded(cursor);
- }
+ deliverResult(cursor);
}
}
public CursorLoader(Context context) {
- mContext = context.getApplicationContext();
+ super(context);
mObserver = new ForceLoadContentObserver();
}
@@ -78,7 +66,7 @@
@Override
public void startLoading() {
if (mCursor != null) {
- mCallbacks.onCursorLoaded(mCursor);
+ deliverResult(mCursor);
} else {
forceLoad();
}
diff --git a/src/com/android/contacts/mvcframework/Loader.java b/src/com/android/contacts/mvcframework/Loader.java
index 456b53a..fab2f46 100644
--- a/src/com/android/contacts/mvcframework/Loader.java
+++ b/src/com/android/contacts/mvcframework/Loader.java
@@ -16,11 +16,14 @@
package com.android.contacts.mvcframework;
+import android.content.Context;
import android.database.ContentObserver;
import android.os.Handler;
-public abstract class Loader<E> {
- protected E mCallbacks;
+public abstract class Loader<D> {
+ private int mId;
+ private OnLoadCompleteListener<D> mListener;
+ private Context mContext;
protected final class ForceLoadContentObserver extends ContentObserver {
public ForceLoadContentObserver() {
@@ -38,30 +41,53 @@
}
}
+ public interface OnLoadCompleteListener<D> {
+ public void onLoadComplete(int id, D data);
+ }
+
+ protected void deliverResult(D data) {
+ if (mListener != null) {
+ mListener.onLoadComplete(mId, data);
+
+ }
+ }
+
+ public Loader(Context context) {
+ mContext = context.getApplicationContext();
+ }
+
+ /**
+ * @return an application context
+ */
+ public Context getContext() {
+ return mContext;
+ }
+
/**
* 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 registerCallbacks(E callbacks) {
- if (mCallbacks != null) {
- throw new IllegalStateException("There are already callbacks registered");
- }
- mCallbacks = callbacks;
+ public void registerListener(int id, OnLoadCompleteListener<D> listener) {
+// if (mListener != null) {
+ // throw new IllegalStateException("There is already a listener registered");
+ // }
+ mListener = listener;
+ mId = id;
}
/**
* Must be called from the UI thread
*/
- public void unregisterCallbacks(E callbacks) {
- if (mCallbacks == null) {
- throw new IllegalStateException("No callbacks register");
+ public void unregisterListener(OnLoadCompleteListener<D> listener) {
+ if (mListener == null) {
+ throw new IllegalStateException("No listener register");
}
- if (mCallbacks != callbacks) {
- throw new IllegalArgumentException("Attempting to unregister the wrong callbacks");
+ if (mListener != listener) {
+ throw new IllegalArgumentException("Attempting to unregister the wrong listener");
}
- mCallbacks = null;
+ mListener = null;
}
/**
diff --git a/src/com/android/contacts/mvcframework/LoaderActivity.java b/src/com/android/contacts/mvcframework/LoaderActivity.java
new file mode 100644
index 0000000..0e74d62
--- /dev/null
+++ b/src/com/android/contacts/mvcframework/LoaderActivity.java
@@ -0,0 +1,144 @@
+/*
+ * 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 com.android.contacts.mvcframework;
+
+
+import android.app.Activity;
+import android.os.Bundle;
+
+import java.util.HashMap;
+
+/**
+ * The idea here was to abstract the generic life cycle junk needed to properly keep loaders going.
+ * It didn't work out as-is because registering the callbacks post config change didn't work.
+ */
+public abstract class LoaderActivity<D> extends Activity implements
+ Loader.OnLoadCompleteListener<D> {
+ static final class LoaderInfo {
+ public Bundle args;
+ public Loader loader;
+ }
+ private HashMap<Integer, LoaderInfo> mLoaders;
+
+ /**
+ * Registers a loader with this activity, registers the callbacks on it, and starts it loading.
+ */
+ protected void startLoading(int id, Bundle args) {
+ LoaderInfo info = mLoaders.get(id);
+ Loader loader;
+ if (info != null) {
+ loader = info.loader;
+ if (loader != null) {
+ loader.unregisterListener(this);
+ loader.destroy();
+ info.loader = null;
+ }
+ } else {
+ info = new LoaderInfo();
+ info.args = args;
+ }
+ mLoaders.put(id, info);
+ loader = onCreateLoader(id, args);
+ loader.registerListener(id, this);
+ loader.startLoading();
+ info.loader = loader;
+ }
+
+ protected abstract Loader onCreateLoader(int id, Bundle args);
+ protected abstract void onInitializeLoaders();
+
+ public abstract void onLoadComplete(int id, D data);
+
+ @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
+ mLoaders = (HashMap<Integer, LoaderInfo>) getLastNonConfigurationInstance();
+ if (mLoaders == null) {
+ mLoaders = new HashMap<Integer, LoaderInfo>();
+ onInitializeLoaders();
+ }
+ }
+ }
+
+ @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> entry : mLoaders.entrySet()) {
+ LoaderInfo info = entry.getValue();
+ Loader loader = info.loader;
+ int id = entry.getKey();
+ if (loader == null) {
+ loader = onCreateLoader(id, info.args);
+ info.loader = loader;
+ } else {
+ loader.registerListener(id, this);
+ }
+ loader.startLoading();
+ }
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+
+ for (HashMap.Entry<Integer, LoaderInfo> entry : mLoaders.entrySet()) {
+ LoaderInfo info = entry.getValue();
+ Loader 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
+// if (!isChangingConfigurations()) {
+// loader.stopLoading();
+// }
+ }
+ }
+
+ @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> entry : mLoaders.entrySet()) {
+ LoaderInfo info = entry.getValue();
+ Loader loader = info.loader;
+ if (loader == null) {
+ continue;
+ }
+ loader.destroy();
+ }
+ }
+ }
+}
diff --git a/src/com/android/contacts/views/detail/ContactLoader.java b/src/com/android/contacts/views/detail/ContactLoader.java
index 38e7a5d..4528387 100644
--- a/src/com/android/contacts/views/detail/ContactLoader.java
+++ b/src/com/android/contacts/views/detail/ContactLoader.java
@@ -42,8 +42,7 @@
/**
* Loads a single Contact and all it constituent RawContacts.
*/
-public class ContactLoader extends Loader<ContactLoader.Callbacks> {
- private Context mContext;
+public class ContactLoader extends Loader<ContactLoader.Result> {
private Uri mLookupUri;
private Result mContact;
private ForceLoadContentObserver mObserver;
@@ -141,7 +140,7 @@
final class LoadContactTask extends AsyncTask<Void, Void, Result> {
@Override
protected Result doInBackground(Void... args) {
- final ContentResolver resolver = mContext.getContentResolver();
+ final ContentResolver resolver = getContext().getContentResolver();
Result result = loadContactHeaderData(resolver, mLookupUri);
if (result == Result.NOT_FOUND) {
// No record found. Try to lookup up a new record with the same lookupKey.
@@ -312,21 +311,22 @@
mObserver = new ForceLoadContentObserver();
}
Log.i(TAG, "Registering content observer for " + mLookupUri);
- mContext.getContentResolver().registerContentObserver(mLookupUri, true, mObserver);
- mCallbacks.onContactLoaded(result);
+ getContext().getContentResolver().registerContentObserver(
+ mLookupUri, true, mObserver);
+ deliverResult(result);
}
}
}
public ContactLoader(Context context, Uri lookupUri) {
- mContext = context.getApplicationContext();
+ super(context);
mLookupUri = lookupUri;
}
@Override
public void startLoading() {
if (mContact != null) {
- mCallbacks.onContactLoaded(mContact);
+ deliverResult(mContact);
} else {
forceLoad();
}
@@ -341,7 +341,7 @@
public void stopLoading() {
mContact = null;
if (mObserver != null) {
- mContext.getContentResolver().unregisterContentObserver(mObserver);
+ getContext().getContentResolver().unregisterContentObserver(mObserver);
}
}