FileDocCategorySizeDatePackage
ConversationListAdapter.javaAPI DocAndroid 1.5 API15981Wed May 06 22:42:46 BST 2009com.android.mms.ui

ConversationListAdapter

public class ConversationListAdapter extends android.widget.CursorAdapter
The back-end data adapter for ConversationList.

Fields Summary
private static final String
TAG
private static final boolean
LOCAL_LOGV
static final String[]
PROJECTION
static final int
COLUMN_ID
static final int
COLUMN_MESSAGE_COUNT
static final int
COLUMN_RECIPIENTS_IDS
static final int
COLUMN_DATE
static final int
COLUMN_READ
static final int
COLUMN_SNIPPET
static final int
COLUMN_SNIPPET_CHARSET
static final int
COLUMN_ERROR
static final int
COLUMN_HAS_ATTACHMENT
static final String[]
DRAFT_PROJECTION
static final int
COLUMN_DRAFT_THREAD_ID
static final String[]
SEARCH_PROJECTION
static final int
COLUMN_MESSAGE_TYPE
static final int
COLUMN_MESSAGE_ID
static final int
COLUMN_THREAD_ID
static final int
COLUMN_SMS_ADDRESS
static final int
COLUMN_SMS_BODY
static final int
COLUMN_SMS_DATE
static final int
COLUMN_SMS_READ
static final int
COLUMN_SMS_TYPE
static final int
COLUMN_MMS_SUBJECT
static final int
COLUMN_MMS_SUBJECT_CHARSET
static final int
COLUMN_MMS_DATE
static final int
COLUMN_MMS_READ
private final android.view.LayoutInflater
mFactory
private final boolean
mSimpleMode
private final Map
mThreadDisplayFrom
private final ScheduledThreadPoolExecutor
mAsyncLoader
private final Stack
mThingsToLoad
private final Runnable
mPopStackRunnable
private final ConversationList.CachingNameStore
mCachingNameStore
Constructors Summary
public ConversationListAdapter(android.content.Context context, android.database.Cursor cursor, boolean simple, ConversationList.CachingNameStore nameStore)


          
                                     
        super(context, cursor, true /* auto-requery */);
        mSimpleMode = simple;
        mFactory = LayoutInflater.from(context);
        mCachingNameStore = nameStore;
        
        mThreadDisplayFrom = new ConcurrentHashMap<String, String>();
        // 1 thread.  SQLite can't do better anyway.
        mAsyncLoader = new ScheduledThreadPoolExecutor(1);
    
Methods Summary
public voidbindView(android.view.View view, android.content.Context context, android.database.Cursor cursor)

        if (view instanceof ConversationHeaderView) {
            ConversationHeaderView headerView = (ConversationHeaderView) view;
            String from, subject;
            long threadId, date;
            boolean read, error;
            int messageCount = 0;
            String spaceSeparatedRcptIds = null;
            int presenceIconResId = 0;
            boolean cacheEntryInvalid = false;
            boolean hasAttachment = false;

            if (mSimpleMode) {
                threadId = cursor.getLong(COLUMN_ID);
                spaceSeparatedRcptIds = cursor.getString(COLUMN_RECIPIENTS_IDS);
                from = getFromTextFromMessageThread(spaceSeparatedRcptIds);
                subject = MessageUtils.extractEncStrFromCursor(
                        cursor, COLUMN_SNIPPET, COLUMN_SNIPPET_CHARSET);
                date = cursor.getLong(COLUMN_DATE);
                read = cursor.getInt(COLUMN_READ) != 0;
                error = cursor.getInt(COLUMN_ERROR) != 0;
                messageCount = cursor.getInt(COLUMN_MESSAGE_COUNT);
                hasAttachment = cursor.getInt(COLUMN_HAS_ATTACHMENT) != 0;
                
                cacheEntryInvalid = true;

                // display the presence from the cache. The cache entry could be invalidated
                // in the activity's onResume(), but display the info anyways if it's in the cache.
                // If it's invalid, we'll force a refresh in the async thread.
                String address = MessageUtils.getRecipientsByIds(
                        context, spaceSeparatedRcptIds, false /* no query */);
                if (!TextUtils.isEmpty(address)) {
                    ContactInfoCache.CacheEntry entry = null;
                    ContactInfoCache cache = ContactInfoCache.getInstance();

                    if (Mms.isEmailAddress(address)) {
                        entry = cache.getContactInfoForEmailAddress(context, address,
                                false /* no query */);
                    } else {
                        entry = cache.getContactInfoForPhoneNumber(context, address,
                                false /* no query */);
                    }
                    
                    if (entry != null) {
                        presenceIconResId = entry.presenceResId;
                        cacheEntryInvalid = entry.isStale();
                        if (LOCAL_LOGV) {
                            Log.d(TAG, "ConvListAdapter.bindView: " + entry.name + ", presence=" +
                                presenceIconResId + ", cache invalid=" + cacheEntryInvalid);
                        }
                    }
                }
            } else {
                threadId = cursor.getLong(COLUMN_THREAD_ID);
                String msgType = cursor.getString(COLUMN_MESSAGE_TYPE);
                if (msgType.equals("sms")) {
                    from = cursor.getString(COLUMN_SMS_ADDRESS);
                    subject = cursor.getString(COLUMN_SMS_BODY);
                    date = cursor.getLong(COLUMN_SMS_DATE);
                    // FIXME: This is wrong! We cannot determine whether a
                    // thread is read or not by the read flag of the latest
                    // message in the thread.
                    read = cursor.getInt(COLUMN_SMS_READ) != 0;
                } else {
                    from = MessageUtils.getAddressByThreadId(
                            context, threadId);
                    subject = MessageUtils.extractEncStrFromCursor(
                            cursor, COLUMN_MMS_SUBJECT, COLUMN_MMS_SUBJECT_CHARSET);
                    date = cursor.getLong(COLUMN_MMS_DATE) * 1000;
                    read = cursor.getInt(COLUMN_MMS_READ) != 0;
                }
                error = false;
                if (TextUtils.isEmpty(from)) {
                    from = mContext.getString(R.string.anonymous_recipient);
                }
            }

            String timestamp = MessageUtils.formatTimeStampString(
                    context, date);

            if (TextUtils.isEmpty(subject)) {
                subject = mContext.getString(R.string.no_subject_view);
            }

            if (LOCAL_LOGV) Log.v(TAG, "pre-create ConversationHeader");
            boolean hasDraft = DraftCache.getInstance().hasDraft(threadId);
                
            ConversationHeader ch = new ConversationHeader(
                    threadId, from, subject, timestamp,
                    read, error, hasDraft, messageCount, hasAttachment);

            headerView.bind(context, ch);
            headerView.setPresenceIcon(presenceIconResId);

            // if the cache entry is invalid, or if we can't find the "from" field,
            // kick off an async op to refresh the name and presence
            if (cacheEntryInvalid || (from == null && spaceSeparatedRcptIds != null)) {
                startAsyncDisplayFromLoad(context, ch, headerView, spaceSeparatedRcptIds);
            }
            if (LOCAL_LOGV) Log.v(TAG, "post-bind ConversationHeader");
        } else {
            Log.e(TAG, "Unexpected bound view: " + view);
        }
    
public voidchangeCursor(android.database.Cursor cursor)

        // Now that we are requerying, bindView will restart anything
        // that might have been pending in the async loader, so clear
        // out its job stack and let it start fresh.
        synchronized (mThingsToLoad) {
            mThingsToLoad.clear();
        }
 
        super.changeCursor(cursor);
    
private java.lang.StringgetFromTextFromCache(java.lang.String spaceSeparatedRcptIds, java.lang.String address)
Returns the from text using the CachingNameStore.

        // Potentially blocking call to Contacts provider, lookup up
        // names:  (should usually be cached, though)
        String value = mCachingNameStore.getContactNames(address);

        if (TextUtils.isEmpty(value)) {
            value = mContext.getString(R.string.anonymous_recipient);
        }

        mThreadDisplayFrom.put(spaceSeparatedRcptIds, value);
        return value;
    
private java.lang.StringgetFromTextFromMessageThread(java.lang.String spaceSeparatedRcptIds)
Returns cached 'from' text of message thread (display form of list of recipients)

        // Thread IDs could in-theory be reassigned to different
        // recipients (if latest threadid was deleted and new
        // auto-increment was assigned), so our cache key is the
        // space-separated list of recipients IDs instead:
        String value = mThreadDisplayFrom.get(spaceSeparatedRcptIds);
        if (value != null) {
            return value;
        }

        return null;
    
public booleanisSimpleMode()

        return mSimpleMode;
    
public android.view.ViewnewView(android.content.Context context, android.database.Cursor cursor, android.view.ViewGroup parent)

        if (LOCAL_LOGV) Log.v(TAG, "inflating new view");
        return mFactory.inflate(R.layout.conversation_header, parent, false);
    
private voidstartAsyncDisplayFromLoad(android.content.Context context, ConversationHeader ch, ConversationHeaderView headerView, java.lang.String spaceSeparatedRcptIds)

        synchronized (mThingsToLoad) {
            mThingsToLoad.push(new Runnable() {
                    public void run() {
                        String addresses = MessageUtils.getRecipientsByIds(
                                context, spaceSeparatedRcptIds, true /* allow query */);

                        // set from text
                        String fromText = getFromTextFromMessageThread(spaceSeparatedRcptIds);
                        if (TextUtils.isEmpty(fromText)) {
                            fromText = getFromTextFromCache(spaceSeparatedRcptIds, addresses);
                        }

                        int presenceIconResId = 0;

                        if (addresses != null && addresses.indexOf(';") < 0) {
                            // only set presence for single recipient
                            ContactInfoCache.CacheEntry entry = null;
                            ContactInfoCache cache = ContactInfoCache.getInstance();
                            String address = addresses;

                            if (Mms.isEmailAddress(address)) {
                                entry = cache.getContactInfoForEmailAddress(context, address,
                                        true /* allow query */);
                            } else {
                                entry = cache.getContactInfoForPhoneNumber(context, address,
                                        true /* allow query */);
                            }

                            if (entry != null) {
                                presenceIconResId = entry.presenceResId;
                            }

                            if (LOCAL_LOGV) {
                                Log.d(TAG, "ConvListAdapter.startAsyncDisplayFromLoad: " + fromText
                                    + ", presence=" + presenceIconResId + ", cacheEntry=" + entry);
                            }
                        }

                        // need to update the from text and presence icon using a callback, so
                        // they are done in the UI thread
                        ch.setFromAndPresence(fromText, presenceIconResId);
                    }
                });
        }
        mAsyncLoader.execute(mPopStackRunnable);