• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 Esmertec AG.
3  * Copyright (C) 2008 The Android Open Source Project
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 package com.android.mms.ui;
19 
20 import com.android.mms.R;
21 import com.google.android.mms.MmsException;
22 
23 import android.content.AsyncQueryHandler;
24 import android.content.ContentResolver;
25 import android.content.ContentUris;
26 import android.content.Context;
27 import android.database.Cursor;
28 import android.graphics.Bitmap;
29 import android.graphics.BitmapFactory;
30 import android.graphics.drawable.BitmapDrawable;
31 import android.graphics.drawable.Drawable;
32 import android.net.Uri;
33 import android.os.Handler;
34 import android.provider.BaseColumns;
35 import android.provider.ContactsContract.Contacts;
36 import android.provider.ContactsContract.Data;
37 import android.provider.ContactsContract.PhoneLookup;
38 import android.provider.ContactsContract.RawContacts;
39 import android.provider.ContactsContract.StatusUpdates;
40 import android.provider.ContactsContract.CommonDataKinds.Email;
41 import android.provider.ContactsContract.CommonDataKinds.Photo;
42 import android.provider.Telephony.Mms;
43 import android.provider.Telephony.MmsSms;
44 import android.provider.Telephony.Sms;
45 import android.provider.Telephony.MmsSms.PendingMessages;
46 import android.provider.Telephony.Sms.Conversations;
47 import android.text.TextUtils;
48 import android.text.format.DateUtils;
49 import android.util.Config;
50 import android.util.Log;
51 import android.view.LayoutInflater;
52 import android.view.View;
53 import android.view.ViewGroup;
54 import android.widget.CursorAdapter;
55 import android.widget.ListView;
56 
57 import java.util.HashMap;
58 import java.util.HashSet;
59 import java.util.LinkedHashMap;
60 import java.util.Map;
61 import java.util.regex.Pattern;
62 
63 /**
64  * The back-end data adapter of a message list.
65  */
66 public class MessageListAdapter extends CursorAdapter {
67     private static final String TAG = "MessageListAdapter";
68     private static final boolean DEBUG = false;
69     private static final boolean LOCAL_LOGV = Config.LOGV && DEBUG;
70 
71     static final String[] PROJECTION = new String[] {
72         // TODO: should move this symbol into com.android.mms.telephony.Telephony.
73         MmsSms.TYPE_DISCRIMINATOR_COLUMN,
74         BaseColumns._ID,
75         Conversations.THREAD_ID,
76         // For SMS
77         Sms.ADDRESS,
78         Sms.BODY,
79         Sms.DATE,
80         Sms.READ,
81         Sms.TYPE,
82         Sms.STATUS,
83         Sms.LOCKED,
84         Sms.ERROR_CODE,
85         // For MMS
86         Mms.SUBJECT,
87         Mms.SUBJECT_CHARSET,
88         Mms.DATE,
89         Mms.READ,
90         Mms.MESSAGE_TYPE,
91         Mms.MESSAGE_BOX,
92         Mms.DELIVERY_REPORT,
93         Mms.READ_REPORT,
94         PendingMessages.ERROR_TYPE,
95         Mms.LOCKED
96     };
97 
98     // The indexes of the default columns which must be consistent
99     // with above PROJECTION.
100     static final int COLUMN_MSG_TYPE            = 0;
101     static final int COLUMN_ID                  = 1;
102     static final int COLUMN_THREAD_ID           = 2;
103     static final int COLUMN_SMS_ADDRESS         = 3;
104     static final int COLUMN_SMS_BODY            = 4;
105     static final int COLUMN_SMS_DATE            = 5;
106     static final int COLUMN_SMS_READ            = 6;
107     static final int COLUMN_SMS_TYPE            = 7;
108     static final int COLUMN_SMS_STATUS          = 8;
109     static final int COLUMN_SMS_LOCKED          = 9;
110     static final int COLUMN_SMS_ERROR_CODE      = 10;
111     static final int COLUMN_MMS_SUBJECT         = 11;
112     static final int COLUMN_MMS_SUBJECT_CHARSET = 12;
113     static final int COLUMN_MMS_DATE            = 13;
114     static final int COLUMN_MMS_READ            = 14;
115     static final int COLUMN_MMS_MESSAGE_TYPE    = 15;
116     static final int COLUMN_MMS_MESSAGE_BOX     = 16;
117     static final int COLUMN_MMS_DELIVERY_REPORT = 17;
118     static final int COLUMN_MMS_READ_REPORT     = 18;
119     static final int COLUMN_MMS_ERROR_TYPE      = 19;
120     static final int COLUMN_MMS_LOCKED          = 20;
121 
122     private static final int CACHE_SIZE         = 50;
123 
124     protected LayoutInflater mInflater;
125     private final ListView mListView;
126     private final LinkedHashMap<Long, MessageItem> mMessageItemCache;
127     private final ColumnsMap mColumnsMap;
128     private OnDataSetChangedListener mOnDataSetChangedListener;
129     private Handler mMsgListItemHandler;
130     private Pattern mHighlight;
131     private Context mContext;
132 
133     private HashMap<String, HashSet<MessageListItem>> mAddressToMessageListItems
134         = new HashMap<String, HashSet<MessageListItem>>();
135 
MessageListAdapter( Context context, Cursor c, ListView listView, boolean useDefaultColumnsMap, Pattern highlight)136     public MessageListAdapter(
137             Context context, Cursor c, ListView listView,
138             boolean useDefaultColumnsMap, Pattern highlight) {
139         super(context, c, false /* auto-requery */);
140         mContext = context;
141         mHighlight = highlight;
142 
143         mInflater = (LayoutInflater) context.getSystemService(
144                 Context.LAYOUT_INFLATER_SERVICE);
145         mListView = listView;
146         mMessageItemCache = new LinkedHashMap<Long, MessageItem>(
147                     10, 1.0f, true) {
148             @Override
149             protected boolean removeEldestEntry(Map.Entry eldest) {
150                 return size() > CACHE_SIZE;
151             }
152         };
153 
154         if (useDefaultColumnsMap) {
155             mColumnsMap = new ColumnsMap();
156         } else {
157             mColumnsMap = new ColumnsMap(c);
158         }
159 
160         mAvatarCache = new AvatarCache();
161     }
162 
163     @Override
bindView(View view, Context context, Cursor cursor)164     public void bindView(View view, Context context, Cursor cursor) {
165         if (view instanceof MessageListItem) {
166             String type = cursor.getString(mColumnsMap.mColumnMsgType);
167             long msgId = cursor.getLong(mColumnsMap.mColumnMsgId);
168 
169             MessageItem msgItem = getCachedMessageItem(type, msgId, cursor);
170             if (msgItem != null) {
171                 MessageListItem mli = (MessageListItem) view;
172 
173                 // Remove previous item from mapping
174                 MessageItem oldMessageItem = mli.getMessageItem();
175                 if (oldMessageItem != null) {
176                     String oldAddress = oldMessageItem.mAddress;
177                     if (oldAddress != null) {
178                         HashSet<MessageListItem> set = mAddressToMessageListItems.get(oldAddress);
179                         if (set != null) {
180                             set.remove(mli);
181                         }
182                     }
183                 }
184 
185                 mli.bind(mAvatarCache, msgItem);
186                 mli.setMsgListItemHandler(mMsgListItemHandler);
187 
188                 // Add current item to mapping
189 
190                 String addr;
191                 if (!Sms.isOutgoingFolder(msgItem.mBoxId)) {
192                     addr = msgItem.mAddress;
193                 } else {
194                     addr = MessageUtils.getLocalNumber();
195                 }
196 
197                 HashSet<MessageListItem> set = mAddressToMessageListItems.get(addr);
198                 if (set == null) {
199                     set = new HashSet<MessageListItem>();
200                     mAddressToMessageListItems.put(addr, set);
201                 }
202                 set.add(mli);
203             }
204         }
205     }
206 
207     public interface OnDataSetChangedListener {
onDataSetChanged(MessageListAdapter adapter)208         void onDataSetChanged(MessageListAdapter adapter);
onContentChanged(MessageListAdapter adapter)209         void onContentChanged(MessageListAdapter adapter);
210     }
211 
setOnDataSetChangedListener(OnDataSetChangedListener l)212     public void setOnDataSetChangedListener(OnDataSetChangedListener l) {
213         mOnDataSetChangedListener = l;
214     }
215 
setMsgListItemHandler(Handler handler)216     public void setMsgListItemHandler(Handler handler) {
217         mMsgListItemHandler = handler;
218     }
219 
notifyImageLoaded(String address)220     public void notifyImageLoaded(String address) {
221         HashSet<MessageListItem> set = mAddressToMessageListItems.get(address);
222         if (set != null) {
223             for (MessageListItem mli : set) {
224                 mli.bind(mAvatarCache, mli.getMessageItem());
225             }
226         }
227     }
228 
229     @Override
notifyDataSetChanged()230     public void notifyDataSetChanged() {
231         super.notifyDataSetChanged();
232         if (LOCAL_LOGV) {
233             Log.v(TAG, "MessageListAdapter.notifyDataSetChanged().");
234         }
235 
236         mListView.setSelection(mListView.getCount());
237         mMessageItemCache.clear();
238 
239         if (mOnDataSetChangedListener != null) {
240             mOnDataSetChangedListener.onDataSetChanged(this);
241         }
242     }
243 
244     @Override
onContentChanged()245     protected void onContentChanged() {
246         if (getCursor() != null && !getCursor().isClosed()) {
247             if (mOnDataSetChangedListener != null) {
248                 mOnDataSetChangedListener.onContentChanged(this);
249             }
250         }
251     }
252 
253     @Override
newView(Context context, Cursor cursor, ViewGroup parent)254     public View newView(Context context, Cursor cursor, ViewGroup parent) {
255         return mInflater.inflate(R.layout.message_list_item, parent, false);
256     }
257 
getCachedMessageItem(String type, long msgId, Cursor c)258     public MessageItem getCachedMessageItem(String type, long msgId, Cursor c) {
259         MessageItem item = mMessageItemCache.get(getKey(type, msgId));
260         if (item == null && c != null && isCursorValid(c)) {
261             try {
262                 item = new MessageItem(mContext, type, c, mColumnsMap, mHighlight);
263                 mMessageItemCache.put(getKey(item.mType, item.mMsgId), item);
264             } catch (MmsException e) {
265                 Log.e(TAG, "getCachedMessageItem: ", e);
266             }
267         }
268         return item;
269     }
270 
isCursorValid(Cursor cursor)271     private boolean isCursorValid(Cursor cursor) {
272         // Check whether the cursor is valid or not.
273         if (cursor.isClosed() || cursor.isBeforeFirst() || cursor.isAfterLast()) {
274             return false;
275         }
276         return true;
277     }
278 
getKey(String type, long id)279     private static long getKey(String type, long id) {
280         if (type.equals("mms")) {
281             return -id;
282         } else {
283             return id;
284         }
285     }
286 
287     public static class ColumnsMap {
288         public int mColumnMsgType;
289         public int mColumnMsgId;
290         public int mColumnSmsAddress;
291         public int mColumnSmsBody;
292         public int mColumnSmsDate;
293         public int mColumnSmsRead;
294         public int mColumnSmsType;
295         public int mColumnSmsStatus;
296         public int mColumnSmsLocked;
297         public int mColumnSmsErrorCode;
298         public int mColumnMmsSubject;
299         public int mColumnMmsSubjectCharset;
300         public int mColumnMmsDate;
301         public int mColumnMmsRead;
302         public int mColumnMmsMessageType;
303         public int mColumnMmsMessageBox;
304         public int mColumnMmsDeliveryReport;
305         public int mColumnMmsReadReport;
306         public int mColumnMmsErrorType;
307         public int mColumnMmsLocked;
308 
ColumnsMap()309         public ColumnsMap() {
310             mColumnMsgType            = COLUMN_MSG_TYPE;
311             mColumnMsgId              = COLUMN_ID;
312             mColumnSmsAddress         = COLUMN_SMS_ADDRESS;
313             mColumnSmsBody            = COLUMN_SMS_BODY;
314             mColumnSmsDate            = COLUMN_SMS_DATE;
315             mColumnSmsType            = COLUMN_SMS_TYPE;
316             mColumnSmsStatus          = COLUMN_SMS_STATUS;
317             mColumnSmsLocked          = COLUMN_SMS_LOCKED;
318             mColumnSmsErrorCode       = COLUMN_SMS_ERROR_CODE;
319             mColumnMmsSubject         = COLUMN_MMS_SUBJECT;
320             mColumnMmsSubjectCharset  = COLUMN_MMS_SUBJECT_CHARSET;
321             mColumnMmsMessageType     = COLUMN_MMS_MESSAGE_TYPE;
322             mColumnMmsMessageBox      = COLUMN_MMS_MESSAGE_BOX;
323             mColumnMmsDeliveryReport  = COLUMN_MMS_DELIVERY_REPORT;
324             mColumnMmsReadReport      = COLUMN_MMS_READ_REPORT;
325             mColumnMmsErrorType       = COLUMN_MMS_ERROR_TYPE;
326             mColumnMmsLocked          = COLUMN_MMS_LOCKED;
327         }
328 
ColumnsMap(Cursor cursor)329         public ColumnsMap(Cursor cursor) {
330             // Ignore all 'not found' exceptions since the custom columns
331             // may be just a subset of the default columns.
332             try {
333                 mColumnMsgType = cursor.getColumnIndexOrThrow(
334                         MmsSms.TYPE_DISCRIMINATOR_COLUMN);
335             } catch (IllegalArgumentException e) {
336                 Log.w("colsMap", e.getMessage());
337             }
338 
339             try {
340                 mColumnMsgId = cursor.getColumnIndexOrThrow(BaseColumns._ID);
341             } catch (IllegalArgumentException e) {
342                 Log.w("colsMap", e.getMessage());
343             }
344 
345             try {
346                 mColumnSmsAddress = cursor.getColumnIndexOrThrow(Sms.ADDRESS);
347             } catch (IllegalArgumentException e) {
348                 Log.w("colsMap", e.getMessage());
349             }
350 
351             try {
352                 mColumnSmsBody = cursor.getColumnIndexOrThrow(Sms.BODY);
353             } catch (IllegalArgumentException e) {
354                 Log.w("colsMap", e.getMessage());
355             }
356 
357             try {
358                 mColumnSmsDate = cursor.getColumnIndexOrThrow(Sms.DATE);
359             } catch (IllegalArgumentException e) {
360                 Log.w("colsMap", e.getMessage());
361             }
362 
363             try {
364                 mColumnSmsType = cursor.getColumnIndexOrThrow(Sms.TYPE);
365             } catch (IllegalArgumentException e) {
366                 Log.w("colsMap", e.getMessage());
367             }
368 
369             try {
370                 mColumnSmsStatus = cursor.getColumnIndexOrThrow(Sms.STATUS);
371             } catch (IllegalArgumentException e) {
372                 Log.w("colsMap", e.getMessage());
373             }
374 
375             try {
376                 mColumnSmsLocked = cursor.getColumnIndexOrThrow(Sms.LOCKED);
377             } catch (IllegalArgumentException e) {
378                 Log.w("colsMap", e.getMessage());
379             }
380 
381             try {
382                 mColumnSmsErrorCode = cursor.getColumnIndexOrThrow(Sms.ERROR_CODE);
383             } catch (IllegalArgumentException e) {
384                 Log.w("colsMap", e.getMessage());
385             }
386 
387             try {
388                 mColumnMmsSubject = cursor.getColumnIndexOrThrow(Mms.SUBJECT);
389             } catch (IllegalArgumentException e) {
390                 Log.w("colsMap", e.getMessage());
391             }
392 
393             try {
394                 mColumnMmsSubjectCharset = cursor.getColumnIndexOrThrow(Mms.SUBJECT_CHARSET);
395             } catch (IllegalArgumentException e) {
396                 Log.w("colsMap", e.getMessage());
397             }
398 
399             try {
400                 mColumnMmsMessageType = cursor.getColumnIndexOrThrow(Mms.MESSAGE_TYPE);
401             } catch (IllegalArgumentException e) {
402                 Log.w("colsMap", e.getMessage());
403             }
404 
405             try {
406                 mColumnMmsMessageBox = cursor.getColumnIndexOrThrow(Mms.MESSAGE_BOX);
407             } catch (IllegalArgumentException e) {
408                 Log.w("colsMap", e.getMessage());
409             }
410 
411             try {
412                 mColumnMmsDeliveryReport = cursor.getColumnIndexOrThrow(Mms.DELIVERY_REPORT);
413             } catch (IllegalArgumentException e) {
414                 Log.w("colsMap", e.getMessage());
415             }
416 
417             try {
418                 mColumnMmsReadReport = cursor.getColumnIndexOrThrow(Mms.READ_REPORT);
419             } catch (IllegalArgumentException e) {
420                 Log.w("colsMap", e.getMessage());
421             }
422 
423             try {
424                 mColumnMmsErrorType = cursor.getColumnIndexOrThrow(PendingMessages.ERROR_TYPE);
425             } catch (IllegalArgumentException e) {
426                 Log.w("colsMap", e.getMessage());
427             }
428 
429             try {
430                 mColumnMmsLocked = cursor.getColumnIndexOrThrow(Mms.LOCKED);
431             } catch (IllegalArgumentException e) {
432                 Log.w("colsMap", e.getMessage());
433             }
434         }
435     }
436 
437     private AvatarCache mAvatarCache;
438 
439     /*
440      * Track avatars for each of the members of in the group chat.
441      */
442     class AvatarCache {
443         private static final int TOKEN_PHONE_LOOKUP = 101;
444         private static final int TOKEN_EMAIL_LOOKUP = 102;
445         private static final int TOKEN_CONTACT_INFO = 201;
446         private static final int TOKEN_PHOTO_DATA = 301;
447 
448         //Projection used for the summary info in the header.
449         private final String[] COLUMNS = new String[] {
450                   Contacts._ID,
451                   Contacts.PHOTO_ID,
452                   // Other fields which we might want/need in the future (for example)
453 //                Contacts.LOOKUP_KEY,
454 //                Contacts.DISPLAY_NAME,
455 //                Contacts.STARRED,
456 //                Contacts.CONTACT_PRESENCE,
457 //                Contacts.CONTACT_STATUS,
458 //                Contacts.CONTACT_STATUS_TIMESTAMP,
459 //                Contacts.CONTACT_STATUS_RES_PACKAGE,
460 //                Contacts.CONTACT_STATUS_LABEL,
461         };
462         private final int PHOTO_ID = 1;
463 
464         private final String[] PHONE_LOOKUP_PROJECTION = new String[] {
465             PhoneLookup._ID,
466             PhoneLookup.LOOKUP_KEY,
467         };
468         private static final int PHONE_LOOKUP_CONTACT_ID_COLUMN_INDEX = 0;
469         private static final int PHONE_LOOKUP_CONTACT_LOOKUP_KEY_COLUMN_INDEX = 1;
470 
471         private final String[] EMAIL_LOOKUP_PROJECTION = new String[] {
472             RawContacts.CONTACT_ID,
473             Contacts.LOOKUP_KEY,
474         };
475         private static final int EMAIL_LOOKUP_CONTACT_ID_COLUMN_INDEX = 0;
476         private static final int EMAIL_LOOKUP_CONTACT_LOOKUP_KEY_COLUMN_INDEX = 1;
477 
478 
479         /*
480          * Map from mAddress to a blob of data which contains the contact id
481          * and the avatar.
482          */
483         HashMap<String, ContactData> mImageCache = new HashMap<String, ContactData>();
484 
485         public class ContactData {
486             private String mAddress;
487             private long mContactId;
488             private Uri mContactUri;
489             private Drawable mPhoto;
490 
ContactData(String address)491             ContactData(String address) {
492                 mAddress = address;
493             }
494 
getAvatar()495             public Drawable getAvatar() {
496                 return mPhoto;
497             }
498 
getContactUri()499             public Uri getContactUri() {
500                 return mContactUri;
501             }
502 
startInitialQuery()503             private boolean startInitialQuery() {
504                 if (Mms.isPhoneNumber(mAddress)) {
505                     mQueryHandler.startQuery(
506                             TOKEN_PHONE_LOOKUP,
507                             this,
508                             Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(mAddress)),
509                             PHONE_LOOKUP_PROJECTION,
510                             null,
511                             null,
512                             null);
513                     return true;
514                 } else if (Mms.isEmailAddress(mAddress)) {
515                     mQueryHandler.startQuery(
516                             TOKEN_EMAIL_LOOKUP,
517                             this,
518                             Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, Uri.encode(mAddress)),
519                             EMAIL_LOOKUP_PROJECTION,
520                             null,
521                             null,
522                             null);
523                     return true;
524                 } else {
525                     return false;
526                 }
527             }
528             /*
529              * Once we have the photo data load it into a drawable.
530              */
onPhotoDataLoaded(Cursor c)531             private boolean onPhotoDataLoaded(Cursor c) {
532                 if (c == null || !c.moveToFirst()) return false;
533 
534                 try {
535                     byte[] photoData = c.getBlob(0);
536                     Bitmap b = BitmapFactory.decodeByteArray(photoData, 0, photoData.length, null);
537                     mPhoto = new BitmapDrawable(mContext.getResources(), b);
538                     return true;
539                 } catch (Exception ex) {
540                     return false;
541                 }
542             }
543 
544             /*
545              * Once we have the contact info loaded take the photo id and query
546              * for the photo data.
547              */
onContactInfoLoaded(Cursor c)548             private boolean onContactInfoLoaded(Cursor c) {
549                 if (c == null || !c.moveToFirst()) return false;
550 
551                 long photoId = c.getLong(PHOTO_ID);
552                 Uri contactUri  = ContentUris.withAppendedId(Data.CONTENT_URI, photoId);
553                 mQueryHandler.startQuery(
554                         TOKEN_PHOTO_DATA,
555                         this,
556                         contactUri,
557                         new String[] { Photo.PHOTO },
558                         null,
559                         null,
560                         null);
561 
562                 return true;
563             }
564 
565             /*
566              * Once we have the contact id loaded start the query for the
567              * contact information (which will give us the photo id).
568              */
onContactIdLoaded(Cursor c, int contactIdColumn, int lookupKeyColumn)569             private boolean onContactIdLoaded(Cursor c, int contactIdColumn, int lookupKeyColumn) {
570                 if (c == null || !c.moveToFirst()) return false;
571 
572                 mContactId = c.getLong(contactIdColumn);
573                 String lookupKey = c.getString(lookupKeyColumn);
574                 mContactUri = Contacts.getLookupUri(mContactId, lookupKey);
575                 mQueryHandler.startQuery(
576                         TOKEN_CONTACT_INFO,
577                         this,
578                         mContactUri,
579                         COLUMNS,
580                         null,
581                         null,
582                         null);
583                 return true;
584             }
585 
586             /*
587              * If for whatever reason we can't get the photo load teh
588              * default avatar.  NOTE that fasttrack tries to get fancy
589              * with various random images (upside down, etc.) we're not
590              * doing that here.
591              */
loadDefaultAvatar()592             private void loadDefaultAvatar() {
593                 try {
594                     if (mDefaultAvatarDrawable == null) {
595                         Bitmap b = BitmapFactory.decodeResource(mContext.getResources(),
596                                 R.drawable.ic_contact_picture);
597                         mDefaultAvatarDrawable = new BitmapDrawable(mContext.getResources(), b);
598                     }
599                     mPhoto = mDefaultAvatarDrawable;
600                 } catch (java.lang.OutOfMemoryError e) {
601                     Log.e(TAG, "loadDefaultAvatar: out of memory: ", e);
602                 }
603             }
604 
605         };
606 
607         Drawable mDefaultAvatarDrawable = null;
608         AsyncQueryHandler mQueryHandler = new AsyncQueryHandler(mContext.getContentResolver()) {
609             @Override
610             protected void onQueryComplete(int token, Object cookieObject, Cursor cursor) {
611                 super.onQueryComplete(token, cookieObject, cursor);
612 
613                 ContactData cookie = (ContactData) cookieObject;
614                 switch (token) {
615                     case TOKEN_PHONE_LOOKUP: {
616                         if (!cookie.onContactIdLoaded(
617                                 cursor,
618                                 PHONE_LOOKUP_CONTACT_ID_COLUMN_INDEX,
619                                 PHONE_LOOKUP_CONTACT_LOOKUP_KEY_COLUMN_INDEX)) {
620                             cookie.loadDefaultAvatar();
621                         }
622                         break;
623                     }
624                     case TOKEN_EMAIL_LOOKUP: {
625                         if (!cookie.onContactIdLoaded(
626                                 cursor,
627                                 EMAIL_LOOKUP_CONTACT_ID_COLUMN_INDEX,
628                                 EMAIL_LOOKUP_CONTACT_LOOKUP_KEY_COLUMN_INDEX)) {
629                             cookie.loadDefaultAvatar();
630                         }
631                         break;
632                     }
633                     case TOKEN_CONTACT_INFO: {
634                         if (!cookie.onContactInfoLoaded(cursor)) {
635                             cookie.loadDefaultAvatar();
636                         }
637                         break;
638                     }
639                     case TOKEN_PHOTO_DATA: {
640                         if (!cookie.onPhotoDataLoaded(cursor)) {
641                             cookie.loadDefaultAvatar();
642                         } else {
643                             MessageListAdapter.this.notifyImageLoaded(cookie.mAddress);
644                         }
645                         break;
646                     }
647                     default:
648                         break;
649                 }
650             }
651         };
652 
get(final String address)653         public ContactData get(final String address) {
654             if (mImageCache.containsKey(address)) {
655                 return mImageCache.get(address);
656             } else {
657                 // Create the ContactData object and put it into the hashtable
658                 // so that any subsequent requests for this same avatar do not kick
659                 // off another query.
660                 ContactData cookie = new ContactData(address);
661                 mImageCache.put(address, cookie);
662                 cookie.startInitialQuery();
663                 cookie.loadDefaultAvatar();
664                 return cookie;
665             }
666         }
667 
AvatarCache()668         public AvatarCache() {
669         }
670     };
671 
672 
673 }
674