summaryrefslogtreecommitdiffstats
path: root/core/java/com
diff options
context:
space:
mode:
authorWei Huang <weih@google.com>2010-01-21 02:51:34 -0800
committerWei Huang <weih@google.com>2010-01-21 12:06:43 -0800
commit22149f3d4ed44c63e066ba16f00709da47220a25 (patch)
tree3c389bc02c2ee1b21784b35f28aab4a1e438cb23 /core/java/com
parentdcc14d6a26961a3ba45eaeb7305fdafb2b6694e2 (diff)
downloadframeworks_base-22149f3d4ed44c63e066ba16f00709da47220a25.zip
frameworks_base-22149f3d4ed44c63e066ba16f00709da47220a25.tar.gz
frameworks_base-22149f3d4ed44c63e066ba16f00709da47220a25.tar.bz2
fix bug 2264186: clean up the outstanding async queries in ContactHeaderWidget.
- ContactHeaderWidget has cascading async queries, which weren't cancelled if a new query for a different phone number is started. If the new query fails to find a corresponding contact, the old async queries from the previous number could end up setting the contact name and photo to the wrong contact. I tested this by calling ContactHeaderWidget.bindFromPhoneNumber(number1); ContactHeaderWidget.bindFromPhoneNumber(number2); where number1 has a corresponding contact in the databse, and number2 doesn't. At the end of these 2 calls, the ContactHeaderWidget would display the contact info for number1. - also found a bug in AsyncQueryHandler.cancelOperation(), which doesn't reliably cancel the previous query. In ContactHeaderWidget's case, we really depend on the cancelling to work. So work around this bug by resetting mAsyncQueryHandler when we need to do a new lookup/query. When the old query result is passed back in the callback, discard the result if the QueryHandler is not the same as mAsyncQueryHandler. Change-Id: Ice79e77f787af03400e080cbd58162a91838181f
Diffstat (limited to 'core/java/com')
-rw-r--r--core/java/com/android/internal/widget/ContactHeaderWidget.java95
1 files changed, 74 insertions, 21 deletions
diff --git a/core/java/com/android/internal/widget/ContactHeaderWidget.java b/core/java/com/android/internal/widget/ContactHeaderWidget.java
index 33fd936..c4f9988 100644
--- a/core/java/com/android/internal/widget/ContactHeaderWidget.java
+++ b/core/java/com/android/internal/widget/ContactHeaderWidget.java
@@ -201,7 +201,7 @@ public class ContactHeaderWidget extends FrameLayout implements View.OnClickList
mNoPhotoResource = R.drawable.ic_contact_picture_3;
}
- mQueryHandler = new QueryHandler(mContentResolver);
+ resetAsyncQueryHandler();
}
public void enableClickListeners() {
@@ -237,6 +237,11 @@ public class ContactHeaderWidget extends FrameLayout implements View.OnClickList
@Override
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
try{
+ if (this != mQueryHandler) {
+ Log.d(TAG, "onQueryComplete: discard result, the query handler is reset!");
+ return;
+ }
+
switch (token) {
case TOKEN_PHOTO_QUERY: {
//Set the photo
@@ -263,8 +268,14 @@ public class ContactHeaderWidget extends FrameLayout implements View.OnClickList
bindContactInfo(cursor);
Uri lookupUri = Contacts.getLookupUri(cursor.getLong(ContactQuery._ID),
cursor.getString(ContactQuery.LOOKUP_KEY));
- startPhotoQuery(cursor.getLong(ContactQuery.PHOTO_ID), lookupUri);
+ startPhotoQuery(cursor.getLong(ContactQuery.PHOTO_ID),
+ lookupUri, false /* don't reset query handler */);
invalidate();
+ } else {
+ // shouldn't really happen
+ setDisplayName(null, null);
+ setSocialSnippet(null);
+ setPhoto(loadPlaceholderPhoto(null));
}
break;
}
@@ -273,11 +284,13 @@ public class ContactHeaderWidget extends FrameLayout implements View.OnClickList
long contactId = cursor.getLong(PHONE_LOOKUP_CONTACT_ID_COLUMN_INDEX);
String lookupKey = cursor.getString(
PHONE_LOOKUP_CONTACT_LOOKUP_KEY_COLUMN_INDEX);
- bindFromContactUri(Contacts.getLookupUri(contactId, lookupKey));
+ bindFromContactUriInternal(Contacts.getLookupUri(contactId, lookupKey),
+ false /* don't reset query handler */);
} else {
String phoneNumber = (String) cookie;
setDisplayName(phoneNumber, null);
setSocialSnippet(null);
+ setPhoto(loadPlaceholderPhoto(null));
mPhotoView.assignContactFromPhone(phoneNumber, true);
}
break;
@@ -287,11 +300,13 @@ public class ContactHeaderWidget extends FrameLayout implements View.OnClickList
long contactId = cursor.getLong(EMAIL_LOOKUP_CONTACT_ID_COLUMN_INDEX);
String lookupKey = cursor.getString(
EMAIL_LOOKUP_CONTACT_LOOKUP_KEY_COLUMN_INDEX);
- bindFromContactUri(Contacts.getLookupUri(contactId, lookupKey));
+ bindFromContactUriInternal(Contacts.getLookupUri(contactId, lookupKey),
+ false /* don't reset query handler */);
} else {
String emailAddress = (String) cookie;
setDisplayName(emailAddress, null);
setSocialSnippet(null);
+ setPhoto(loadPlaceholderPhoto(null));
mPhotoView.assignContactFromEmail(emailAddress, true);
}
break;
@@ -397,22 +412,22 @@ public class ContactHeaderWidget extends FrameLayout implements View.OnClickList
* Convenience method for binding all available data from an existing
* contact.
*
- * @param conatctUri a {Contacts.CONTENT_LOOKUP_URI} style URI.
+ * @param contactLookupUri a {Contacts.CONTENT_LOOKUP_URI} style URI.
*/
public void bindFromContactLookupUri(Uri contactLookupUri) {
- mContactUri = contactLookupUri;
- startContactQuery(contactLookupUri);
+ bindFromContactUriInternal(contactLookupUri, true /* reset query handler */);
}
/**
* Convenience method for binding all available data from an existing
* contact.
*
- * @param conatctUri a {Contacts.CONTENT_URI} style URI.
+ * @param contactUri a {Contacts.CONTENT_URI} style URI.
+ * @param resetQueryHandler whether to use a new AsyncQueryHandler or not.
*/
- public void bindFromContactUri(Uri contactUri) {
+ private void bindFromContactUriInternal(Uri contactUri, boolean resetQueryHandler) {
mContactUri = contactUri;
- startContactQuery(contactUri);
+ startContactQuery(contactUri, resetQueryHandler);
}
/**
@@ -424,6 +439,8 @@ public class ContactHeaderWidget extends FrameLayout implements View.OnClickList
* address, one of them will be chosen to bind to.
*/
public void bindFromEmail(String emailAddress) {
+ resetAsyncQueryHandler();
+
mQueryHandler.startQuery(TOKEN_EMAIL_LOOKUP, emailAddress,
Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, Uri.encode(emailAddress)),
EMAIL_LOOKUP_PROJECTION, null, null, null);
@@ -438,36 +455,72 @@ public class ContactHeaderWidget extends FrameLayout implements View.OnClickList
* number, one of them will be chosen to bind to.
*/
public void bindFromPhoneNumber(String number) {
+ resetAsyncQueryHandler();
+
mQueryHandler.startQuery(TOKEN_PHONE_LOOKUP, number,
Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number)),
PHONE_LOOKUP_PROJECTION, null, null, null);
}
/**
- * Method to force this widget to forget everything it knows about the contact.
- * The widget isn't automatically updated or redrawn.
+ * startContactQuery
*
+ * internal method to query contact by Uri.
+ *
+ * @param contactUri the contact uri
+ * @param resetQueryHandler whether to use a new AsyncQueryHandler or not
*/
- public void wipeClean() {
- setDisplayName(null, null);
- setPhoto(null);
- setSocialSnippet(null);
- mContactUri = null;
- mExcludeMimes = null;
- }
+ private void startContactQuery(Uri contactUri, boolean resetQueryHandler) {
+ if (resetQueryHandler) {
+ resetAsyncQueryHandler();
+ }
- private void startContactQuery(Uri contactUri) {
mQueryHandler.startQuery(TOKEN_CONTACT_INFO, null, contactUri, ContactQuery.COLUMNS,
null, null, null);
}
- protected void startPhotoQuery(long photoId, Uri lookupKey) {
+ /**
+ * startPhotoQuery
+ *
+ * internal method to query contact photo by photo id and uri.
+ *
+ * @param photoId the photo id.
+ * @param lookupKey the lookup uri.
+ * @param resetQueryHandler whether to use a new AsyncQueryHandler or not.
+ */
+ protected void startPhotoQuery(long photoId, Uri lookupKey, boolean resetQueryHandler) {
+ if (resetQueryHandler) {
+ resetAsyncQueryHandler();
+ }
+
mQueryHandler.startQuery(TOKEN_PHOTO_QUERY, lookupKey,
ContentUris.withAppendedId(Data.CONTENT_URI, photoId), PhotoQuery.COLUMNS,
null, null, null);
}
/**
+ * Method to force this widget to forget everything it knows about the contact.
+ * We need to stop any existing async queries for phone, email, contact, and photos.
+ */
+ public void wipeClean() {
+ resetAsyncQueryHandler();
+
+ setDisplayName(null, null);
+ setPhoto(loadPlaceholderPhoto(null));
+ setSocialSnippet(null);
+ setPresence(0);
+ mContactUri = null;
+ mExcludeMimes = null;
+ }
+
+
+ private void resetAsyncQueryHandler() {
+ // the api AsyncQueryHandler.cancelOperation() doesn't really work. Since we really
+ // need the old async queries to be cancelled, let's do it the hard way.
+ mQueryHandler = new QueryHandler(mContentResolver);
+ }
+
+ /**
* Bind the contact details provided by the given {@link Cursor}.
*/
protected void bindContactInfo(Cursor c) {