Home | History | Annotate | Download | only in data
      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 
     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