Refactor to coupler/presenter pattern. Moved framework classes to correct packages
Bug:2579760
Change-Id: Ia0c475e55830aa2d8b13cdb2685fc0981b6f6ade
diff --git a/res/layout/contact_detail.xml b/res/layout/contact_detail.xml
index 541ba1c..b4f8101 100644
--- a/res/layout/contact_detail.xml
+++ b/res/layout/contact_detail.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
-<com.android.contacts.views.detail.ContactDetailView xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/contact_details"
android:orientation="vertical"
android:layout_width="match_parent"
@@ -49,5 +49,5 @@
/>
</ScrollView>
-</com.android.contacts.views.detail.ContactDetailView>
+</LinearLayout>
diff --git a/src/android/app/patterns/AsyncTaskLoader.java b/src/android/app/patterns/AsyncTaskLoader.java
new file mode 100644
index 0000000..01b3e24
--- /dev/null
+++ b/src/android/app/patterns/AsyncTaskLoader.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2010 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.app.patterns;
+
+import android.content.Context;
+import android.os.AsyncTask;
+
+/**
+ * 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> {
+ final class LoadListTask extends AsyncTask<Void, Void, D> {
+ /* Runs on a worker thread */
+ @Override
+ protected D doInBackground(Void... params) {
+ return AsyncTaskLoader.this.loadInBackground();
+ }
+
+ /* Runs on the UI thread */
+ @Override
+ protected void onPostExecute(D data) {
+ AsyncTaskLoader.this.onLoadComplete(data);
+ }
+ }
+
+ public AsyncTaskLoader(Context context) {
+ super(context);
+ }
+
+ /**
+ * 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()}
+ *
+ * @return the result of the load
+ */
+ protected abstract D loadInBackground();
+
+ /**
+ * Called on the UI thread with the result of the load.
+ *
+ * @param data the result of the load
+ */
+ protected abstract void onLoadComplete(D data);
+}
diff --git a/src/android/app/patterns/CursorLoader.java b/src/android/app/patterns/CursorLoader.java
new file mode 100644
index 0000000..a1f8132
--- /dev/null
+++ b/src/android/app/patterns/CursorLoader.java
@@ -0,0 +1,115 @@
+/*
+ * 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.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+
+public class CursorLoader extends AsyncTaskLoader<Cursor> {
+ Cursor mCursor;
+ ForceLoadContentObserver mObserver;
+ boolean mStopped;
+ Uri mUri;
+ String[] mProjection;
+ String mSelection;
+ String[] mSelectionArgs;
+ String mSortOrder;
+
+ /* Runs on a worker thread */
+ @Override
+ protected Cursor loadInBackground() {
+ Cursor cursor = getContext().getContentResolver().query(mUri, mProjection, mSelection,
+ mSelectionArgs, mSortOrder);
+ // Ensure the cursor window is filled
+ if (cursor != null) {
+ cursor.getCount();
+ cursor.registerContentObserver(mObserver);
+ }
+ return cursor;
+ }
+
+ /* Runs on the UI thread */
+ @Override
+ protected void onLoadComplete(Cursor cursor) {
+ if (mStopped) {
+ // An async query came in while the loader is stopped
+ cursor.close();
+ return;
+ }
+ mCursor = cursor;
+ deliverResult(cursor);
+ }
+
+ public CursorLoader(Context context, Uri uri, String[] projection, String selection,
+ String[] selectionArgs, String sortOrder) {
+ super(context);
+ mObserver = new ForceLoadContentObserver();
+ mUri = uri;
+ mProjection = projection;
+ mSelection = selection;
+ mSelectionArgs = selectionArgs;
+ mSortOrder = sortOrder;
+ }
+
+ /**
+ * 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.
+ *
+ * Must be called from the UI thread
+ */
+ @Override
+ public void startLoading() {
+ mStopped = false;
+
+ if (mCursor != null) {
+ deliverResult(mCursor);
+ } else {
+ forceLoad();
+ }
+ }
+
+ /**
+ * 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
+ public void stopLoading() {
+ if (mCursor != null && !mCursor.isClosed()) {
+ mCursor.close();
+ mCursor = null;
+ }
+
+ // Make sure that any outstanding loads clean themselves up properly
+ mStopped = true;
+ }
+
+ @Override
+ public void destroy() {
+ // Ensure the loader is stopped
+ stopLoading();
+ }
+}
diff --git a/src/com/android/contacts/mvcframework/Loader.java b/src/android/app/patterns/Loader.java
similarity index 71%
rename from src/com/android/contacts/mvcframework/Loader.java
rename to src/android/app/patterns/Loader.java
index fab2f46..dfc35e9 100644
--- a/src/com/android/contacts/mvcframework/Loader.java
+++ b/src/android/app/patterns/Loader.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.contacts.mvcframework;
+package android.app.patterns;
import android.content.Context;
import android.database.ContentObserver;
@@ -42,37 +42,60 @@
}
public interface OnLoadCompleteListener<D> {
- public void onLoadComplete(int id, D data);
+ /**
+ * 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 loader, D data);
}
+ /**
+ * Sends the result of the load to the register listener.
+ *
+ * @param data the result of the load
+ */
protected void deliverResult(D data) {
if (mListener != null) {
- mListener.onLoadComplete(mId, data);
-
+ mListener.onLoadComplete(this, 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();
}
/**
- * @return an application context
+ * @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 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) {
-// if (mListener != null) {
- // throw new IllegalStateException("There is already a listener registered");
- // }
+ if (mListener != null) {
+ throw new IllegalStateException("There is already a listener registered");
+ }
mListener = listener;
mId = id;
}
@@ -95,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
*/
@@ -108,11 +131,15 @@
public abstract void forceLoad();
/**
- * Stops delivery of updates.
+ * Stops delivery of updates until the next time {@link #startLoading()} is called
+ *
+ * Must be called from the UI thread
*/
public abstract void stopLoading();
/**
+ * Destroys the loader and frees it's resources, making it unusable.
+ *
* Must be called from the UI thread
*/
public abstract void destroy();
diff --git a/src/com/android/contacts/mvcframework/LoaderActivity.java b/src/android/app/patterns/LoaderActivity.java
similarity index 69%
rename from src/com/android/contacts/mvcframework/LoaderActivity.java
rename to src/android/app/patterns/LoaderActivity.java
index 0e74d62..664a8bc 100644
--- a/src/com/android/contacts/mvcframework/LoaderActivity.java
+++ b/src/android/app/patterns/LoaderActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.contacts.mvcframework;
+package android.app.patterns;
import android.app.Activity;
@@ -28,40 +28,61 @@
*/
public abstract class LoaderActivity<D> extends Activity implements
Loader.OnLoadCompleteListener<D> {
+ private boolean mStarted = false;
+
static final class LoaderInfo {
public Bundle args;
public Loader loader;
}
private HashMap<Integer, LoaderInfo> mLoaders;
+ private HashMap<Integer, LoaderInfo> 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 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;
+ // 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.args = args;
mLoaders.put(id, info);
- loader = onCreateLoader(id, args);
- loader.registerListener(id, this);
- loader.startLoading();
+ Loader 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 onCreateLoader(int id, Bundle args);
protected abstract void onInitializeLoaders();
+ protected abstract void onLoadFinished(Loader loader, D data);
- public abstract void onLoadComplete(int id, D data);
+ public final void onLoadComplete(Loader 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);
+ if (info != null) {
+ Loader oldLoader = info.loader;
+ if (oldLoader != null) {
+ oldLoader.destroy();
+ }
+ mInactiveLoaders.remove(id);
+ }
+ }
@Override
public void onCreate(Bundle savedState) {
@@ -75,6 +96,9 @@
onInitializeLoaders();
}
}
+ if (mInactiveLoaders == null) {
+ mInactiveLoaders = new HashMap<Integer, LoaderInfo>();
+ }
}
@Override
@@ -90,11 +114,12 @@
if (loader == null) {
loader = onCreateLoader(id, info.args);
info.loader = loader;
- } else {
- loader.registerListener(id, this);
}
+ loader.registerListener(id, this);
loader.startLoading();
}
+
+ mStarted = true;
}
@Override
@@ -116,6 +141,8 @@
// loader.stopLoading();
// }
}
+
+ mStarted = false;
}
@Override
diff --git a/src/com/android/contacts/activities/ContactDetailActivity.java b/src/com/android/contacts/activities/ContactDetailActivity.java
index dc14770..d413a25 100644
--- a/src/com/android/contacts/activities/ContactDetailActivity.java
+++ b/src/com/android/contacts/activities/ContactDetailActivity.java
@@ -18,12 +18,14 @@
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.util.DialogManager;
+import com.android.contacts.views.detail.ContactPresenter;
import com.android.contacts.views.detail.ContactLoader;
import android.app.Dialog;
+import android.app.patterns.Loader;
+import android.app.patterns.LoaderActivity;
+import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
@@ -33,7 +35,7 @@
public class ContactDetailActivity extends LoaderActivity<ContactLoader.Result> implements
DialogManager.DialogShowingViewActivity {
private static final int LOADER_DETAILS = 1;
- private ContactDetailView mDetails;
+ private ContactPresenter mCoupler;
private DialogManager mDialogManager;
private static final String TAG = "ContactDetailActivity";
@@ -49,8 +51,8 @@
mDialogManager = new DialogManager(this, DIALOG_VIEW_DIALOGS_ID1, DIALOG_VIEW_DIALOGS_ID2);
- mDetails = (ContactDetailView) findViewById(R.id.contact_details);
- mDetails.setCallbacks(new ContactDetailView.DefaultCallbacks(this));
+ mCoupler = new ContactPresenter(this, findViewById(R.id.contact_details));
+ mCoupler.setController(new ContactPresenter.DefaultController(this));
}
@Override
@@ -64,13 +66,16 @@
case LOADER_DETAILS: {
return new ContactLoader(this, getIntent().getData());
}
+ default: {
+ Log.wtf(TAG, "Unknown ID in onCreateLoader: " + id);
+ }
}
return null;
}
-
@Override
- public void onLoadComplete(int id, ContactLoader.Result data) {
+ public void onLoadFinished(Loader loader, ContactLoader.Result data) {
+ final int id = loader.getId();
switch (id) {
case LOADER_DETAILS:
if (data == ContactLoader.Result.NOT_FOUND) {
@@ -79,15 +84,18 @@
finish();
return;
}
- mDetails.setData(data);
+ mCoupler.setData(data);
break;
+ default: {
+ Log.wtf(TAG, "Unknown ID in onLoadFinished: " + id);
+ }
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// TODO: This is too hardwired.
- if (mDetails.onCreateOptionsMenu(menu, getMenuInflater())) return true;
+ if (mCoupler.onCreateOptionsMenu(menu, getMenuInflater())) return true;
return super.onCreateOptionsMenu(menu);
}
@@ -95,7 +103,7 @@
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
// TODO: This is too hardwired.
- if (mDetails.onPrepareOptionsMenu(menu)) return true;
+ if (mCoupler.onPrepareOptionsMenu(menu)) return true;
return super.onPrepareOptionsMenu(menu);
}
@@ -103,7 +111,7 @@
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// TODO: This is too hardwired.
- if (mDetails.onOptionsItemSelected(item)) return true;
+ if (mCoupler.onOptionsItemSelected(item)) return true;
return super.onOptionsItemSelected(item);
}
@@ -120,7 +128,7 @@
@Override
public boolean onContextItemSelected(MenuItem item) {
// TODO: This is too hardwired.
- if (mDetails.onContextItemSelected(item)) return true;
+ if (mCoupler.onContextItemSelected(item)) return true;
return super.onContextItemSelected(item);
}
@@ -138,7 +146,7 @@
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
// TODO: This is too hardwired.
- if (mDetails.onKeyDown(keyCode, event)) return true;
+ if (mCoupler.onKeyDown(keyCode, event)) return true;
return super.onKeyDown(keyCode, event);
}
diff --git a/src/com/android/contacts/mvcframework/CursorLoader.java b/src/com/android/contacts/mvcframework/CursorLoader.java
deleted file mode 100644
index 25585be..0000000
--- a/src/com/android/contacts/mvcframework/CursorLoader.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * 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.content.Context;
-import android.database.Cursor;
-import android.os.AsyncTask;
-
-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 */
- @Override
- protected Cursor doInBackground(Void... params) {
- Cursor cursor = doQueryInBackground();
- // Ensure the data is loaded
- if (cursor != null) {
- cursor.getCount();
- cursor.registerContentObserver(mObserver);
- }
- return cursor;
- }
-
- /* Runs on the UI thread */
- @Override
- protected void onPostExecute(Cursor cursor) {
- if (mClosed) {
- // An async query came in after the call to close()
- cursor.close();
- return;
- }
- mCursor = cursor;
- deliverResult(cursor);
- }
- }
-
- public CursorLoader(Context context) {
- super(context);
- mObserver = new ForceLoadContentObserver();
- }
-
- /**
- * 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.
- *
- * Must be called from the UI thread
- */
- @Override
- public void startLoading() {
- if (mCursor != null) {
- deliverResult(mCursor);
- } else {
- forceLoad();
- }
- }
-
- /**
- * 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
- public void stopLoading() {
- if (mCursor != null && !mCursor.isClosed()) {
- mCursor.close();
- mCursor = null;
- }
- }
-
- @Override
- public void destroy() {
- // Close up the cursor
- stopLoading();
- // Make sure that any outstanding loads clean themselves up properly
- mClosed = true;
- }
-
- /** Called from a worker thread to execute the desired query */
- protected abstract Cursor doQueryInBackground();
-}
diff --git a/src/com/android/contacts/ui/EditContactActivity.java b/src/com/android/contacts/ui/EditContactActivity.java
index 993417d..ffb1b00 100644
--- a/src/com/android/contacts/ui/EditContactActivity.java
+++ b/src/com/android/contacts/ui/EditContactActivity.java
@@ -30,9 +30,9 @@
import com.android.contacts.model.ContactsSource.EditType;
import com.android.contacts.model.Editor.EditorListener;
import com.android.contacts.model.EntityDelta.ValuesDelta;
-import com.android.contacts.mvcframework.DialogManager;
import com.android.contacts.ui.widget.BaseContactEditorView;
import com.android.contacts.ui.widget.PhotoEditorView;
+import com.android.contacts.util.DialogManager;
import com.android.contacts.util.EmptyService;
import com.android.contacts.util.WeakAsyncTask;
diff --git a/src/com/android/contacts/ui/widget/GenericEditorView.java b/src/com/android/contacts/ui/widget/GenericEditorView.java
index 30ef8c1..7901f60 100644
--- a/src/com/android/contacts/ui/widget/GenericEditorView.java
+++ b/src/com/android/contacts/ui/widget/GenericEditorView.java
@@ -25,9 +25,9 @@
import com.android.contacts.model.ContactsSource.EditField;
import com.android.contacts.model.ContactsSource.EditType;
import com.android.contacts.model.EntityDelta.ValuesDelta;
-import com.android.contacts.mvcframework.DialogManager;
-import com.android.contacts.mvcframework.DialogManager.DialogShowingView;
import com.android.contacts.ui.ViewIdGenerator;
+import com.android.contacts.util.DialogManager;
+import com.android.contacts.util.DialogManager.DialogShowingView;
import android.app.AlertDialog;
import android.app.Dialog;
diff --git a/src/com/android/contacts/mvcframework/DialogManager.java b/src/com/android/contacts/util/DialogManager.java
similarity index 98%
rename from src/com/android/contacts/mvcframework/DialogManager.java
rename to src/com/android/contacts/util/DialogManager.java
index 420c4b2..4c6baf3 100644
--- a/src/com/android/contacts/mvcframework/DialogManager.java
+++ b/src/com/android/contacts/util/DialogManager.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.contacts.mvcframework;
+package com.android.contacts.util;
import android.app.Activity;
import android.app.Dialog;
diff --git a/src/com/android/contacts/views/detail/ContactLoader.java b/src/com/android/contacts/views/detail/ContactLoader.java
index 55abd9f..bc885df 100644
--- a/src/com/android/contacts/views/detail/ContactLoader.java
+++ b/src/com/android/contacts/views/detail/ContactLoader.java
@@ -16,9 +16,9 @@
package com.android.contacts.views.detail;
-import com.android.contacts.mvcframework.Loader;
import com.android.contacts.util.DataStatus;
+import android.app.patterns.Loader;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Context;
diff --git a/src/com/android/contacts/views/detail/ContactDetailView.java b/src/com/android/contacts/views/detail/ContactPresenter.java
similarity index 96%
rename from src/com/android/contacts/views/detail/ContactDetailView.java
rename to src/com/android/contacts/views/detail/ContactPresenter.java
index 82d0699..d2dfda1 100644
--- a/src/com/android/contacts/views/detail/ContactDetailView.java
+++ b/src/com/android/contacts/views/detail/ContactPresenter.java
@@ -27,7 +27,7 @@
import com.android.contacts.model.ContactsSource;
import com.android.contacts.model.Sources;
import com.android.contacts.model.ContactsSource.DataKind;
-import com.android.contacts.mvcframework.DialogManager;
+import com.android.contacts.util.DialogManager;
import com.android.contacts.util.Constants;
import com.android.contacts.util.DataStatus;
import com.android.contacts.views.detail.ContactLoader.Result;
@@ -68,7 +68,6 @@
import android.provider.ContactsContract.CommonDataKinds.Website;
import android.telephony.PhoneNumberUtils;
import android.text.TextUtils;
-import android.util.AttributeSet;
import android.util.Log;
import android.view.ContextMenu;
import android.view.KeyEvent;
@@ -79,10 +78,10 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.ContextMenu.ContextMenuInfo;
+import android.view.View.OnClickListener;
import android.view.View.OnCreateContextMenuListener;
import android.widget.AdapterView;
import android.widget.ImageView;
-import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
@@ -90,7 +89,7 @@
import java.util.ArrayList;
-public class ContactDetailView extends LinearLayout implements OnCreateContextMenuListener,
+public class ContactPresenter implements OnCreateContextMenuListener,
OnItemClickListener, DialogManager.DialogShowingView {
private static final String TAG = "ContactDetailsView";
private static final boolean SHOW_SEPARATORS = false;
@@ -103,8 +102,11 @@
private static final int MENU_ITEM_MAKE_DEFAULT = 3;
+ private final Context mContext;
+ private final View mView;
+
private Result mContactData;
- private Callbacks mCallbacks;
+ private Controller mCallbacks;
private LayoutInflater mInflater;
private ContactHeaderWidget mContactHeaderWidget;
private ListView mListView;
@@ -141,12 +143,38 @@
private ArrayList<ViewEntry> mOtherEntries = new ArrayList<ViewEntry>();
private ArrayList<ArrayList<ViewEntry>> mSections = new ArrayList<ArrayList<ViewEntry>>();
- public ContactDetailView(Context context) {
- super(context);
- }
+ public ContactPresenter(Context context, View view) {
+ mContext = context;
+ mView = view;
- public ContactDetailView(Context context, AttributeSet attrs) {
- super(context, attrs);
+ mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ mContactHeaderWidget = (ContactHeaderWidget) view.findViewById(R.id.contact_header_widget);
+ mContactHeaderWidget.showStar(true);
+ mContactHeaderWidget.setExcludeMimes(new String[] {
+ Contacts.CONTENT_ITEM_TYPE
+ });
+
+ mListView = (ListView) view.findViewById(android.R.id.list);
+ mListView.setOnCreateContextMenuListener(this);
+ mListView.setScrollBarStyle(ListView.SCROLLBARS_OUTSIDE_OVERLAY);
+ mListView.setOnItemClickListener(this);
+ // Don't set it to mListView yet. We do so later when we bind the adapter.
+ mEmptyView = view.findViewById(android.R.id.empty);
+
+ // Build the list of sections. The order they're added to mSections dictates the
+ // order they are displayed in the list.
+ mSections.add(mPhoneEntries);
+ mSections.add(mSmsEntries);
+ mSections.add(mEmailEntries);
+ mSections.add(mImEntries);
+ mSections.add(mPostalEntries);
+ mSections.add(mNicknameEntries);
+ mSections.add(mOrganizationEntries);
+ mSections.add(mGroupEntries);
+ mSections.add(mOtherEntries);
+
+ //TODO Read this value from a preference
+ mShowSmsLinksForAllPhones = true;
}
public void setData(Result data) {
@@ -384,15 +412,15 @@
}
}
- public interface Callbacks {
+ public interface Controller {
public void onPrimaryClick(ViewEntry entry);
public void onSecondaryClick(ViewEntry entry);
}
- public static final class DefaultCallbacks implements Callbacks {
+ public static final class DefaultController implements Controller {
private Context mContext;
- public DefaultCallbacks(Context context) {
+ public DefaultController(Context context) {
mContext = context;
}
@@ -419,44 +447,10 @@
}
}
- public void setCallbacks(Callbacks callbacks) {
+ public void setController(Controller callbacks) {
mCallbacks = callbacks;
}
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- Context context = getContext();
- mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- mContactHeaderWidget = (ContactHeaderWidget) findViewById(R.id.contact_header_widget);
- mContactHeaderWidget.showStar(true);
- mContactHeaderWidget.setExcludeMimes(new String[] {
- Contacts.CONTENT_ITEM_TYPE
- });
-
- mListView = (ListView) findViewById(android.R.id.list);
- mListView.setOnCreateContextMenuListener(this);
- mListView.setScrollBarStyle(ListView.SCROLLBARS_OUTSIDE_OVERLAY);
- mListView.setOnItemClickListener(this);
- // Don't set it to mListView yet. We do so later when we bind the adapter.
- mEmptyView = findViewById(android.R.id.empty);
-
- // Build the list of sections. The order they're added to mSections dictates the
- // order they are displayed in the list.
- mSections.add(mPhoneEntries);
- mSections.add(mSmsEntries);
- mSections.add(mEmailEntries);
- mSections.add(mImEntries);
- mSections.add(mPostalEntries);
- mSections.add(mNicknameEntries);
- mSections.add(mOrganizationEntries);
- mSections.add(mGroupEntries);
- mSections.add(mOtherEntries);
-
- //TODO Read this value from a preference
- mShowSmsLinksForAllPhones = true;
- }
-
/* package */ static String buildActionString(DataKind kind, ContentValues values,
boolean lowerCase, Context context) {
if (kind.actionHeader == null) {
@@ -931,18 +925,17 @@
/* package */ void showDialog(int bundleDialogId) {
Bundle bundle = new Bundle();
bundle.putInt(DIALOG_ID_KEY, bundleDialogId);
- getDialogManager().showDialogInView(this, bundle);
+ getDialogManager().showDialogInView(mView, bundle);
}
private DialogManager getDialogManager() {
if (mDialogManager == null) {
- Context context = getContext();
- if (!(context instanceof DialogManager.DialogShowingViewActivity)) {
+ if (!(mContext instanceof DialogManager.DialogShowingViewActivity)) {
throw new IllegalStateException(
"View must be hosted in an Activity that implements " +
"DialogManager.DialogShowingViewActivity");
}
- mDialogManager = ((DialogManager.DialogShowingViewActivity)context).getDialogManager();
+ mDialogManager = ((DialogManager.DialogShowingViewActivity)mContext).getDialogManager();
}
return mDialogManager;
}
@@ -987,7 +980,6 @@
return ContactEntryAdapter.getEntry(mSections, info.position, SHOW_SEPARATORS);
}
- @Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_CALL: {
@@ -1026,6 +1018,6 @@
}
}
- return super.onKeyDown(keyCode, event);
+ return false;
}
}
diff --git a/tests/src/com/android/contacts/ContactDetailLoaderTest.java b/tests/src/com/android/contacts/ContactDetailLoaderTest.java
index b3a7b88..094de6a 100644
--- a/tests/src/com/android/contacts/ContactDetailLoaderTest.java
+++ b/tests/src/com/android/contacts/ContactDetailLoaderTest.java
@@ -16,11 +16,12 @@
package com.android.contacts;
-import com.android.contacts.mvcframework.Loader.OnLoadCompleteListener;
import com.android.contacts.tests.mocks.ContactsMockContext;
import com.android.contacts.tests.mocks.MockContentProvider;
import com.android.contacts.views.detail.ContactLoader;
+import android.app.patterns.Loader;
+import android.app.patterns.Loader.OnLoadCompleteListener;
import android.content.ContentUris;
import android.net.Uri;
import android.provider.ContactsContract.CommonDataKinds;
@@ -80,7 +81,7 @@
// A regular variable can not be written from an anonymous class, so use a 1-array
final ContactLoader.Result[] result = new ContactLoader.Result[1];
loader.registerListener(0, new OnLoadCompleteListener<ContactLoader.Result>() {
- public void onLoadComplete(int id, ContactLoader.Result data) {
+ public void onLoadComplete(Loader loader, ContactLoader.Result data) {
result[0] = data;
}
});