diff options
author | Wei Huang <weih@google.com> | 2010-01-21 02:51:34 -0800 |
---|---|---|
committer | Wei Huang <weih@google.com> | 2010-01-21 12:06:43 -0800 |
commit | 22149f3d4ed44c63e066ba16f00709da47220a25 (patch) | |
tree | 3c389bc02c2ee1b21784b35f28aab4a1e438cb23 /core/java/com | |
parent | dcc14d6a26961a3ba45eaeb7305fdafb2b6694e2 (diff) | |
download | frameworks_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.java | 95 |
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) { |