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