ConversationListAdapterpublic 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 void | bindView(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 void | changeCursor(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.String | getFromTextFromCache(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.String | getFromTextFromMessageThread(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 boolean | isSimpleMode()
return mSimpleMode;
| public android.view.View | newView(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 void | startAsyncDisplayFromLoad(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);
|
|