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.content.ContentValues; 20 import android.database.sqlite.SQLiteDatabase; 21 import android.net.Uri; 22 import android.provider.BaseColumns; 23 import android.provider.ContactsContract.CommonDataKinds.Phone; 24 import android.provider.ContactsContract.Contacts; 25 import android.provider.MediaStore.Images.Media; 26 27 import com.android.messaging.datamodel.BugleDatabaseOperations; 28 import com.android.messaging.datamodel.DatabaseHelper; 29 import com.android.messaging.datamodel.DatabaseHelper.ConversationColumns; 30 import com.android.messaging.datamodel.DatabaseHelper.MessageColumns; 31 import com.android.messaging.datamodel.DatabaseHelper.PartColumns; 32 import com.android.messaging.datamodel.DatabaseHelper.ParticipantColumns; 33 import com.android.messaging.datamodel.FakeCursor; 34 import com.android.messaging.datamodel.data.ConversationListItemData.ConversationListViewColumns; 35 import com.android.messaging.datamodel.data.ConversationMessageData.ConversationMessageViewColumns; 36 import com.android.messaging.util.Assert; 37 import com.android.messaging.util.ContactUtil; 38 import com.android.messaging.util.ContentType; 39 40 import java.util.Arrays; 41 import java.util.List; 42 43 /** 44 * A factory for fake objects that can be useful for multiple tests. 45 */ 46 public class TestDataFactory { 47 private final static String[] sConversationListCursorColumns = new String[] { 48 ConversationListViewColumns._ID, 49 ConversationListViewColumns.NAME, 50 ConversationListViewColumns.ICON, 51 ConversationListViewColumns.SNIPPET_TEXT, 52 ConversationListViewColumns.PREVIEW_URI, 53 ConversationListViewColumns.SORT_TIMESTAMP, 54 ConversationListViewColumns.READ, 55 ConversationListViewColumns.PREVIEW_CONTENT_TYPE, 56 ConversationListViewColumns.MESSAGE_STATUS, 57 }; 58 59 private final static String[] sContactCursorColumns = new String[] { 60 Phone.CONTACT_ID, 61 Phone.DISPLAY_NAME_PRIMARY, 62 Phone.PHOTO_THUMBNAIL_URI, 63 Phone.NUMBER, 64 Phone.TYPE, 65 Phone.LABEL, 66 Phone.LOOKUP_KEY, 67 Phone._ID, 68 Phone.SORT_KEY_PRIMARY, 69 }; 70 71 private final static String[] sFrequentContactCursorColumns = new String[] { 72 Contacts._ID, 73 Contacts.DISPLAY_NAME, 74 Contacts.PHOTO_URI, 75 Phone.LOOKUP_KEY, 76 }; 77 78 private final static String[] sConversationMessageCursorColumns = new String[] { 79 ConversationMessageViewColumns._ID, 80 ConversationMessageViewColumns.CONVERSATION_ID, 81 ConversationMessageViewColumns.PARTICIPANT_ID, 82 ConversationMessageViewColumns.SENT_TIMESTAMP, 83 ConversationMessageViewColumns.RECEIVED_TIMESTAMP, 84 ConversationMessageViewColumns.STATUS, 85 ConversationMessageViewColumns.SENDER_FULL_NAME, 86 ConversationMessageViewColumns.SENDER_PROFILE_PHOTO_URI, 87 ConversationMessageViewColumns.PARTS_IDS, 88 ConversationMessageViewColumns.PARTS_CONTENT_TYPES, 89 ConversationMessageViewColumns.PARTS_CONTENT_URIS, 90 ConversationMessageViewColumns.PARTS_WIDTHS, 91 ConversationMessageViewColumns.PARTS_HEIGHTS, 92 ConversationMessageViewColumns.PARTS_TEXTS, 93 ConversationMessageViewColumns.PARTS_COUNT 94 }; 95 96 private final static String[] sGalleryCursorColumns = new String[] { 97 Media._ID, 98 Media.DATA, 99 Media.WIDTH, 100 Media.HEIGHT, 101 Media.MIME_TYPE 102 }; 103 getConversationListCursor()104 public static FakeCursor getConversationListCursor() { 105 final Object[][] cursorData = new Object[][] { 106 new Object[] { Long.valueOf(1), "name1", "content://icon1", 107 "snippetText1", "content://snippetUri1", Long.valueOf(10), 1, 108 ContentType.IMAGE_JPEG, MessageData.BUGLE_STATUS_INCOMING_COMPLETE}, 109 new Object[] { Long.valueOf(2), "name2", "content://icon2", 110 "snippetText2", "content://snippetUri2", Long.valueOf(20) + 24*60*60*1000, 111 0, ContentType.IMAGE_JPEG, MessageData.BUGLE_STATUS_INCOMING_COMPLETE}, 112 new Object[] { Long.valueOf(3), "name3", "content://icon3", 113 "snippetText3", "content://snippetUri3", Long.valueOf(30) + 2*24*60*60*1000, 114 0, ContentType.IMAGE_JPEG, MessageData.BUGLE_STATUS_OUTGOING_COMPLETE} 115 }; 116 return new FakeCursor(ConversationListItemData.PROJECTION, sConversationListCursorColumns, 117 cursorData); 118 } 119 public static final int CONVERSATION_LIST_CURSOR_READ_MESSAGE_INDEX = 0; 120 public static final int CONVERSATION_LIST_CURSOR_UNREAD_MESSAGE_INDEX = 1; 121 getEmptyConversationListCursor()122 public static FakeCursor getEmptyConversationListCursor() { 123 return new FakeCursor(ConversationListItemData.PROJECTION, sConversationListCursorColumns, 124 new Object[][] {}); 125 } 126 getConversationMessageCursor()127 public static FakeCursor getConversationMessageCursor() { 128 final Object[][] cursorData = new Object[][] { 129 new Object[] { Long.valueOf(0), Long.valueOf(1), Long.valueOf(1), 130 Long.valueOf(10), Long.valueOf(10), 131 MessageData.BUGLE_STATUS_INCOMING_COMPLETE, "Alice", null, 132 "0", "text/plain", "''", -1, -1, "msg0", 1}, 133 new Object[] { Long.valueOf(1), Long.valueOf(1), Long.valueOf(2), 134 Long.valueOf(20), Long.valueOf(20), 135 MessageData.BUGLE_STATUS_OUTGOING_COMPLETE, "Bob", null, 136 "1", "text/plain", "''", -1, -1, "msg1", 1}, 137 new Object[] { Long.valueOf(2), Long.valueOf(1), Long.valueOf(1), 138 Long.valueOf(30), Long.valueOf(30), 139 MessageData.BUGLE_STATUS_OUTGOING_COMPLETE, "Alice", null, 140 "2", "contentType3", "'content://fakeUri3'", "0", "0", "msg1", 1}, 141 new Object[] { Long.valueOf(3), Long.valueOf(1), Long.valueOf(1), 142 Long.valueOf(40), Long.valueOf(40), 143 MessageData.BUGLE_STATUS_OUTGOING_COMPLETE, "Alice", null, 144 "3|4", "'contentType4'|'text/plain'", "'content://fakeUri4'|''", "0|-1", "0|-1", "''|'msg3'", 2}, 145 }; 146 return new FakeCursor( 147 ConversationMessageData.getProjection(), 148 sConversationMessageCursorColumns, 149 cursorData); 150 } 151 getMessageText(final FakeCursor messageCursor, final int row)152 public static String getMessageText(final FakeCursor messageCursor, final int row) { 153 final String allPartsText = messageCursor.getAt(ConversationMessageViewColumns.PARTS_TEXTS, row) 154 .toString(); 155 final int partsCount = (Integer) messageCursor.getAt( 156 ConversationMessageViewColumns.PARTS_COUNT, row); 157 final String messageId = messageCursor.getAt( 158 ConversationMessageViewColumns._ID, row).toString(); 159 final List<MessagePartData> parts = ConversationMessageData.makeParts( 160 messageCursor.getAt(ConversationMessageViewColumns.PARTS_IDS, row).toString(), 161 messageCursor.getAt(ConversationMessageViewColumns.PARTS_CONTENT_TYPES, row).toString(), 162 messageCursor.getAt(ConversationMessageViewColumns.PARTS_CONTENT_URIS, row).toString(), 163 messageCursor.getAt(ConversationMessageViewColumns.PARTS_WIDTHS, row).toString(), 164 messageCursor.getAt(ConversationMessageViewColumns.PARTS_HEIGHTS, row).toString(), 165 messageCursor.getAt(ConversationMessageViewColumns.PARTS_TEXTS, row).toString(), 166 partsCount, 167 messageId); 168 169 for (final MessagePartData part : parts) { 170 if (part.isText()) { 171 return part.getText(); 172 } 173 } 174 return null; 175 } 176 177 // Indexes where to find consecutive and non consecutive messages from same participant 178 // (respect to index - 1). 179 public static final int MESSAGE_WITH_SAME_PARTICIPANT_AS_PREVIOUS = 3; 180 public static final int MESSAGE_WITH_DIFFERENT_PARTICIPANT_AS_PREVIOUS = 2; 181 getConversationParticipantsCursor()182 public static FakeCursor getConversationParticipantsCursor() { 183 final String[] sConversationParticipantsCursorColumns = new String[] { 184 ParticipantColumns._ID, 185 ParticipantColumns.SUB_ID, 186 ParticipantColumns.NORMALIZED_DESTINATION, 187 ParticipantColumns.SEND_DESTINATION, 188 ParticipantColumns.FULL_NAME, 189 ParticipantColumns.FIRST_NAME, 190 ParticipantColumns.PROFILE_PHOTO_URI, 191 }; 192 193 final Object[][] cursorData = new Object[][] { 194 new Object[] { 1, ParticipantData.OTHER_THAN_SELF_SUB_ID, "+15554567890", 195 "(555)456-7890", "alice in wonderland", "alice", "alice.png" }, 196 new Object[] { 2, ParticipantData.OTHER_THAN_SELF_SUB_ID, "+15551011121", 197 "(555)101-1121", "bob the baker", "bob", "bob.png"}, 198 new Object[] { 3, ParticipantData.OTHER_THAN_SELF_SUB_ID, "+15551314152", 199 "(555)131-4152", "charles in charge", "charles", "charles.png" }, 200 }; 201 202 return new FakeCursor(ParticipantData.ParticipantsQuery.PROJECTION, 203 sConversationParticipantsCursorColumns, cursorData); 204 } 205 206 public static final int CONTACT_LIST_CURSOR_FIRST_LEVEL_CONTACT_INDEX = 0; 207 public static final int CONTACT_LIST_CURSOR_SECOND_LEVEL_CONTACT_INDEX = 2; 208 209 /** 210 * Returns a cursor for the all contacts list consumable by ContactPickerFragment. 211 */ getAllContactListCursor()212 public static FakeCursor getAllContactListCursor() { 213 final Object[][] cursorData = new Object[][] { 214 new Object[] { Long.valueOf(0), "John Smith", "content://uri1", 215 "425-555-1234", Phone.TYPE_HOME, "", "0", Long.valueOf(0), 0 }, 216 new Object[] { Long.valueOf(1), "Sun Woo Kong", "content://uri2", 217 "425-555-1235", Phone.TYPE_MOBILE, "", "1", Long.valueOf(1), 1 }, 218 new Object[] { Long.valueOf(1), "Sun Woo Kong", "content://uri2", 219 "425-555-1238", Phone.TYPE_HOME, "", "1", Long.valueOf(2), 2 }, 220 new Object[] { Long.valueOf(2), "Anna Kinney", "content://uri3", 221 "425-555-1236", Phone.TYPE_MAIN, "", "3", Long.valueOf(3), 3 }, 222 new Object[] { Long.valueOf(3), "Mike Jones", "content://uri3", 223 "425-555-1236", Phone.TYPE_MAIN, "", "5", Long.valueOf(4), 4 }, 224 }; 225 return new FakeCursor(ContactUtil.PhoneQuery.PROJECTION, sContactCursorColumns, 226 cursorData); 227 } 228 229 /** 230 * Returns a cursor for the frequent contacts list consumable by ContactPickerFragment. 231 * Note: make it so that this cursor is the generated result of getStrequentContactsCursor() 232 * and getAllContactListCursor(), i.e., expand the entries in getStrequentContactsCursor() 233 * with the details from getAllContactListCursor() 234 */ getFrequentContactListCursor()235 public static FakeCursor getFrequentContactListCursor() { 236 final Object[][] cursorData = new Object[][] { 237 new Object[] { Long.valueOf(2), "Anna Kinney", "content://uri3", 238 "425-555-1236", Phone.TYPE_MAIN, "", "3", Long.valueOf(3), 0 }, 239 new Object[] { Long.valueOf(1), "Sun Woo Kong", "content://uri2", 240 "425-555-1235", Phone.TYPE_MOBILE, "", "1", Long.valueOf(1), 1}, 241 new Object[] { Long.valueOf(1), "Sun Woo Kong", "content://uri2", 242 "425-555-1238", Phone.TYPE_HOME, "", "1", Long.valueOf(2), 2 }, 243 new Object[] { Long.valueOf(0), "John Smith", "content://uri1", 244 "425-555-1234", Phone.TYPE_HOME, "", "0", Long.valueOf(0), 3 }, 245 }; 246 return new FakeCursor(ContactUtil.PhoneQuery.PROJECTION, sContactCursorColumns, 247 cursorData); 248 } 249 250 /** 251 * Returns a strequent (starred + frequent) cursor (like the one produced by android contact 252 * provider's CONTENT_STREQUENT_URI query) that's consumable by FrequentContactsCursorBuilder. 253 */ getStrequentContactsCursor()254 public static FakeCursor getStrequentContactsCursor() { 255 final Object[][] cursorData = new Object[][] { 256 new Object[] { Long.valueOf(0), "Anna Kinney", "content://uri1", "3" }, 257 new Object[] { Long.valueOf(1), "Sun Woo Kong", "content://uri2", "1" }, 258 new Object[] { Long.valueOf(2), "John Smith", "content://uri3", "0" }, 259 // Email-only entry that shouldn't be included in the result. 260 new Object[] { Long.valueOf(3), "Email Contact", "content://uri4", "100" }, 261 }; 262 return new FakeCursor(ContactUtil.FrequentContactQuery.PROJECTION, 263 sFrequentContactCursorColumns, cursorData); 264 } 265 266 public static final int SMS_MMS_THREAD_ID_CURSOR_VALUE = 123456789; 267 getSmsMmsThreadIdCursor()268 public static FakeCursor getSmsMmsThreadIdCursor() { 269 final String[] ID_PROJECTION = { BaseColumns._ID }; 270 final Object[][] cursorData = new Object[][] { 271 new Object[] { Long.valueOf(SMS_MMS_THREAD_ID_CURSOR_VALUE) }, 272 }; 273 return new FakeCursor(ID_PROJECTION, ID_PROJECTION, cursorData); 274 } 275 getGalleryGridCursor()276 public static FakeCursor getGalleryGridCursor() { 277 final Object[][] cursorData = new Object[][] { 278 new Object[] { Long.valueOf(0), "/sdcard/image1", 100, 100, "image/jpeg" }, 279 new Object[] { Long.valueOf(1), "/sdcard/image2", 200, 200, "image/png" }, 280 new Object[] { Long.valueOf(2), "/sdcard/image3", 300, 300, "image/jpeg" }, 281 }; 282 return new FakeCursor(GalleryGridItemData.IMAGE_PROJECTION, sGalleryCursorColumns, 283 cursorData); 284 } 285 286 public static final int NUM_TEST_CONVERSATIONS = 10; 287 288 /** 289 * Create test data in our db. 290 * 291 * Ideally this will create more realistic data with more variety. 292 */ createTestData(final SQLiteDatabase db)293 public static void createTestData(final SQLiteDatabase db) { 294 BugleDatabaseOperations.clearParticipantIdCache(); 295 296 // Timestamp for 1 day ago 297 final long yesterday = System.currentTimeMillis() - (24 * 60 * 60 * 1000); 298 299 final ContentValues conversationValues = new ContentValues(); 300 for (int i = 1; i <= NUM_TEST_CONVERSATIONS; i++) { 301 conversationValues.put(ConversationColumns.NAME, "Conversation " + i); 302 final long conversationId = db.insert(DatabaseHelper.CONVERSATIONS_TABLE, null, 303 conversationValues); 304 305 final ContentValues messageValues = new ContentValues(); 306 for (int m = 1; m <= 25; m++) { 307 // Move forward ten minutes per conversation, 1 minute per message. 308 final long messageTime = yesterday + (i * 10 * 60 * 1000) + (m * 60 * 1000); 309 messageValues.put(MessageColumns.RECEIVED_TIMESTAMP, messageTime); 310 messageValues.put(MessageColumns.CONVERSATION_ID, conversationId); 311 messageValues.put(MessageColumns.SENDER_PARTICIPANT_ID, 312 Math.abs(("" + messageTime).hashCode()) % 2); 313 final long messageId = db.insert(DatabaseHelper.MESSAGES_TABLE, null, messageValues); 314 315 // Create a text part for this message 316 final ContentValues partValues = new ContentValues(); 317 partValues.put(PartColumns.MESSAGE_ID, messageId); 318 partValues.put(PartColumns.CONVERSATION_ID, conversationId); 319 partValues.put(PartColumns.TEXT, "Conversation: " + conversationId + 320 " Message: " + m); 321 db.insert(DatabaseHelper.PARTS_TABLE, null, partValues); 322 323 // Update the snippet for this conversation to the latest message inserted 324 conversationValues.clear(); 325 conversationValues.put(ConversationColumns.LATEST_MESSAGE_ID, messageId); 326 final int updatedCount = db.update(DatabaseHelper.CONVERSATIONS_TABLE, 327 conversationValues, 328 "_id=?", new String[]{String.valueOf(conversationId)}); 329 Assert.isTrue(updatedCount == 1); 330 } 331 } 332 } 333 getTestDraftAttachments()334 public static List<MessagePartData> getTestDraftAttachments() { 335 final MessagePartData[] retParts = new MessagePartData[] { 336 new MessagePartData(ContentType.IMAGE_JPEG, Uri.parse("content://image"), 337 100, 100), 338 new MessagePartData(ContentType.VIDEO_3GPP, Uri.parse("content://video"), 339 100, 100), 340 new MessagePartData(ContentType.TEXT_VCARD, Uri.parse("content://vcard"), 341 0, 0), 342 new MessagePartData(ContentType.AUDIO_3GPP, Uri.parse("content://audio"), 343 0, 0) 344 }; 345 return Arrays.asList(retParts); 346 } 347 } 348