1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.messaging.datamodel.data;
18
19import android.database.Cursor;
20import android.net.Uri;
21import android.provider.BaseColumns;
22import android.text.TextUtils;
23
24import com.android.messaging.datamodel.DatabaseHelper;
25import com.android.messaging.datamodel.DatabaseHelper.ConversationColumns;
26import com.android.messaging.datamodel.DatabaseHelper.MessageColumns;
27import com.android.messaging.datamodel.DatabaseHelper.ParticipantColumns;
28import com.android.messaging.datamodel.DatabaseWrapper;
29import com.android.messaging.datamodel.action.DeleteConversationAction;
30import com.android.messaging.util.Assert;
31import com.android.messaging.util.Dates;
32import com.google.common.base.Joiner;
33
34import java.util.ArrayList;
35import java.util.List;
36
37/**
38 * Class wrapping the conversation list view used to display each item in conversation list
39 */
40public class ConversationListItemData {
41    private String mConversationId;
42    private String mName;
43    private String mIcon;
44    private boolean mIsRead;
45    private long mTimestamp;
46    private String mSnippetText;
47    private Uri mPreviewUri;
48    private String mPreviewContentType;
49    private long mParticipantContactId;
50    private String mParticipantLookupKey;
51    private String mOtherParticipantNormalizedDestination;
52    private String mSelfId;
53    private int mParticipantCount;
54    private boolean mNotificationEnabled;
55    private String mNotificationSoundUri;
56    private boolean mNotificationVibrate;
57    private boolean mIncludeEmailAddress;
58    private int mMessageStatus;
59    private int mMessageRawTelephonyStatus;
60    private boolean mShowDraft;
61    private Uri mDraftPreviewUri;
62    private String mDraftPreviewContentType;
63    private String mDraftSnippetText;
64    private boolean mIsArchived;
65    private String mSubject;
66    private String mDraftSubject;
67    private String mSnippetSenderFirstName;
68    private String mSnippetSenderDisplayDestination;
69
70    public ConversationListItemData() {
71    }
72
73    public void bind(final Cursor cursor) {
74        bind(cursor, false);
75    }
76
77    public void bind(final Cursor cursor, final boolean ignoreDraft) {
78        mConversationId = cursor.getString(INDEX_ID);
79        mName = cursor.getString(INDEX_CONVERSATION_NAME);
80        mIcon = cursor.getString(INDEX_CONVERSATION_ICON);
81        mSnippetText = cursor.getString(INDEX_SNIPPET_TEXT);
82        mTimestamp = cursor.getLong(INDEX_SORT_TIMESTAMP);
83        mIsRead = cursor.getInt(INDEX_READ) == 1;
84        final String previewUriString = cursor.getString(INDEX_PREVIEW_URI);
85        mPreviewUri = TextUtils.isEmpty(previewUriString) ? null : Uri.parse(previewUriString);
86        mPreviewContentType = cursor.getString(INDEX_PREVIEW_CONTENT_TYPE);
87        mParticipantContactId = cursor.getLong(INDEX_PARTICIPANT_CONTACT_ID);
88        mParticipantLookupKey = cursor.getString(INDEX_PARTICIPANT_LOOKUP_KEY);
89        mOtherParticipantNormalizedDestination = cursor.getString(
90                INDEX_OTHER_PARTICIPANT_NORMALIZED_DESTINATION);
91        mSelfId = cursor.getString(INDEX_SELF_ID);
92        mParticipantCount = cursor.getInt(INDEX_PARTICIPANT_COUNT);
93        mNotificationEnabled = cursor.getInt(INDEX_NOTIFICATION_ENABLED) == 1;
94        mNotificationSoundUri = cursor.getString(INDEX_NOTIFICATION_SOUND_URI);
95        mNotificationVibrate = cursor.getInt(INDEX_NOTIFICATION_VIBRATION) == 1;
96        mIncludeEmailAddress = cursor.getInt(INDEX_INCLUDE_EMAIL_ADDRESS) == 1;
97        mMessageStatus = cursor.getInt(INDEX_MESSAGE_STATUS);
98        mMessageRawTelephonyStatus = cursor.getInt(INDEX_MESSAGE_RAW_TELEPHONY_STATUS);
99        if (!ignoreDraft) {
100            mShowDraft = cursor.getInt(INDEX_SHOW_DRAFT) == 1;
101            final String draftPreviewUriString = cursor.getString(INDEX_DRAFT_PREVIEW_URI);
102            mDraftPreviewUri = TextUtils.isEmpty(draftPreviewUriString) ?
103                    null : Uri.parse(draftPreviewUriString);
104            mDraftPreviewContentType = cursor.getString(INDEX_DRAFT_PREVIEW_CONTENT_TYPE);
105            mDraftSnippetText = cursor.getString(INDEX_DRAFT_SNIPPET_TEXT);
106            mDraftSubject = cursor.getString(INDEX_DRAFT_SUBJECT_TEXT);
107        } else {
108            mShowDraft = false;
109            mDraftPreviewUri = null;
110            mDraftPreviewContentType = null;
111            mDraftSnippetText = null;
112            mDraftSubject = null;
113        }
114
115        mIsArchived = cursor.getInt(INDEX_ARCHIVE_STATUS) == 1;
116        mSubject = cursor.getString(INDEX_SUBJECT_TEXT);
117        mSnippetSenderFirstName = cursor.getString(INDEX_SNIPPET_SENDER_FIRST_NAME);
118        mSnippetSenderDisplayDestination =
119                cursor.getString(INDEX_SNIPPET_SENDER_DISPLAY_DESTINATION);
120    }
121
122    public String getConversationId() {
123        return mConversationId;
124    }
125
126    public String getName() {
127        return mName;
128    }
129
130    public String getIcon() {
131        return mIcon;
132    }
133
134    public boolean getIsRead() {
135        return mIsRead;
136    }
137
138    public String getFormattedTimestamp() {
139        return Dates.getConversationTimeString(mTimestamp).toString();
140    }
141
142    public long getTimestamp() {
143        return mTimestamp;
144    }
145
146    public String getSnippetText() {
147        return mSnippetText;
148    }
149
150    public Uri getPreviewUri() {
151        return mPreviewUri;
152    }
153
154    public String getPreviewContentType() {
155        return mPreviewContentType;
156    }
157
158    public long getParticipantContactId() {
159        return mParticipantContactId;
160    }
161
162    public String getParticipantLookupKey() {
163        return mParticipantLookupKey;
164    }
165
166    public String getOtherParticipantNormalizedDestination() {
167        return mOtherParticipantNormalizedDestination;
168    }
169
170    public String getSelfId() {
171        return mSelfId;
172    }
173
174    public int getParticipantCount() {
175        return mParticipantCount;
176    }
177
178    public boolean getIsGroup() {
179        // Participant count excludes self
180        return (mParticipantCount > 1);
181    }
182
183    public boolean getIncludeEmailAddress() {
184        return mIncludeEmailAddress;
185    }
186
187    public boolean getNotificationEnabled() {
188        return mNotificationEnabled;
189    }
190
191    public String getNotificationSoundUri() {
192        return mNotificationSoundUri;
193    }
194
195    public boolean getNotifiationVibrate() {
196        return mNotificationVibrate;
197    }
198
199    public final boolean getIsFailedStatus() {
200        return (mMessageStatus == MessageData.BUGLE_STATUS_OUTGOING_FAILED ||
201                mMessageStatus == MessageData.BUGLE_STATUS_OUTGOING_FAILED_EMERGENCY_NUMBER ||
202                mMessageStatus == MessageData.BUGLE_STATUS_INCOMING_DOWNLOAD_FAILED ||
203                mMessageStatus == MessageData.BUGLE_STATUS_INCOMING_EXPIRED_OR_NOT_AVAILABLE);
204    }
205
206    public final boolean getIsSendRequested() {
207        return (mMessageStatus == MessageData.BUGLE_STATUS_OUTGOING_YET_TO_SEND ||
208                mMessageStatus == MessageData.BUGLE_STATUS_OUTGOING_AWAITING_RETRY ||
209                mMessageStatus == MessageData.BUGLE_STATUS_OUTGOING_SENDING ||
210                mMessageStatus == MessageData.BUGLE_STATUS_OUTGOING_RESENDING);
211    }
212
213    public boolean getIsMessageTypeOutgoing() {
214        return !MessageData.getIsIncoming(mMessageStatus);
215    }
216
217    public int getMessageRawTelephonyStatus() {
218        return mMessageRawTelephonyStatus;
219    }
220
221    public int getMessageStatus() {
222        return mMessageStatus;
223    }
224
225    public boolean getShowDraft() {
226        return mShowDraft;
227    }
228
229    public String getDraftSnippetText() {
230        return mDraftSnippetText;
231    }
232
233    public Uri getDraftPreviewUri() {
234        return mDraftPreviewUri;
235    }
236
237    public String getDraftPreviewContentType() {
238        return mDraftPreviewContentType;
239    }
240
241    public boolean getIsArchived() {
242        return mIsArchived;
243    }
244
245    public String getSubject() {
246        return mSubject;
247    }
248
249    public String getDraftSubject() {
250        return mDraftSubject;
251    }
252
253    public String getSnippetSenderName() {
254        if (!TextUtils.isEmpty(mSnippetSenderFirstName)) {
255            return mSnippetSenderFirstName;
256        }
257        return mSnippetSenderDisplayDestination;
258    }
259
260    public void deleteConversation() {
261        DeleteConversationAction.deleteConversation(mConversationId, mTimestamp);
262    }
263
264    /**
265     * Get the name of the view for this data item
266     */
267    public static final String getConversationListView() {
268        return CONVERSATION_LIST_VIEW;
269    }
270
271    public static final String getConversationListViewSql() {
272        return CONVERSATION_LIST_VIEW_SQL;
273    }
274
275    private static final String CONVERSATION_LIST_VIEW = "conversation_list_view";
276
277    private static final String CONVERSATION_LIST_VIEW_PROJECTION =
278            DatabaseHelper.CONVERSATIONS_TABLE + '.' + ConversationColumns._ID
279            + " as " + ConversationListViewColumns._ID + ", "
280            + DatabaseHelper.CONVERSATIONS_TABLE + '.' + ConversationColumns.NAME
281            + " as " + ConversationListViewColumns.NAME + ", "
282            + DatabaseHelper.CONVERSATIONS_TABLE + '.' + ConversationColumns.CURRENT_SELF_ID
283            + " as " + ConversationListViewColumns.CURRENT_SELF_ID + ", "
284            + DatabaseHelper.CONVERSATIONS_TABLE + '.' + ConversationColumns.ARCHIVE_STATUS
285            + " as " + ConversationListViewColumns.ARCHIVE_STATUS + ", "
286            + DatabaseHelper.MESSAGES_TABLE + '.' + MessageColumns.READ
287            + " as " + ConversationListViewColumns.READ + ", "
288            + DatabaseHelper.CONVERSATIONS_TABLE + '.' + ConversationColumns.ICON
289            + " as " + ConversationListViewColumns.ICON + ", "
290            + DatabaseHelper.CONVERSATIONS_TABLE + '.' + ConversationColumns.PARTICIPANT_CONTACT_ID
291            + " as " + ConversationListViewColumns.PARTICIPANT_CONTACT_ID + ", "
292            + DatabaseHelper.CONVERSATIONS_TABLE + '.' + ConversationColumns.PARTICIPANT_LOOKUP_KEY
293            + " as " + ConversationListViewColumns.PARTICIPANT_LOOKUP_KEY + ", "
294            + DatabaseHelper.CONVERSATIONS_TABLE + '.'
295                    + ConversationColumns.OTHER_PARTICIPANT_NORMALIZED_DESTINATION
296            + " as " + ConversationListViewColumns.OTHER_PARTICIPANT_NORMALIZED_DESTINATION + ", "
297            + DatabaseHelper.CONVERSATIONS_TABLE + '.' + ConversationColumns.SORT_TIMESTAMP
298            + " as " + ConversationListViewColumns.SORT_TIMESTAMP + ", "
299            + DatabaseHelper.CONVERSATIONS_TABLE + '.' + ConversationColumns.SHOW_DRAFT
300            + " as " + ConversationListViewColumns.SHOW_DRAFT + ", "
301            + DatabaseHelper.CONVERSATIONS_TABLE + '.' + ConversationColumns.DRAFT_SNIPPET_TEXT
302            + " as " + ConversationListViewColumns.DRAFT_SNIPPET_TEXT + ", "
303            + DatabaseHelper.CONVERSATIONS_TABLE + '.' + ConversationColumns.DRAFT_PREVIEW_URI
304            + " as " + ConversationListViewColumns.DRAFT_PREVIEW_URI + ", "
305            + DatabaseHelper.CONVERSATIONS_TABLE + '.' + ConversationColumns.DRAFT_SUBJECT_TEXT
306            + " as " + ConversationListViewColumns.DRAFT_SUBJECT_TEXT + ", "
307            + DatabaseHelper.CONVERSATIONS_TABLE + '.'
308                    + ConversationColumns.DRAFT_PREVIEW_CONTENT_TYPE
309            + " as " + ConversationListViewColumns.DRAFT_PREVIEW_CONTENT_TYPE + ", "
310            + DatabaseHelper.CONVERSATIONS_TABLE + '.' + ConversationColumns.PREVIEW_URI
311            + " as " + ConversationListViewColumns.PREVIEW_URI + ", "
312            + DatabaseHelper.CONVERSATIONS_TABLE + '.' + ConversationColumns.PREVIEW_CONTENT_TYPE
313            + " as " + ConversationListViewColumns.PREVIEW_CONTENT_TYPE + ", "
314            + DatabaseHelper.CONVERSATIONS_TABLE + '.' + ConversationColumns.PARTICIPANT_COUNT
315            + " as " + ConversationListViewColumns.PARTICIPANT_COUNT + ", "
316            + DatabaseHelper.CONVERSATIONS_TABLE + '.' + ConversationColumns.NOTIFICATION_ENABLED
317            + " as " + ConversationListViewColumns.NOTIFICATION_ENABLED + ", "
318            + DatabaseHelper.CONVERSATIONS_TABLE + '.' + ConversationColumns.NOTIFICATION_SOUND_URI
319            + " as " + ConversationListViewColumns.NOTIFICATION_SOUND_URI + ", "
320            + DatabaseHelper.CONVERSATIONS_TABLE + '.' + ConversationColumns.NOTIFICATION_VIBRATION
321            + " as " + ConversationListViewColumns.NOTIFICATION_VIBRATION + ", "
322            + DatabaseHelper.CONVERSATIONS_TABLE + '.' +
323                    ConversationColumns.INCLUDE_EMAIL_ADDRESS
324            + " as " + ConversationListViewColumns.INCLUDE_EMAIL_ADDRESS + ", "
325            + DatabaseHelper.MESSAGES_TABLE + '.' + MessageColumns.STATUS
326            + " as " + ConversationListViewColumns.MESSAGE_STATUS + ", "
327            + DatabaseHelper.MESSAGES_TABLE + '.' + MessageColumns.RAW_TELEPHONY_STATUS
328            + " as " + ConversationListViewColumns.MESSAGE_RAW_TELEPHONY_STATUS + ", "
329            + DatabaseHelper.MESSAGES_TABLE + '.' + MessageColumns._ID
330            + " as " + ConversationListViewColumns.MESSAGE_ID + ", "
331            + DatabaseHelper.PARTICIPANTS_TABLE + '.' + ParticipantColumns.FIRST_NAME
332            + " as " + ConversationListViewColumns.SNIPPET_SENDER_FIRST_NAME + ", "
333            + DatabaseHelper.PARTICIPANTS_TABLE + '.' + ParticipantColumns.DISPLAY_DESTINATION
334            + " as " + ConversationListViewColumns.SNIPPET_SENDER_DISPLAY_DESTINATION;
335
336    private static final String JOIN_PARTICIPANTS =
337            " LEFT JOIN " + DatabaseHelper.PARTICIPANTS_TABLE + " ON ("
338            + DatabaseHelper.MESSAGES_TABLE + '.' + MessageColumns.SENDER_PARTICIPANT_ID
339            + '=' + DatabaseHelper.PARTICIPANTS_TABLE + '.' + DatabaseHelper.ParticipantColumns._ID
340            + ") ";
341
342    // View that makes latest message read flag available with rest of conversation data.
343    private static final String CONVERSATION_LIST_VIEW_SQL = "CREATE VIEW " +
344            CONVERSATION_LIST_VIEW + " AS SELECT "
345            + CONVERSATION_LIST_VIEW_PROJECTION + ", "
346            // Snippet not part of the base projection shared with search view
347            + DatabaseHelper.CONVERSATIONS_TABLE + '.' + ConversationColumns.SNIPPET_TEXT
348            + " as " + ConversationListViewColumns.SNIPPET_TEXT + ", "
349            + DatabaseHelper.CONVERSATIONS_TABLE + '.' + ConversationColumns.SUBJECT_TEXT
350            + " as " + ConversationListViewColumns.SUBJECT_TEXT + " "
351            + " FROM " + DatabaseHelper.CONVERSATIONS_TABLE
352            + " LEFT JOIN " + DatabaseHelper.MESSAGES_TABLE + " ON ("
353            + DatabaseHelper.CONVERSATIONS_TABLE + '.' +  ConversationColumns.LATEST_MESSAGE_ID
354            + '=' + DatabaseHelper.MESSAGES_TABLE + '.' + MessageColumns._ID + ") "
355            + JOIN_PARTICIPANTS
356            + "ORDER BY " + DatabaseHelper.CONVERSATIONS_TABLE + '.'
357            + ConversationColumns.SORT_TIMESTAMP + " DESC";
358
359    public static class ConversationListViewColumns implements BaseColumns {
360        public static final String _ID = ConversationColumns._ID;
361        static final String NAME = ConversationColumns.NAME;
362        static final String ARCHIVE_STATUS = ConversationColumns.ARCHIVE_STATUS;
363        static final String READ = MessageColumns.READ;
364        static final String SORT_TIMESTAMP = ConversationColumns.SORT_TIMESTAMP;
365        static final String PREVIEW_URI = ConversationColumns.PREVIEW_URI;
366        static final String PREVIEW_CONTENT_TYPE = ConversationColumns.PREVIEW_CONTENT_TYPE;
367        static final String SNIPPET_TEXT = ConversationColumns.SNIPPET_TEXT;
368        static final String SUBJECT_TEXT = ConversationColumns.SUBJECT_TEXT;
369        static final String ICON = ConversationColumns.ICON;
370        static final String SHOW_DRAFT = ConversationColumns.SHOW_DRAFT;
371        static final String DRAFT_SUBJECT_TEXT = ConversationColumns.DRAFT_SUBJECT_TEXT;
372        static final String DRAFT_PREVIEW_URI = ConversationColumns.DRAFT_PREVIEW_URI;
373        static final String DRAFT_PREVIEW_CONTENT_TYPE =
374                ConversationColumns.DRAFT_PREVIEW_CONTENT_TYPE;
375        static final String DRAFT_SNIPPET_TEXT = ConversationColumns.DRAFT_SNIPPET_TEXT;
376        static final String PARTICIPANT_CONTACT_ID = ConversationColumns.PARTICIPANT_CONTACT_ID;
377        static final String PARTICIPANT_LOOKUP_KEY = ConversationColumns.PARTICIPANT_LOOKUP_KEY;
378        static final String OTHER_PARTICIPANT_NORMALIZED_DESTINATION =
379                ConversationColumns.OTHER_PARTICIPANT_NORMALIZED_DESTINATION;
380        static final String CURRENT_SELF_ID = ConversationColumns.CURRENT_SELF_ID;
381        static final String PARTICIPANT_COUNT = ConversationColumns.PARTICIPANT_COUNT;
382        static final String NOTIFICATION_ENABLED = ConversationColumns.NOTIFICATION_ENABLED;
383        static final String NOTIFICATION_SOUND_URI = ConversationColumns.NOTIFICATION_SOUND_URI;
384        static final String NOTIFICATION_VIBRATION = ConversationColumns.NOTIFICATION_VIBRATION;
385        static final String INCLUDE_EMAIL_ADDRESS =
386                ConversationColumns.INCLUDE_EMAIL_ADDRESS;
387        static final String MESSAGE_STATUS = MessageColumns.STATUS;
388        static final String MESSAGE_RAW_TELEPHONY_STATUS = MessageColumns.RAW_TELEPHONY_STATUS;
389        static final String MESSAGE_ID = "message_id";
390        static final String SNIPPET_SENDER_FIRST_NAME = "snippet_sender_first_name";
391        static final String SNIPPET_SENDER_DISPLAY_DESTINATION =
392                "snippet_sender_display_destination";
393    }
394
395    public static final String[] PROJECTION = {
396        ConversationListViewColumns._ID,
397        ConversationListViewColumns.NAME,
398        ConversationListViewColumns.ICON,
399        ConversationListViewColumns.SNIPPET_TEXT,
400        ConversationListViewColumns.SORT_TIMESTAMP,
401        ConversationListViewColumns.READ,
402        ConversationListViewColumns.PREVIEW_URI,
403        ConversationListViewColumns.PREVIEW_CONTENT_TYPE,
404        ConversationListViewColumns.PARTICIPANT_CONTACT_ID,
405        ConversationListViewColumns.PARTICIPANT_LOOKUP_KEY,
406        ConversationListViewColumns.OTHER_PARTICIPANT_NORMALIZED_DESTINATION,
407        ConversationListViewColumns.PARTICIPANT_COUNT,
408        ConversationListViewColumns.CURRENT_SELF_ID,
409        ConversationListViewColumns.NOTIFICATION_ENABLED,
410        ConversationListViewColumns.NOTIFICATION_SOUND_URI,
411        ConversationListViewColumns.NOTIFICATION_VIBRATION,
412        ConversationListViewColumns.INCLUDE_EMAIL_ADDRESS,
413        ConversationListViewColumns.MESSAGE_STATUS,
414        ConversationListViewColumns.SHOW_DRAFT,
415        ConversationListViewColumns.DRAFT_PREVIEW_URI,
416        ConversationListViewColumns.DRAFT_PREVIEW_CONTENT_TYPE,
417        ConversationListViewColumns.DRAFT_SNIPPET_TEXT,
418        ConversationListViewColumns.ARCHIVE_STATUS,
419        ConversationListViewColumns.MESSAGE_ID,
420        ConversationListViewColumns.SUBJECT_TEXT,
421        ConversationListViewColumns.DRAFT_SUBJECT_TEXT,
422        ConversationListViewColumns.MESSAGE_RAW_TELEPHONY_STATUS,
423        ConversationListViewColumns.SNIPPET_SENDER_FIRST_NAME,
424        ConversationListViewColumns.SNIPPET_SENDER_DISPLAY_DESTINATION,
425    };
426
427    private static final int INDEX_ID = 0;
428    private static final int INDEX_CONVERSATION_NAME = 1;
429    private static final int INDEX_CONVERSATION_ICON = 2;
430    private static final int INDEX_SNIPPET_TEXT = 3;
431    private static final int INDEX_SORT_TIMESTAMP = 4;
432    private static final int INDEX_READ = 5;
433    private static final int INDEX_PREVIEW_URI = 6;
434    private static final int INDEX_PREVIEW_CONTENT_TYPE = 7;
435    private static final int INDEX_PARTICIPANT_CONTACT_ID = 8;
436    private static final int INDEX_PARTICIPANT_LOOKUP_KEY = 9;
437    private static final int INDEX_OTHER_PARTICIPANT_NORMALIZED_DESTINATION = 10;
438    private static final int INDEX_PARTICIPANT_COUNT = 11;
439    private static final int INDEX_SELF_ID = 12;
440    private static final int INDEX_NOTIFICATION_ENABLED = 13;
441    private static final int INDEX_NOTIFICATION_SOUND_URI = 14;
442    private static final int INDEX_NOTIFICATION_VIBRATION = 15;
443    private static final int INDEX_INCLUDE_EMAIL_ADDRESS = 16;
444    private static final int INDEX_MESSAGE_STATUS = 17;
445    private static final int INDEX_SHOW_DRAFT = 18;
446    private static final int INDEX_DRAFT_PREVIEW_URI = 19;
447    private static final int INDEX_DRAFT_PREVIEW_CONTENT_TYPE = 20;
448    private static final int INDEX_DRAFT_SNIPPET_TEXT = 21;
449    private static final int INDEX_ARCHIVE_STATUS = 22;
450    private static final int INDEX_MESSAGE_ID = 23;
451    private static final int INDEX_SUBJECT_TEXT = 24;
452    private static final int INDEX_DRAFT_SUBJECT_TEXT = 25;
453    private static final int INDEX_MESSAGE_RAW_TELEPHONY_STATUS = 26;
454    private static final int INDEX_SNIPPET_SENDER_FIRST_NAME = 27;
455    private static final int INDEX_SNIPPET_SENDER_DISPLAY_DESTINATION = 28;
456
457    private static final String DIVIDER_TEXT = ", ";
458
459    /**
460     * Get a conversation from the local DB based on the conversation id.
461     *
462     * @param dbWrapper       The database
463     * @param conversationId  The conversation Id to read
464     * @return The existing conversation or null
465     */
466    public static ConversationListItemData getExistingConversation(final DatabaseWrapper dbWrapper,
467            final String conversationId) {
468        ConversationListItemData conversation = null;
469
470        // Look for an existing conversation in the db with this conversation id
471        Cursor cursor = null;
472        try {
473            // TODO: Should we be able to read a row from just the conversation table?
474            cursor = dbWrapper.query(getConversationListView(),
475                    PROJECTION,
476                    ConversationColumns._ID + "=?",
477                    new String[] { conversationId },
478                    null, null, null);
479            Assert.inRange(cursor.getCount(), 0, 1);
480            if (cursor.moveToFirst()) {
481                conversation = new ConversationListItemData();
482                conversation.bind(cursor);
483            }
484        } finally {
485            if (cursor != null) {
486                cursor.close();
487            }
488        }
489
490        return conversation;
491    }
492
493    public static String generateConversationName(final List<ParticipantData>
494            participants) {
495        if (participants.size() == 1) {
496            // Prefer full name over first name for 1:1 conversation
497            return participants.get(0).getDisplayName(true);
498        }
499
500        final ArrayList<String> participantNames = new ArrayList<String>();
501        for (final ParticipantData participant : participants) {
502            // Prefer first name over full name for group conversation
503            participantNames.add(participant.getDisplayName(false));
504        }
505
506        final Joiner joiner = Joiner.on(DIVIDER_TEXT).skipNulls();
507        return joiner.join(participantNames);
508    }
509
510}
511