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 
17 package com.android.messaging.datamodel.data;
18 
19 import android.database.Cursor;
20 import android.net.Uri;
21 import android.provider.BaseColumns;
22 import android.text.TextUtils;
23 
24 import com.android.messaging.datamodel.DatabaseHelper;
25 import com.android.messaging.datamodel.DatabaseHelper.ConversationColumns;
26 import com.android.messaging.datamodel.DatabaseHelper.MessageColumns;
27 import com.android.messaging.datamodel.DatabaseHelper.ParticipantColumns;
28 import com.android.messaging.datamodel.DatabaseWrapper;
29 import com.android.messaging.datamodel.action.DeleteConversationAction;
30 import com.android.messaging.util.Assert;
31 import com.android.messaging.util.Dates;
32 import com.google.common.base.Joiner;
33 
34 import java.util.ArrayList;
35 import java.util.List;
36 
37 /**
38  * Class wrapping the conversation list view used to display each item in conversation list
39  */
40 public 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 
ConversationListItemData()70     public ConversationListItemData() {
71     }
72 
bind(final Cursor cursor)73     public void bind(final Cursor cursor) {
74         bind(cursor, false);
75     }
76 
bind(final Cursor cursor, final boolean ignoreDraft)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 
getConversationId()122     public String getConversationId() {
123         return mConversationId;
124     }
125 
getName()126     public String getName() {
127         return mName;
128     }
129 
getIcon()130     public String getIcon() {
131         return mIcon;
132     }
133 
getIsRead()134     public boolean getIsRead() {
135         return mIsRead;
136     }
137 
getFormattedTimestamp()138     public String getFormattedTimestamp() {
139         return Dates.getConversationTimeString(mTimestamp).toString();
140     }
141 
getTimestamp()142     public long getTimestamp() {
143         return mTimestamp;
144     }
145 
getSnippetText()146     public String getSnippetText() {
147         return mSnippetText;
148     }
149 
getPreviewUri()150     public Uri getPreviewUri() {
151         return mPreviewUri;
152     }
153 
getPreviewContentType()154     public String getPreviewContentType() {
155         return mPreviewContentType;
156     }
157 
getParticipantContactId()158     public long getParticipantContactId() {
159         return mParticipantContactId;
160     }
161 
getParticipantLookupKey()162     public String getParticipantLookupKey() {
163         return mParticipantLookupKey;
164     }
165 
getOtherParticipantNormalizedDestination()166     public String getOtherParticipantNormalizedDestination() {
167         return mOtherParticipantNormalizedDestination;
168     }
169 
getSelfId()170     public String getSelfId() {
171         return mSelfId;
172     }
173 
getParticipantCount()174     public int getParticipantCount() {
175         return mParticipantCount;
176     }
177 
getIsGroup()178     public boolean getIsGroup() {
179         // Participant count excludes self
180         return (mParticipantCount > 1);
181     }
182 
getIncludeEmailAddress()183     public boolean getIncludeEmailAddress() {
184         return mIncludeEmailAddress;
185     }
186 
getNotificationEnabled()187     public boolean getNotificationEnabled() {
188         return mNotificationEnabled;
189     }
190 
getNotificationSoundUri()191     public String getNotificationSoundUri() {
192         return mNotificationSoundUri;
193     }
194 
getNotifiationVibrate()195     public boolean getNotifiationVibrate() {
196         return mNotificationVibrate;
197     }
198 
getIsFailedStatus()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 
getIsSendRequested()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 
getIsMessageTypeOutgoing()213     public boolean getIsMessageTypeOutgoing() {
214         return !MessageData.getIsIncoming(mMessageStatus);
215     }
216 
getMessageRawTelephonyStatus()217     public int getMessageRawTelephonyStatus() {
218         return mMessageRawTelephonyStatus;
219     }
220 
getMessageStatus()221     public int getMessageStatus() {
222         return mMessageStatus;
223     }
224 
getShowDraft()225     public boolean getShowDraft() {
226         return mShowDraft;
227     }
228 
getDraftSnippetText()229     public String getDraftSnippetText() {
230         return mDraftSnippetText;
231     }
232 
getDraftPreviewUri()233     public Uri getDraftPreviewUri() {
234         return mDraftPreviewUri;
235     }
236 
getDraftPreviewContentType()237     public String getDraftPreviewContentType() {
238         return mDraftPreviewContentType;
239     }
240 
getIsArchived()241     public boolean getIsArchived() {
242         return mIsArchived;
243     }
244 
getSubject()245     public String getSubject() {
246         return mSubject;
247     }
248 
getDraftSubject()249     public String getDraftSubject() {
250         return mDraftSubject;
251     }
252 
getSnippetSenderName()253     public String getSnippetSenderName() {
254         if (!TextUtils.isEmpty(mSnippetSenderFirstName)) {
255             return mSnippetSenderFirstName;
256         }
257         return mSnippetSenderDisplayDestination;
258     }
259 
deleteConversation()260     public void deleteConversation() {
261         DeleteConversationAction.deleteConversation(mConversationId, mTimestamp);
262     }
263 
264     /**
265      * Get the name of the view for this data item
266      */
getConversationListView()267     public static final String getConversationListView() {
268         return CONVERSATION_LIST_VIEW;
269     }
270 
getConversationListViewSql()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      */
getExistingConversation(final DatabaseWrapper dbWrapper, final String conversationId)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 
generateConversationName(final List<ParticipantData> participants)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