blob: a9692e9c233b85097f13e3c3b1bc7127790e0671 [file] [log] [blame]
/**
* Copyright (c) 2012, Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.mail.providers;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.provider.BaseColumns;
import android.text.TextUtils;
import com.android.mail.R;
import com.android.mail.browse.ConversationCursor;
import com.android.mail.content.CursorCreator;
import com.android.mail.providers.UIProvider.ConversationColumns;
import com.android.mail.providers.UIProvider.ConversationCursorCommand;
import com.android.mail.ui.ConversationCursorLoader;
import com.android.mail.utils.LogTag;
import com.android.mail.utils.LogUtils;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
public class Conversation implements Parcelable {
public static final int NO_POSITION = -1;
private static final String LOG_TAG = LogTag.getLogTag();
private static final String EMPTY_STRING = "";
/**
* @see BaseColumns#_ID
*/
public final long id;
/**
* @see UIProvider.ConversationColumns#URI
*/
public final Uri uri;
/**
* @see UIProvider.ConversationColumns#SUBJECT
*/
public final String subject;
/**
* @see UIProvider.ConversationColumns#DATE_RECEIVED_MS
*/
public final long dateMs;
/**
* @see UIProvider.ConversationColumns#HAS_ATTACHMENTS
*/
public final boolean hasAttachments;
/**
* Union of attachmentPreviewUri0 and attachmentPreviewUri1
*/
public transient ArrayList<String> attachmentPreviews;
/**
* @see UIProvider.ConversationColumns#ATTACHMENT_PREVIEW_URI0
*/
public String attachmentPreviewUri0;
/**
* @see UIProvider.ConversationColumns#ATTACHMENT_PREVIEW_URI1
*/
public String attachmentPreviewUri1;
/**
* @see UIProvider.ConversationColumns#ATTACHMENT_PREVIEW_STATES
*/
public final int attachmentPreviewStates;
/**
* @see UIProvider.ConversationColumns#ATTACHMENT_PREVIEWS_COUNT
*/
public final int attachmentPreviewsCount;
/**
* @see UIProvider.ConversationColumns#MESSAGE_LIST_URI
*/
public final Uri messageListUri;
/**
* @see UIProvider.ConversationColumns#SENDING_STATE
*/
public final int sendingState;
/**
* @see UIProvider.ConversationColumns#PRIORITY
*/
public int priority;
/**
* @see UIProvider.ConversationColumns#READ
*/
public boolean read;
/**
* @see UIProvider.ConversationColumns#SEEN
*/
public boolean seen;
/**
* @see UIProvider.ConversationColumns#STARRED
*/
public boolean starred;
/**
* @see UIProvider.ConversationColumns#RAW_FOLDERS
*/
private FolderList rawFolders;
/**
* @see UIProvider.ConversationColumns#FLAGS
*/
public int convFlags;
/**
* @see UIProvider.ConversationColumns#PERSONAL_LEVEL
*/
public final int personalLevel;
/**
* @see UIProvider.ConversationColumns#SPAM
*/
public final boolean spam;
/**
* @see UIProvider.ConversationColumns#MUTED
*/
public final boolean muted;
/**
* @see UIProvider.ConversationColumns#PHISHING
*/
public final boolean phishing;
/**
* @see UIProvider.ConversationColumns#COLOR
*/
public final int color;
/**
* @see UIProvider.ConversationColumns#ACCOUNT_URI
*/
public final Uri accountUri;
/**
* @see UIProvider.ConversationColumns#CONVERSATION_INFO
*/
public final ConversationInfo conversationInfo;
/**
* @see UIProvider.ConversationColumns#CONVERSATION_BASE_URI
*/
public final Uri conversationBaseUri;
/**
* @see UIProvider.ConversationColumns#REMOTE
*/
public final boolean isRemote;
/**
* Used within the UI to indicate the adapter position of this conversation
*
* @deprecated Keeping this in sync with the desired value is a not always done properly, is a
* source of bugs, and is a bad idea in general. Do not trust this value. Try to
* migrate code away from using it.
*/
@Deprecated
public transient int position;
// Used within the UI to indicate that a Conversation should be removed from
// the ConversationCursor when executing an update, e.g. the the
// Conversation is no longer in the ConversationList for the current folder,
// that is it's now in some other folder(s)
public transient boolean localDeleteOnUpdate;
private transient boolean viewed;
private static String sSubjectAndSnippet;
private static String sBadgeSubjectAndSnippet;
// Constituents of convFlags below
// Flag indicating that the item has been deleted, but will continue being
// shown in the list Delete/Archive of a mostly-dead item will NOT propagate
// the delete/archive, but WILL remove the item from the cursor
public static final int FLAG_MOSTLY_DEAD = 1 << 0;
/** An immutable, empty conversation list */
public static final Collection<Conversation> EMPTY = Collections.emptyList();
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(id);
dest.writeParcelable(uri, flags);
dest.writeString(subject);
dest.writeLong(dateMs);
dest.writeInt(hasAttachments ? 1 : 0);
dest.writeParcelable(messageListUri, 0);
dest.writeInt(sendingState);
dest.writeInt(priority);
dest.writeInt(read ? 1 : 0);
dest.writeInt(seen ? 1 : 0);
dest.writeInt(starred ? 1 : 0);
dest.writeParcelable(rawFolders, 0);
dest.writeInt(convFlags);
dest.writeInt(personalLevel);
dest.writeInt(spam ? 1 : 0);
dest.writeInt(phishing ? 1 : 0);
dest.writeInt(muted ? 1 : 0);
dest.writeInt(color);
dest.writeParcelable(accountUri, 0);
dest.writeParcelable(conversationInfo, 0);
dest.writeParcelable(conversationBaseUri, 0);
dest.writeInt(isRemote ? 1 : 0);
dest.writeString(attachmentPreviewUri0);
dest.writeString(attachmentPreviewUri1);
dest.writeInt(attachmentPreviewStates);
dest.writeInt(attachmentPreviewsCount);
}
private Conversation(Parcel in, ClassLoader loader) {
id = in.readLong();
uri = in.readParcelable(null);
subject = in.readString();
dateMs = in.readLong();
hasAttachments = (in.readInt() != 0);
messageListUri = in.readParcelable(null);
sendingState = in.readInt();
priority = in.readInt();
read = (in.readInt() != 0);
seen = (in.readInt() != 0);
starred = (in.readInt() != 0);
rawFolders = in.readParcelable(loader);
convFlags = in.readInt();
personalLevel = in.readInt();
spam = in.readInt() != 0;
phishing = in.readInt() != 0;
muted = in.readInt() != 0;
color = in.readInt();
accountUri = in.readParcelable(null);
position = NO_POSITION;
localDeleteOnUpdate = false;
conversationInfo = in.readParcelable(loader);
conversationBaseUri = in.readParcelable(null);
isRemote = in.readInt() != 0;
attachmentPreviews = null;
attachmentPreviewUri0 = in.readString();
attachmentPreviewUri1 = in.readString();
attachmentPreviewStates = in.readInt();
attachmentPreviewsCount = in.readInt();
}
@Override
public String toString() {
// log extra info at DEBUG level or finer
final StringBuilder sb = new StringBuilder("[conversation id=");
sb.append(id);
if (LogUtils.isLoggable(LOG_TAG, LogUtils.DEBUG)) {
sb.append(", subject=");
sb.append(subject);
}
sb.append("]");
return sb.toString();
}
public static final ClassLoaderCreator<Conversation> CREATOR =
new ClassLoaderCreator<Conversation>() {
@Override
public Conversation createFromParcel(Parcel source) {
return new Conversation(source, null);
}
@Override
public Conversation createFromParcel(Parcel source, ClassLoader loader) {
return new Conversation(source, loader);
}
@Override
public Conversation[] newArray(int size) {
return new Conversation[size];
}
};
public static final Uri MOVE_CONVERSATIONS_URI = Uri.parse("content://moveconversations");
/**
* The column that needs to be updated to change the folders for a conversation.
*/
public static final String UPDATE_FOLDER_COLUMN = ConversationColumns.RAW_FOLDERS;
public Conversation(Cursor cursor) {
if (cursor == null) {
throw new IllegalArgumentException("Creating conversation from null cursor");
}
id = cursor.getLong(UIProvider.CONVERSATION_ID_COLUMN);
uri = Uri.parse(cursor.getString(UIProvider.CONVERSATION_URI_COLUMN));
dateMs = cursor.getLong(UIProvider.CONVERSATION_DATE_RECEIVED_MS_COLUMN);
final String subj = cursor.getString(UIProvider.CONVERSATION_SUBJECT_COLUMN);
// Don't allow null subject
if (subj == null) {
subject = "";
} else {
subject = subj;
}
hasAttachments = cursor.getInt(UIProvider.CONVERSATION_HAS_ATTACHMENTS_COLUMN) != 0;
String messageList = cursor.getString(UIProvider.CONVERSATION_MESSAGE_LIST_URI_COLUMN);
messageListUri = !TextUtils.isEmpty(messageList) ? Uri.parse(messageList) : null;
sendingState = cursor.getInt(UIProvider.CONVERSATION_SENDING_STATE_COLUMN);
priority = cursor.getInt(UIProvider.CONVERSATION_PRIORITY_COLUMN);
read = cursor.getInt(UIProvider.CONVERSATION_READ_COLUMN) != 0;
seen = cursor.getInt(UIProvider.CONVERSATION_SEEN_COLUMN) != 0;
starred = cursor.getInt(UIProvider.CONVERSATION_STARRED_COLUMN) != 0;
rawFolders = readRawFolders(cursor);
convFlags = cursor.getInt(UIProvider.CONVERSATION_FLAGS_COLUMN);
personalLevel = cursor.getInt(UIProvider.CONVERSATION_PERSONAL_LEVEL_COLUMN);
spam = cursor.getInt(UIProvider.CONVERSATION_IS_SPAM_COLUMN) != 0;
phishing = cursor.getInt(UIProvider.CONVERSATION_IS_PHISHING_COLUMN) != 0;
muted = cursor.getInt(UIProvider.CONVERSATION_MUTED_COLUMN) != 0;
color = cursor.getInt(UIProvider.CONVERSATION_COLOR_COLUMN);
String account = cursor.getString(UIProvider.CONVERSATION_ACCOUNT_URI_COLUMN);
accountUri = !TextUtils.isEmpty(account) ? Uri.parse(account) : null;
position = NO_POSITION;
localDeleteOnUpdate = false;
conversationInfo = readConversationInfo(cursor);
if (conversationInfo == null) {
LogUtils.wtf(LOG_TAG, "Null conversation info from cursor");
}
final String conversationBase =
cursor.getString(UIProvider.CONVERSATION_BASE_URI_COLUMN);
conversationBaseUri = !TextUtils.isEmpty(conversationBase) ?
Uri.parse(conversationBase) : null;
isRemote = cursor.getInt(UIProvider.CONVERSATION_REMOTE_COLUMN) != 0;
attachmentPreviews = null;
attachmentPreviewUri0 = cursor.getString(
UIProvider.CONVERSATION_ATTACHMENT_PREVIEW_URI0_COLUMN);
attachmentPreviewUri1 = cursor.getString(
UIProvider.CONVERSATION_ATTACHMENT_PREVIEW_URI1_COLUMN);
attachmentPreviewStates = cursor.getInt(
UIProvider.CONVERSATION_ATTACHMENT_PREVIEW_STATES_COLUMN);
attachmentPreviewsCount = cursor.getInt(
UIProvider.CONVERSATION_ATTACHMENT_PREVIEWS_COUNT_COLUMN);
}
public Conversation(Conversation other) {
if (other == null) {
throw new IllegalArgumentException("Copying null conversation");
}
id = other.id;
uri = other.uri;
dateMs = other.dateMs;
subject = other.subject;
hasAttachments = other.hasAttachments;
messageListUri = other.messageListUri;
sendingState = other.sendingState;
priority = other.priority;
read = other.read;
seen = other.seen;
starred = other.starred;
rawFolders = other.rawFolders; // FolderList is immutable, shallow copy is OK
convFlags = other.convFlags;
personalLevel = other.personalLevel;
spam = other.spam;
phishing = other.phishing;
muted = other.muted;
color = other.color;
accountUri = other.accountUri;
position = other.position;
localDeleteOnUpdate = other.localDeleteOnUpdate;
// although ConversationInfo is mutable (see ConversationInfo.markRead), applyCachedValues
// will overwrite this if cached changes exist anyway, so a shallow copy is OK
conversationInfo = other.conversationInfo;
conversationBaseUri = other.conversationBaseUri;
isRemote = other.isRemote;
attachmentPreviews = null;
attachmentPreviewUri0 = other.attachmentPreviewUri0;
attachmentPreviewUri1 = other.attachmentPreviewUri1;
attachmentPreviewStates = other.attachmentPreviewStates;
attachmentPreviewsCount = other.attachmentPreviewsCount;
}
public Conversation(long id, Uri uri, String subject, long dateMs,
boolean hasAttachment, Uri messageListUri,
int sendingState, int priority, boolean read,
boolean seen, boolean starred, FolderList rawFolders, int convFlags, int personalLevel,
boolean spam, boolean phishing, boolean muted, Uri accountUri,
ConversationInfo conversationInfo, Uri conversationBase, boolean isRemote,
String attachmentPreviewUri0, String attachmentPreviewUri1, int attachmentPreviewStates,
int attachmentPreviewsCount) {
if (conversationInfo == null) {
throw new IllegalArgumentException("Null conversationInfo");
}
this.id = id;
this.uri = uri;
this.subject = subject;
this.dateMs = dateMs;
this.hasAttachments = hasAttachment;
this.messageListUri = messageListUri;
this.sendingState = sendingState;
this.priority = priority;
this.read = read;
this.seen = seen;
this.starred = starred;
this.rawFolders = rawFolders;
this.convFlags = convFlags;
this.personalLevel = personalLevel;
this.spam = spam;
this.phishing = phishing;
this.muted = muted;
this.color = 0;
this.accountUri = accountUri;
this.conversationInfo = conversationInfo;
this.conversationBaseUri = conversationBase;
this.isRemote = isRemote;
this.attachmentPreviews = null;
this.attachmentPreviewUri0 = attachmentPreviewUri0;
this.attachmentPreviewUri1 = attachmentPreviewUri1;
this.attachmentPreviewStates = attachmentPreviewStates;
this.attachmentPreviewsCount = attachmentPreviewsCount;
}
public static class Builder {
private long mId;
private Uri mUri;
private String mSubject;
private long mDateMs;
private boolean mHasAttachments;
private Uri mMessageListUri;
private int mSendingState;
private int mPriority;
private boolean mRead;
private boolean mSeen;
private boolean mStarred;
private FolderList mRawFolders;
private int mConvFlags;
private int mPersonalLevel;
private boolean mSpam;
private boolean mPhishing;
private boolean mMuted;
private Uri mAccountUri;
private ConversationInfo mConversationInfo;
private Uri mConversationBaseUri;
private boolean mIsRemote;
private String mAttachmentPreviewUri0;
private String mAttachmentPreviewUri1;
private int mAttachmentPreviewStates;
private int mAttachmentPreviewsCount;
public Builder setId(long id) {
mId = id;
return this;
}
public Builder setUri(Uri uri) {
mUri = uri;
return this;
}
public Builder setSubject(String subject) {
mSubject = subject;
return this;
}
public Builder setDateMs(long dateMs) {
mDateMs = dateMs;
return this;
}
public Builder setHasAttachments(boolean hasAttachments) {
mHasAttachments = hasAttachments;
return this;
}
public Builder setMessageListUri(Uri messageListUri) {
mMessageListUri = messageListUri;
return this;
}
public Builder setSendingState(int sendingState) {
mSendingState = sendingState;
return this;
}
public Builder setPriority(int priority) {
mPriority = priority;
return this;
}
public Builder setRead(boolean read) {
mRead = read;
return this;
}
public Builder setSeen(boolean seen) {
mSeen = seen;
return this;
}
public Builder setStarred(boolean starred) {
mStarred = starred;
return this;
}
public Builder setRawFolders(FolderList rawFolders) {
mRawFolders = rawFolders;
return this;
}
public Builder setConvFlags(int convFlags) {
mConvFlags = convFlags;
return this;
}
public Builder setPersonalLevel(int personalLevel) {
mPersonalLevel = personalLevel;
return this;
}
public Builder setSpam(boolean spam) {
mSpam = spam;
return this;
}
public Builder setPhishing(boolean phishing) {
mPhishing = phishing;
return this;
}
public Builder setMuted(boolean muted) {
mMuted = muted;
return this;
}
public Builder setAccountUri(Uri accountUri) {
mAccountUri = accountUri;
return this;
}
public Builder setConversationInfo(ConversationInfo conversationInfo) {
if (conversationInfo == null) {
throw new IllegalArgumentException("Can't set null ConversationInfo");
}
mConversationInfo = conversationInfo;
return this;
}
public Builder setConversationBaseUri(Uri conversationBaseUri) {
mConversationBaseUri = conversationBaseUri;
return this;
}
public Builder setIsRemote(boolean isRemote) {
mIsRemote = isRemote;
return this;
}
public Builder setAttachmentPreviewUri0(String attachmentPreviewUri0) {
mAttachmentPreviewUri0 = attachmentPreviewUri0;
return this;
}
public Builder setAttachmentPreviewUri1(String attachmentPreviewUri1) {
mAttachmentPreviewUri1 = attachmentPreviewUri1;
return this;
}
public Builder setAttachmentPreviewStates(int attachmentPreviewStates) {
mAttachmentPreviewStates = attachmentPreviewStates;
return this;
}
public Builder setAttachmentPreviewsCount(int attachmentPreviewsCount) {
mAttachmentPreviewsCount = attachmentPreviewsCount;
return this;
}
public Builder() {}
public Conversation build() {
if (mConversationInfo == null) {
LogUtils.d(LOG_TAG, "Null conversationInfo in Builder");
mConversationInfo = new ConversationInfo();
}
return new Conversation(mId, mUri, mSubject, mDateMs, mHasAttachments, mMessageListUri,
mSendingState, mPriority, mRead, mSeen, mStarred, mRawFolders, mConvFlags,
mPersonalLevel, mSpam, mPhishing, mMuted, mAccountUri, mConversationInfo,
mConversationBaseUri, mIsRemote, mAttachmentPreviewUri0, mAttachmentPreviewUri1,
mAttachmentPreviewStates, mAttachmentPreviewsCount);
}
}
private static final Bundle CONVERSATION_INFO_REQUEST;
private static final Bundle RAW_FOLDERS_REQUEST;
static {
RAW_FOLDERS_REQUEST = new Bundle(2);
RAW_FOLDERS_REQUEST.putBoolean(
ConversationCursorCommand.COMMAND_GET_RAW_FOLDERS, true);
RAW_FOLDERS_REQUEST.putInt(
ConversationCursorCommand.COMMAND_KEY_OPTIONS,
ConversationCursorCommand.OPTION_MOVE_POSITION);
CONVERSATION_INFO_REQUEST = new Bundle(2);
CONVERSATION_INFO_REQUEST.putBoolean(
ConversationCursorCommand.COMMAND_GET_CONVERSATION_INFO, true);
CONVERSATION_INFO_REQUEST.putInt(
ConversationCursorCommand.COMMAND_KEY_OPTIONS,
ConversationCursorCommand.OPTION_MOVE_POSITION);
}
private static ConversationInfo readConversationInfo(Cursor cursor) {
final ConversationInfo ci;
if (cursor instanceof ConversationCursor) {
final byte[] blob = ((ConversationCursor) cursor).getCachedBlob(
UIProvider.CONVERSATION_INFO_COLUMN);
if (blob != null && blob.length > 0) {
return ConversationInfo.fromBlob(blob);
}
}
final Bundle response = cursor.respond(CONVERSATION_INFO_REQUEST);
if (response.containsKey(ConversationCursorCommand.COMMAND_GET_CONVERSATION_INFO)) {
ci = response.getParcelable(ConversationCursorCommand.COMMAND_GET_CONVERSATION_INFO);
} else {
// legacy fallback
ci = ConversationInfo.fromBlob(cursor.getBlob(UIProvider.CONVERSATION_INFO_COLUMN));
}
return ci;
}
private static FolderList readRawFolders(Cursor cursor) {
final FolderList fl;
if (cursor instanceof ConversationCursor) {
final byte[] blob = ((ConversationCursor) cursor).getCachedBlob(
UIProvider.CONVERSATION_RAW_FOLDERS_COLUMN);
if (blob != null && blob.length > 0) {
return FolderList.fromBlob(blob);
}
}
final Bundle response = cursor.respond(RAW_FOLDERS_REQUEST);
if (response.containsKey(ConversationCursorCommand.COMMAND_GET_RAW_FOLDERS)) {
fl = response.getParcelable(ConversationCursorCommand.COMMAND_GET_RAW_FOLDERS);
} else {
// legacy fallback
// TODO: delete this once Email supports the respond call
fl = FolderList.fromBlob(
cursor.getBlob(UIProvider.CONVERSATION_RAW_FOLDERS_COLUMN));
}
return fl;
}
/**
* Apply any column values from the given {@link ContentValues} (where column names are the
* keys) to this conversation.
*
*/
public void applyCachedValues(ContentValues values) {
if (values == null) {
return;
}
for (String key : values.keySet()) {
final Object val = values.get(key);
LogUtils.i(LOG_TAG, "Conversation: applying cached value to col=%s val=%s", key,
val);
if (ConversationColumns.READ.equals(key)) {
read = (Integer) val != 0;
} else if (ConversationColumns.CONVERSATION_INFO.equals(key)) {
final ConversationInfo cachedCi = ConversationInfo.fromBlob((byte[]) val);
if (cachedCi == null) {
LogUtils.d(LOG_TAG, "Null ConversationInfo in applyCachedValues");
} else {
conversationInfo.overwriteWith(cachedCi);
}
} else if (ConversationColumns.FLAGS.equals(key)) {
convFlags = (Integer) val;
} else if (ConversationColumns.STARRED.equals(key)) {
starred = (Integer) val != 0;
} else if (ConversationColumns.SEEN.equals(key)) {
seen = (Integer) val != 0;
} else if (ConversationColumns.RAW_FOLDERS.equals(key)) {
rawFolders = FolderList.fromBlob((byte[]) val);
} else if (ConversationColumns.VIEWED.equals(key)) {
// ignore. this is not read from the cursor, either.
} else {
LogUtils.e(LOG_TAG, new UnsupportedOperationException(),
"unsupported cached conv value in col=%s", key);
}
}
}
/**
* Get the <strong>immutable</strong> list of {@link Folder}s for this conversation. To modify
* this list, make a new {@link FolderList} and use {@link #setRawFolders(FolderList)}.
*
* @return <strong>Immutable</strong> list of {@link Folder}s.
*/
public List<Folder> getRawFolders() {
return rawFolders.folders;
}
public void setRawFolders(FolderList folders) {
rawFolders = folders;
}
@Override
public boolean equals(Object o) {
if (o instanceof Conversation) {
Conversation conv = (Conversation) o;
return conv.uri.equals(uri);
}
return false;
}
@Override
public int hashCode() {
return uri.hashCode();
}
/**
* Get if this conversation is marked as high priority.
*/
public boolean isImportant() {
return priority == UIProvider.ConversationPriority.IMPORTANT;
}
/**
* Get if this conversation is mostly dead
*/
public boolean isMostlyDead() {
return (convFlags & FLAG_MOSTLY_DEAD) != 0;
}
/**
* Returns true if the URI of the conversation specified as the needle was
* found in the collection of conversations specified as the haystack. False
* otherwise. This method is safe to call with null arguments.
*
* @param haystack
* @param needle
* @return true if the needle was found in the haystack, false otherwise.
*/
public final static boolean contains(Collection<Conversation> haystack, Conversation needle) {
// If the haystack is empty, it cannot contain anything.
if (haystack == null || haystack.size() <= 0) {
return false;
}
// The null folder exists everywhere.
if (needle == null) {
return true;
}
final long toFind = needle.id;
for (final Conversation c : haystack) {
if (toFind == c.id) {
return true;
}
}
return false;
}
/**
* Returns a collection of a single conversation. This method always returns
* a valid collection even if the input conversation is null.
*
* @param in a conversation, possibly null.
* @return a collection of the conversation.
*/
public static Collection<Conversation> listOf(Conversation in) {
final Collection<Conversation> target = (in == null) ? EMPTY : ImmutableList.of(in);
return target;
}
/**
* Get the snippet for this conversation.
*/
public String getSnippet() {
return !TextUtils.isEmpty(conversationInfo.firstSnippet) ?
conversationInfo.firstSnippet : "";
}
/**
* Get the number of messages for this conversation.
*/
public int getNumMessages() {
return conversationInfo.messageCount;
}
/**
* Get the number of drafts for this conversation.
*/
public int numDrafts() {
return conversationInfo.draftCount;
}
public boolean isViewed() {
return viewed;
}
public void markViewed() {
viewed = true;
}
public String getBaseUri(String defaultValue) {
return conversationBaseUri != null ? conversationBaseUri.toString() : defaultValue;
}
public ArrayList<String> getAttachmentPreviewUris() {
if (attachmentPreviews == null) {
attachmentPreviews = Lists.newArrayListWithCapacity(2);
if (!TextUtils.isEmpty(attachmentPreviewUri0)) {
attachmentPreviews.add(attachmentPreviewUri0);
}
if (!TextUtils.isEmpty(attachmentPreviewUri1)) {
attachmentPreviews.add(attachmentPreviewUri1);
}
}
return attachmentPreviews;
}
/**
* Create a human-readable string of all the conversations
* @param collection Any collection of conversations
* @return string with a human readable representation of the conversations.
*/
public static String toString(Collection<Conversation> collection) {
final StringBuilder out = new StringBuilder(collection.size() + " conversations:");
int count = 0;
for (final Conversation c : collection) {
count++;
// Indent the conversations to make them easy to read in debug
// output.
out.append(" " + count + ": " + c.toString() + "\n");
}
return out.toString();
}
/**
* Returns an empty string if the specified string is null
*/
private static String emptyIfNull(String in) {
return in != null ? in : EMPTY_STRING;
}
/**
* Get the properly formatted subject and snippet string for display a
* conversation.
*/
public static String getSubjectAndSnippetForDisplay(Context context,
String badgeText, String filteredSubject, String snippet) {
if (TextUtils.isEmpty(filteredSubject) && TextUtils.isEmpty(snippet)) {
return "";
} else if (TextUtils.isEmpty(filteredSubject)) {
return snippet;
} else if (TextUtils.isEmpty(snippet)) {
return filteredSubject;
} else if (!TextUtils.isEmpty(badgeText)) {
if (sBadgeSubjectAndSnippet == null) {
sBadgeSubjectAndSnippet = context.getString(R.string.badge_subject_and_snippet);
}
return String.format(sBadgeSubjectAndSnippet, badgeText, filteredSubject, snippet);
}
if (sSubjectAndSnippet == null) {
sSubjectAndSnippet = context.getString(R.string.subject_and_snippet);
}
return String.format(sSubjectAndSnippet, filteredSubject, snippet);
}
/**
* Public object that knows how to construct Conversation given Cursors. This is not used by
* {@link ConversationCursor} or {@link ConversationCursorLoader}.
*/
public static final CursorCreator<Conversation> FACTORY = new CursorCreator<Conversation>() {
@Override
public Conversation createFromCursor(final Cursor c) {
return new Conversation(c);
}
@Override
public String toString() {
return "Conversation CursorCreator";
}
};
}