BluetoothMapContent.java revision aa852f71699b29af8353d596f2ddaba6cf2a1ae0
1/* 2* Copyright (C) 2014 Samsung System LSI 3* Licensed under the Apache License, Version 2.0 (the "License"); 4* you may not use this file except in compliance with the License. 5* You may obtain a copy of the License at 6* 7* http://www.apache.org/licenses/LICENSE-2.0 8* 9* Unless required by applicable law or agreed to in writing, software 10* distributed under the License is distributed on an "AS IS" BASIS, 11* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12* See the License for the specific language governing permissions and 13* limitations under the License. 14*/ 15package com.android.bluetooth.map; 16 17import android.annotation.TargetApi; 18import android.content.ContentResolver; 19import android.content.Context; 20import android.database.Cursor; 21import android.net.Uri; 22import android.net.Uri.Builder; 23import android.os.ParcelFileDescriptor; 24import android.provider.BaseColumns; 25import android.provider.ContactsContract; 26import android.provider.ContactsContract.Contacts; 27import android.provider.ContactsContract.PhoneLookup; 28import android.provider.Telephony.Mms; 29import android.provider.Telephony.Sms; 30import android.provider.Telephony.MmsSms; 31import android.provider.Telephony.CanonicalAddressesColumns; 32import android.provider.Telephony.Threads; 33import android.telephony.PhoneNumberUtils; 34import android.telephony.TelephonyManager; 35import android.text.util.Rfc822Token; 36import android.text.util.Rfc822Tokenizer; 37import android.text.TextUtils; 38import android.util.Log; 39import android.util.SparseArray; 40 41import com.android.bluetooth.SignedLongLong; 42import com.android.bluetooth.map.BluetoothMapContentObserver.Msg; 43import com.android.bluetooth.map.BluetoothMapUtils.TYPE; 44import com.android.bluetooth.map.BluetoothMapbMessageMime.MimePart; 45import com.android.bluetooth.mapapi.BluetoothMapContract; 46import com.android.bluetooth.mapapi.BluetoothMapContract.ConversationColumns; 47import com.google.android.mms.pdu.CharacterSets; 48import com.google.android.mms.pdu.PduHeaders; 49 50import java.io.ByteArrayOutputStream; 51import java.io.Closeable; 52import java.io.FileInputStream; 53import java.io.FileNotFoundException; 54import java.io.IOException; 55import java.io.InputStream; 56import java.io.UnsupportedEncodingException; 57import java.text.SimpleDateFormat; 58import java.util.ArrayList; 59import java.util.Arrays; 60import java.util.Date; 61import java.util.HashMap; 62import java.util.List; 63import java.util.concurrent.atomic.AtomicLong; 64 65@TargetApi(19) 66public class BluetoothMapContent { 67 68 private static final String TAG = "BluetoothMapContent"; 69 70 private static final boolean D = BluetoothMapService.DEBUG; 71 private static final boolean V = BluetoothMapService.VERBOSE; 72 73 // Parameter Mask for selection of parameters to return in listings 74 private static final int MASK_SUBJECT = 0x00000001; 75 private static final int MASK_DATETIME = 0x00000002; 76 private static final int MASK_SENDER_NAME = 0x00000004; 77 private static final int MASK_SENDER_ADDRESSING = 0x00000008; 78 private static final int MASK_RECIPIENT_NAME = 0x00000010; 79 private static final int MASK_RECIPIENT_ADDRESSING = 0x00000020; 80 private static final int MASK_TYPE = 0x00000040; 81 private static final int MASK_SIZE = 0x00000080; 82 private static final int MASK_RECEPTION_STATUS = 0x00000100; 83 private static final int MASK_TEXT = 0x00000200; 84 private static final int MASK_ATTACHMENT_SIZE = 0x00000400; 85 private static final int MASK_PRIORITY = 0x00000800; 86 private static final int MASK_READ = 0x00001000; 87 private static final int MASK_SENT = 0x00002000; 88 private static final int MASK_PROTECTED = 0x00004000; 89 private static final int MASK_REPLYTO_ADDRESSING = 0x00008000; 90 // TODO: Duplicate in proposed spec 91 // private static final int MASK_RECEPTION_STATE = 0x00010000; 92 private static final int MASK_DELIVERY_STATUS = 0x00020000; 93 private static final int MASK_CONVERSATION_ID = 0x00040000; 94 private static final int MASK_CONVERSATION_NAME = 0x00080000; 95 private static final int MASK_FOLDER_TYPE = 0x00100000; 96 // TODO: about to be removed from proposed spec 97 // private static final int MASK_SEQUENCE_NUMBER = 0x00200000; 98 private static final int MASK_ATTACHMENT_MIME = 0x00400000; 99 100 private static final int CONVO_PARAM_MASK_CONVO_NAME = 0x00000001; 101 private static final int CONVO_PARAM_MASK_CONVO_LAST_ACTIVITY = 0x00000002; 102 private static final int CONVO_PARAM_MASK_CONVO_READ_STATUS = 0x00000004; 103 private static final int CONVO_PARAM_MASK_CONVO_VERSION_COUNTER = 0x00000008; 104 private static final int CONVO_PARAM_MASK_CONVO_SUMMARY = 0x00000010; 105 private static final int CONVO_PARAM_MASK_PARTTICIPANTS = 0x00000020; 106 private static final int CONVO_PARAM_MASK_PART_UCI = 0x00000040; 107 private static final int CONVO_PARAM_MASK_PART_DISP_NAME = 0x00000080; 108 private static final int CONVO_PARAM_MASK_PART_CHAT_STATE = 0x00000100; 109 private static final int CONVO_PARAM_MASK_PART_LAST_ACTIVITY = 0x00000200; 110 private static final int CONVO_PARAM_MASK_PART_X_BT_UID = 0x00000400; 111 private static final int CONVO_PARAM_MASK_PART_NAME = 0x00000800; 112 private static final int CONVO_PARAM_MASK_PART_PRESENCE = 0x00001000; 113 private static final int CONVO_PARAM_MASK_PART_PRESENCE_TEXT = 0x00002000; 114 private static final int CONVO_PARAM_MASK_PART_PRIORITY = 0x00004000; 115 116 /* Default values for omitted or 0 parameterMask application parameters */ 117 // MAP specification states that the default value for parameter mask are 118 // the #REQUIRED attributes in the DTD, and not all enabled 119 public static final long PARAMETER_MASK_ALL_ENABLED = 0xFFFFFFFFL; 120 public static final long CONVO_PARAMETER_MASK_ALL_ENABLED = 0xFFFFFFFFL; 121 public static final long CONVO_PARAMETER_MASK_DEFAULT = 122 CONVO_PARAM_MASK_CONVO_NAME | 123 CONVO_PARAM_MASK_PARTTICIPANTS | 124 CONVO_PARAM_MASK_PART_UCI | 125 CONVO_PARAM_MASK_PART_DISP_NAME; 126 127 128 129 130 private static final int FILTER_READ_STATUS_UNREAD_ONLY = 0x01; 131 private static final int FILTER_READ_STATUS_READ_ONLY = 0x02; 132 private static final int FILTER_READ_STATUS_ALL = 0x00; 133 134 /* Type of MMS address. From Telephony.java it must be one of PduHeaders.BCC, */ 135 /* PduHeaders.CC, PduHeaders.FROM, PduHeaders.TO. These are from PduHeaders.java */ 136 public static final int MMS_FROM = 0x89; 137 public static final int MMS_TO = 0x97; 138 public static final int MMS_BCC = 0x81; 139 public static final int MMS_CC = 0x82; 140 141 /* OMA-TS-MMS-ENC defined many types in X-Mms-Message-Type. 142 Only m-send-req (128) m-retrieve-conf (132), m-notification-ind (130) 143 are interested by user */ 144 private static final String INTERESTED_MESSAGE_TYPE_CLAUSE = String 145 .format("( %s = %d OR %s = %d OR %s = %d )", Mms.MESSAGE_TYPE, 146 PduHeaders.MESSAGE_TYPE_SEND_REQ, Mms.MESSAGE_TYPE, 147 PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF, Mms.MESSAGE_TYPE, 148 PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND ); 149 150 public static final String INSERT_ADDRES_TOKEN = "insert-address-token"; 151 152 private final Context mContext; 153 private final ContentResolver mResolver; 154 private final String mBaseUri; 155 private final BluetoothMapAccountItem mAccount; 156 /* The MasInstance reference is used to update persistent (over a connection) version counters*/ 157 private final BluetoothMapMasInstance mMasInstance; 158 private String mMessageVersion = BluetoothMapUtils.MAP_V10_STR; 159 160 private int mRemoteFeatureMask = BluetoothMapUtils.MAP_FEATURE_DEFAULT_BITMASK; 161 private int mMsgListingVersion = BluetoothMapUtils.MAP_MESSAGE_LISTING_FORMAT_V10; 162 163 static final String[] SMS_PROJECTION = new String[] { 164 BaseColumns._ID, 165 Sms.THREAD_ID, 166 Sms.ADDRESS, 167 Sms.BODY, 168 Sms.DATE, 169 Sms.READ, 170 Sms.TYPE, 171 Sms.STATUS, 172 Sms.LOCKED, 173 Sms.ERROR_CODE 174 }; 175 176 static final String[] MMS_PROJECTION = new String[] { 177 BaseColumns._ID, 178 Mms.THREAD_ID, 179 Mms.MESSAGE_ID, 180 Mms.MESSAGE_SIZE, 181 Mms.SUBJECT, 182 Mms.CONTENT_TYPE, 183 Mms.TEXT_ONLY, 184 Mms.DATE, 185 Mms.DATE_SENT, 186 Mms.READ, 187 Mms.MESSAGE_BOX, 188 Mms.STATUS, 189 Mms.PRIORITY, 190 }; 191 192 static final String[] SMS_CONVO_PROJECTION = new String[] { 193 BaseColumns._ID, 194 Sms.THREAD_ID, 195 Sms.ADDRESS, 196 Sms.DATE, 197 Sms.READ, 198 Sms.TYPE, 199 Sms.STATUS, 200 Sms.LOCKED, 201 Sms.ERROR_CODE 202 }; 203 204 static final String[] MMS_CONVO_PROJECTION = new String[] { 205 BaseColumns._ID, 206 Mms.THREAD_ID, 207 Mms.MESSAGE_ID, 208 Mms.MESSAGE_SIZE, 209 Mms.SUBJECT, 210 Mms.CONTENT_TYPE, 211 Mms.TEXT_ONLY, 212 Mms.DATE, 213 Mms.DATE_SENT, 214 Mms.READ, 215 Mms.MESSAGE_BOX, 216 Mms.STATUS, 217 Mms.PRIORITY, 218 Mms.Addr.ADDRESS 219 }; 220 221 /* CONVO LISTING projections and column indexes */ 222 private static final String[] MMS_SMS_THREAD_PROJECTION = { 223 Threads._ID, 224 Threads.DATE, 225 Threads.SNIPPET, 226 Threads.SNIPPET_CHARSET, 227 Threads.READ, 228 Threads.RECIPIENT_IDS 229 }; 230 231 private static final String[] CONVO_VERSION_PROJECTION = new String[] { 232 /* Thread information */ 233 ConversationColumns.THREAD_ID, 234 ConversationColumns.THREAD_NAME, 235 ConversationColumns.READ_STATUS, 236 ConversationColumns.LAST_THREAD_ACTIVITY, 237 ConversationColumns.SUMMARY, 238 }; 239 240 /* Optimize the Cursor access to avoid the need to do a getColumnIndex() */ 241 private static final int MMS_SMS_THREAD_COL_ID; 242 private static final int MMS_SMS_THREAD_COL_DATE; 243 private static final int MMS_SMS_THREAD_COL_SNIPPET; 244 private static final int MMS_SMS_THREAD_COL_SNIPPET_CS; 245 private static final int MMS_SMS_THREAD_COL_READ; 246 private static final int MMS_SMS_THREAD_COL_RECIPIENT_IDS; 247 static { 248 // TODO: This might not work, if the projection is mapped in the content provider... 249 // Change to init at first query? (Current use in the AOSP code is hard coded values 250 // unrelated to the projection used) 251 List<String> projection = Arrays.asList(MMS_SMS_THREAD_PROJECTION); 252 MMS_SMS_THREAD_COL_ID = projection.indexOf(Threads._ID); 253 MMS_SMS_THREAD_COL_DATE = projection.indexOf(Threads.DATE); 254 MMS_SMS_THREAD_COL_SNIPPET = projection.indexOf(Threads.SNIPPET); 255 MMS_SMS_THREAD_COL_SNIPPET_CS = projection.indexOf(Threads.SNIPPET_CHARSET); 256 MMS_SMS_THREAD_COL_READ = projection.indexOf(Threads.READ); 257 MMS_SMS_THREAD_COL_RECIPIENT_IDS = projection.indexOf(Threads.RECIPIENT_IDS); 258 } 259 260 private class FilterInfo { 261 public static final int TYPE_SMS = 0; 262 public static final int TYPE_MMS = 1; 263 public static final int TYPE_EMAIL = 2; 264 public static final int TYPE_IM = 3; 265 266 // TODO: Change to ENUM, to ensure correct usage 267 int mMsgType = TYPE_SMS; 268 int mPhoneType = 0; 269 String mPhoneNum = null; 270 String mPhoneAlphaTag = null; 271 /*column indices used to optimize queries */ 272 public int mMessageColId = -1; 273 public int mMessageColDate = -1; 274 public int mMessageColBody = -1; 275 public int mMessageColSubject = -1; 276 public int mMessageColFolder = -1; 277 public int mMessageColRead = -1; 278 public int mMessageColSize = -1; 279 public int mMessageColFromAddress = -1; 280 public int mMessageColToAddress = -1; 281 public int mMessageColCcAddress = -1; 282 public int mMessageColBccAddress = -1; 283 public int mMessageColReplyTo = -1; 284 public int mMessageColAccountId = -1; 285 public int mMessageColAttachment = -1; 286 public int mMessageColAttachmentSize = -1; 287 public int mMessageColAttachmentMime = -1; 288 public int mMessageColPriority = -1; 289 public int mMessageColProtected = -1; 290 public int mMessageColReception = -1; 291 public int mMessageColDelivery = -1; 292 public int mMessageColThreadId = -1; 293 public int mMessageColThreadName = -1; 294 295 public int mSmsColFolder = -1; 296 public int mSmsColRead = -1; 297 public int mSmsColId = -1; 298 public int mSmsColSubject = -1; 299 public int mSmsColAddress = -1; 300 public int mSmsColDate = -1; 301 public int mSmsColType = -1; 302 public int mSmsColThreadId = -1; 303 304 public int mMmsColRead = -1; 305 public int mMmsColFolder = -1; 306 public int mMmsColAttachmentSize = -1; 307 public int mMmsColTextOnly = -1; 308 public int mMmsColId = -1; 309 public int mMmsColSize = -1; 310 public int mMmsColDate = -1; 311 public int mMmsColSubject = -1; 312 public int mMmsColThreadId = -1; 313 314 public int mConvoColConvoId = -1; 315 public int mConvoColLastActivity = -1; 316 public int mConvoColName = -1; 317 public int mConvoColRead = -1; 318 public int mConvoColVersionCounter = -1; 319 public int mConvoColSummary = -1; 320 public int mContactColBtUid = -1; 321 public int mContactColChatState = -1; 322 public int mContactColContactUci = -1; 323 public int mContactColNickname = -1; 324 public int mContactColLastActive = -1; 325 public int mContactColName = -1; 326 public int mContactColPresenceState = -1; 327 public int mContactColPresenceText = -1; 328 public int mContactColPriority = -1; 329 330 331 public void setMessageColumns(Cursor c) { 332 mMessageColId = c.getColumnIndex( 333 BluetoothMapContract.MessageColumns._ID); 334 mMessageColDate = c.getColumnIndex( 335 BluetoothMapContract.MessageColumns.DATE); 336 mMessageColSubject = c.getColumnIndex( 337 BluetoothMapContract.MessageColumns.SUBJECT); 338 mMessageColFolder = c.getColumnIndex( 339 BluetoothMapContract.MessageColumns.FOLDER_ID); 340 mMessageColRead = c.getColumnIndex( 341 BluetoothMapContract.MessageColumns.FLAG_READ); 342 mMessageColSize = c.getColumnIndex( 343 BluetoothMapContract.MessageColumns.MESSAGE_SIZE); 344 mMessageColFromAddress = c.getColumnIndex( 345 BluetoothMapContract.MessageColumns.FROM_LIST); 346 mMessageColToAddress = c.getColumnIndex( 347 BluetoothMapContract.MessageColumns.TO_LIST); 348 mMessageColAttachment = c.getColumnIndex( 349 BluetoothMapContract.MessageColumns.FLAG_ATTACHMENT); 350 mMessageColAttachmentSize = c.getColumnIndex( 351 BluetoothMapContract.MessageColumns.ATTACHMENT_SIZE); 352 mMessageColPriority = c.getColumnIndex( 353 BluetoothMapContract.MessageColumns.FLAG_HIGH_PRIORITY); 354 mMessageColProtected = c.getColumnIndex( 355 BluetoothMapContract.MessageColumns.FLAG_PROTECTED); 356 mMessageColReception = c.getColumnIndex( 357 BluetoothMapContract.MessageColumns.RECEPTION_STATE); 358 mMessageColDelivery = c.getColumnIndex( 359 BluetoothMapContract.MessageColumns.DEVILERY_STATE); 360 mMessageColThreadId = c.getColumnIndex( 361 BluetoothMapContract.MessageColumns.THREAD_ID); 362 } 363 364 public void setEmailMessageColumns(Cursor c) { 365 setMessageColumns(c); 366 mMessageColCcAddress = c.getColumnIndex( 367 BluetoothMapContract.MessageColumns.CC_LIST); 368 mMessageColBccAddress = c.getColumnIndex( 369 BluetoothMapContract.MessageColumns.BCC_LIST); 370 mMessageColReplyTo = c.getColumnIndex( 371 BluetoothMapContract.MessageColumns.REPLY_TO_LIST); 372 } 373 374 public void setImMessageColumns(Cursor c) { 375 setMessageColumns(c); 376 mMessageColThreadName = c.getColumnIndex( 377 BluetoothMapContract.MessageColumns.THREAD_NAME); 378 mMessageColAttachmentMime = c.getColumnIndex( 379 BluetoothMapContract.MessageColumns.ATTACHMENT_MINE_TYPES); 380 //TODO this is temporary as text should come from parts table instead 381 mMessageColBody = c.getColumnIndex(BluetoothMapContract.MessageColumns.BODY); 382 383 } 384 385 public void setEmailImConvoColumns(Cursor c) { 386 mConvoColConvoId = c.getColumnIndex( 387 BluetoothMapContract.ConversationColumns.THREAD_ID); 388 mConvoColLastActivity = c.getColumnIndex( 389 BluetoothMapContract.ConversationColumns.LAST_THREAD_ACTIVITY); 390 mConvoColName = c.getColumnIndex( 391 BluetoothMapContract.ConversationColumns.THREAD_NAME); 392 mConvoColRead = c.getColumnIndex( 393 BluetoothMapContract.ConversationColumns.READ_STATUS); 394 mConvoColVersionCounter = c.getColumnIndex( 395 BluetoothMapContract.ConversationColumns.VERSION_COUNTER); 396 mConvoColSummary = c.getColumnIndex( 397 BluetoothMapContract.ConversationColumns.SUMMARY); 398 setEmailImConvoContactColumns(c); 399 } 400 401 public void setEmailImConvoContactColumns(Cursor c){ 402 mContactColBtUid = c.getColumnIndex( 403 BluetoothMapContract.ConvoContactColumns.X_BT_UID); 404 mContactColChatState = c.getColumnIndex( 405 BluetoothMapContract.ConvoContactColumns.CHAT_STATE); 406 mContactColContactUci = c.getColumnIndex( 407 BluetoothMapContract.ConvoContactColumns.UCI); 408 mContactColNickname = c.getColumnIndex( 409 BluetoothMapContract.ConvoContactColumns.NICKNAME); 410 mContactColLastActive = c.getColumnIndex( 411 BluetoothMapContract.ConvoContactColumns.LAST_ACTIVE); 412 mContactColName = c.getColumnIndex( 413 BluetoothMapContract.ConvoContactColumns.NAME); 414 mContactColPresenceState = c.getColumnIndex( 415 BluetoothMapContract.ConvoContactColumns.PRESENCE_STATE); 416 mContactColPresenceText = c.getColumnIndex( 417 BluetoothMapContract.ConvoContactColumns.STATUS_TEXT); 418 mContactColPriority = c.getColumnIndex( 419 BluetoothMapContract.ConvoContactColumns.PRIORITY); 420 } 421 422 public void setSmsColumns(Cursor c) { 423 mSmsColId = c.getColumnIndex(BaseColumns._ID); 424 mSmsColFolder = c.getColumnIndex(Sms.TYPE); 425 mSmsColRead = c.getColumnIndex(Sms.READ); 426 mSmsColSubject = c.getColumnIndex(Sms.BODY); 427 mSmsColAddress = c.getColumnIndex(Sms.ADDRESS); 428 mSmsColDate = c.getColumnIndex(Sms.DATE); 429 mSmsColType = c.getColumnIndex(Sms.TYPE); 430 mSmsColThreadId= c.getColumnIndex(Sms.THREAD_ID); 431 } 432 433 public void setMmsColumns(Cursor c) { 434 mMmsColId = c.getColumnIndex(BaseColumns._ID); 435 mMmsColFolder = c.getColumnIndex(Mms.MESSAGE_BOX); 436 mMmsColRead = c.getColumnIndex(Mms.READ); 437 mMmsColAttachmentSize = c.getColumnIndex(Mms.MESSAGE_SIZE); 438 mMmsColTextOnly = c.getColumnIndex(Mms.TEXT_ONLY); 439 mMmsColSize = c.getColumnIndex(Mms.MESSAGE_SIZE); 440 mMmsColDate = c.getColumnIndex(Mms.DATE); 441 mMmsColSubject = c.getColumnIndex(Mms.SUBJECT); 442 mMmsColThreadId = c.getColumnIndex(Mms.THREAD_ID); 443 } 444 } 445 446 public BluetoothMapContent(final Context context, BluetoothMapAccountItem account, 447 BluetoothMapMasInstance mas) { 448 mContext = context; 449 mResolver = mContext.getContentResolver(); 450 mMasInstance = mas; 451 if (mResolver == null) { 452 if (D) Log.d(TAG, "getContentResolver failed"); 453 } 454 455 if(account != null){ 456 mBaseUri = account.mBase_uri + "/"; 457 mAccount = account; 458 } else { 459 mBaseUri = null; 460 mAccount = null; 461 } 462 } 463 private static void close(Closeable c) { 464 try { 465 if (c != null) c.close(); 466 } catch (IOException e) { 467 } 468 } 469 private void setProtected(BluetoothMapMessageListingElement e, Cursor c, 470 FilterInfo fi, BluetoothMapAppParams ap) { 471 if ((ap.getParameterMask() & MASK_PROTECTED) != 0) { 472 String protect = "no"; 473 if (fi.mMsgType == FilterInfo.TYPE_EMAIL || 474 fi.mMsgType == FilterInfo.TYPE_IM) { 475 int flagProtected = c.getInt(fi.mMessageColProtected); 476 if (flagProtected == 1) { 477 protect = "yes"; 478 } 479 } 480 if (V) Log.d(TAG, "setProtected: " + protect + "\n"); 481 e.setProtect(protect); 482 } 483 } 484 485 private void setThreadId(BluetoothMapMessageListingElement e, Cursor c, 486 FilterInfo fi, BluetoothMapAppParams ap) { 487 if ((ap.getParameterMask() & MASK_CONVERSATION_ID) != 0) { 488 long threadId = 0; 489 TYPE type = TYPE.SMS_GSM; // Just used for handle encoding 490 if (fi.mMsgType == FilterInfo.TYPE_SMS) { 491 threadId = c.getLong(fi.mSmsColThreadId); 492 } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { 493 threadId = c.getLong(fi.mMmsColThreadId); 494 type = TYPE.MMS;// Just used for handle encoding 495 } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL || 496 fi.mMsgType == FilterInfo.TYPE_IM) { 497 threadId = c.getLong(fi.mMessageColThreadId); 498 type = TYPE.EMAIL;// Just used for handle encoding 499 } 500 e.setThreadId(threadId,type); 501 if (V) Log.d(TAG, "setThreadId: " + threadId + "\n"); 502 } 503 } 504 505 private void setThreadName(BluetoothMapMessageListingElement e, Cursor c, 506 FilterInfo fi, BluetoothMapAppParams ap) { 507 // TODO: Maybe this should be valid for SMS/MMS 508 if ((ap.getParameterMask() & MASK_CONVERSATION_NAME) != 0) { 509 if (fi.mMsgType == FilterInfo.TYPE_IM) { 510 String threadName = c.getString(fi.mMessageColThreadName); 511 e.setThreadName(threadName); 512 if (V) Log.d(TAG, "setThreadName: " + threadName + "\n"); 513 } 514 } 515 } 516 517 518 private void setSent(BluetoothMapMessageListingElement e, Cursor c, 519 FilterInfo fi, BluetoothMapAppParams ap) { 520 if ((ap.getParameterMask() & MASK_SENT) != 0) { 521 int msgType = 0; 522 if (fi.mMsgType == FilterInfo.TYPE_SMS) { 523 msgType = c.getInt(fi.mSmsColFolder); 524 } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { 525 msgType = c.getInt(fi.mMmsColFolder); 526 } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL || 527 fi.mMsgType == FilterInfo.TYPE_IM) { 528 msgType = c.getInt(fi.mMessageColFolder); 529 } 530 String sent = null; 531 if (msgType == 2) { 532 sent = "yes"; 533 } else { 534 sent = "no"; 535 } 536 if (V) Log.d(TAG, "setSent: " + sent); 537 e.setSent(sent); 538 } 539 } 540 541 private void setRead(BluetoothMapMessageListingElement e, Cursor c, 542 FilterInfo fi, BluetoothMapAppParams ap) { 543 int read = 0; 544 if (fi.mMsgType == FilterInfo.TYPE_SMS) { 545 read = c.getInt(fi.mSmsColRead); 546 } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { 547 read = c.getInt(fi.mMmsColRead); 548 } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL || 549 fi.mMsgType == FilterInfo.TYPE_IM) { 550 read = c.getInt(fi.mMessageColRead); 551 } 552 String setread = null; 553 554 if (V) Log.d(TAG, "setRead: " + setread); 555 e.setRead((read==1?true:false), ((ap.getParameterMask() & MASK_READ) != 0)); 556 } 557 private void setConvoRead(BluetoothMapConvoListingElement e, Cursor c, 558 FilterInfo fi, BluetoothMapAppParams ap) { 559 String setread = null; 560 int read = 0; 561 read = c.getInt(fi.mConvoColRead); 562 563 564 if (V) Log.d(TAG, "setRead: " + setread); 565 e.setRead((read==1?true:false), ((ap.getParameterMask() & MASK_READ) != 0)); 566 } 567 568 private void setPriority(BluetoothMapMessageListingElement e, Cursor c, 569 FilterInfo fi, BluetoothMapAppParams ap) { 570 if ((ap.getParameterMask() & MASK_PRIORITY) != 0) { 571 String priority = "no"; 572 if (fi.mMsgType == FilterInfo.TYPE_EMAIL || 573 fi.mMsgType == FilterInfo.TYPE_IM) { 574 int highPriority = c.getInt(fi.mMessageColPriority); 575 if (highPriority == 1) { 576 priority = "yes"; 577 } 578 } 579 int pri = 0; 580 if (fi.mMsgType == FilterInfo.TYPE_MMS) { 581 pri = c.getInt(c.getColumnIndex(Mms.PRIORITY)); 582 } 583 if (pri == PduHeaders.PRIORITY_HIGH) { 584 priority = "yes"; 585 } 586 if (V) Log.d(TAG, "setPriority: " + priority); 587 e.setPriority(priority); 588 } 589 } 590 591 /** 592 * For SMS we set the attachment size to 0, as all data will be text data, hence 593 * attachments for SMS is not possible. 594 * For MMS all data is actually attachments, hence we do set the attachment size to 595 * the total message size. To provide a more accurate attachment size, one could 596 * extract the length (in bytes) of the text parts. 597 */ 598 private void setAttachment(BluetoothMapMessageListingElement e, Cursor c, 599 FilterInfo fi, BluetoothMapAppParams ap) { 600 if ((ap.getParameterMask() & MASK_ATTACHMENT_SIZE) != 0) { 601 int size = 0; 602 String attachmentMimeTypes = null; 603 if (fi.mMsgType == FilterInfo.TYPE_MMS) { 604 if(c.getInt(fi.mMmsColTextOnly) == 0) { 605 size = c.getInt(fi.mMmsColAttachmentSize); 606 if(size <= 0) { 607 // We know there are attachments, since it is not TextOnly 608 // Hence the size in the database must be wrong. 609 // Set size to 1 to indicate to the client, that attachments are present 610 if (D) Log.d(TAG, "Error in message database, size reported as: " + size 611 + " Changing size to 1"); 612 size = 1; 613 } 614 // TODO: Add handling of attachemnt mime types 615 } 616 } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL) { 617 int attachment = c.getInt(fi.mMessageColAttachment); 618 size = c.getInt(fi.mMessageColAttachmentSize); 619 if(attachment == 1 && size == 0) { 620 if (D) Log.d(TAG, "Error in message database, attachment size reported as: " + size 621 + " Changing size to 1"); 622 size = 1; /* Ensure we indicate we have attachments in the size, if the 623 message has attachments, in case the e-mail client do not 624 report a size */ 625 } 626 } else if (fi.mMsgType == FilterInfo.TYPE_IM) { 627 int attachment = c.getInt(fi.mMessageColAttachment); 628 size = c.getInt(fi.mMessageColAttachmentSize); 629 if(attachment == 1 && size == 0) { 630 size = 1; /* Ensure we indicate we have attachments in the size, it the 631 message has attachments, in case the e-mail client do not 632 report a size */ 633 attachmentMimeTypes = c.getString(fi.mMessageColAttachmentMime); 634 } 635 } 636 if (V) Log.d(TAG, "setAttachmentSize: " + size + "\n" + 637 "setAttachmentMimeTypes: " + attachmentMimeTypes ); 638 e.setAttachmentSize(size); 639 640 if( (mMsgListingVersion > BluetoothMapUtils.MAP_MESSAGE_LISTING_FORMAT_V10) 641 && ((ap.getParameterMask() & MASK_ATTACHMENT_MIME) != 0) ){ 642 e.setAttachmentMimeTypes(attachmentMimeTypes); 643 } 644 } 645 } 646 647 private void setText(BluetoothMapMessageListingElement e, Cursor c, 648 FilterInfo fi, BluetoothMapAppParams ap) { 649 if ((ap.getParameterMask() & MASK_TEXT) != 0) { 650 String hasText = ""; 651 if (fi.mMsgType == FilterInfo.TYPE_SMS) { 652 hasText = "yes"; 653 } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { 654 int textOnly = c.getInt(fi.mMmsColTextOnly); 655 if (textOnly == 1) { 656 hasText = "yes"; 657 } else { 658 long id = c.getLong(fi.mMmsColId); 659 String text = getTextPartsMms(mResolver, id); 660 if (text != null && text.length() > 0) { 661 hasText = "yes"; 662 } else { 663 hasText = "no"; 664 } 665 } 666 } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL || 667 fi.mMsgType == FilterInfo.TYPE_IM) { 668 hasText = "yes"; 669 } 670 if (V) Log.d(TAG, "setText: " + hasText); 671 e.setText(hasText); 672 } 673 } 674 675 private void setReceptionStatus(BluetoothMapMessageListingElement e, Cursor c, 676 FilterInfo fi, BluetoothMapAppParams ap) { 677 if ((ap.getParameterMask() & MASK_RECEPTION_STATUS) != 0) { 678 String status = "complete"; 679 if (V) Log.d(TAG, "setReceptionStatus: " + status); 680 e.setReceptionStatus(status); 681 } 682 } 683 684 private void setDeliveryStatus(BluetoothMapMessageListingElement e, Cursor c, 685 FilterInfo fi, BluetoothMapAppParams ap) { 686 if ((ap.getParameterMask() & MASK_DELIVERY_STATUS) != 0) { 687 String deliveryStatus = "delivered"; 688 // TODO: Should be handled for SMS and MMS as well 689 if (fi.mMsgType == FilterInfo.TYPE_EMAIL || 690 fi.mMsgType == FilterInfo.TYPE_IM) { 691 deliveryStatus = c.getString(fi.mMessageColDelivery); 692 } 693 if (V) Log.d(TAG, "setDeliveryStatus: " + deliveryStatus); 694 e.setDeliveryStatus(deliveryStatus); 695 } 696 } 697 698 private void setSize(BluetoothMapMessageListingElement e, Cursor c, 699 FilterInfo fi, BluetoothMapAppParams ap) { 700 if ((ap.getParameterMask() & MASK_SIZE) != 0) { 701 int size = 0; 702 if (fi.mMsgType == FilterInfo.TYPE_SMS) { 703 String subject = c.getString(fi.mSmsColSubject); 704 size = subject.length(); 705 } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { 706 size = c.getInt(fi.mMmsColSize); 707 //MMS complete size = attachment_size + subject length 708 String subject = e.getSubject(); 709 if (subject == null || subject.length() == 0 ) { 710 // Handle setSubject if not done case 711 setSubject(e, c, fi, ap); 712 } 713 if (subject != null && subject.length() != 0 ) 714 size += subject.length(); 715 } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL || 716 fi.mMsgType == FilterInfo.TYPE_IM) { 717 size = c.getInt(fi.mMessageColSize); 718 } 719 if(size <= 0) { 720 // A message cannot have size 0 721 // Hence the size in the database must be wrong. 722 // Set size to 1 to indicate to the client, that the message has content. 723 if (D) Log.d(TAG, "Error in message database, size reported as: " + size 724 + " Changing size to 1"); 725 size = 1; 726 } 727 if (V) Log.d(TAG, "setSize: " + size); 728 e.setSize(size); 729 } 730 } 731 732 private TYPE getType(Cursor c, FilterInfo fi) { 733 TYPE type = null; 734 if (V) Log.d(TAG, "getType: for filterMsgType" + fi.mMsgType); 735 if (fi.mMsgType == FilterInfo.TYPE_SMS) { 736 if (V) Log.d(TAG, "getType: phoneType for SMS " + fi.mPhoneType); 737 if (fi.mPhoneType == TelephonyManager.PHONE_TYPE_CDMA) { 738 type = TYPE.SMS_CDMA; 739 } else { 740 type = TYPE.SMS_GSM; 741 } 742 } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { 743 type = TYPE.MMS; 744 } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL) { 745 type = TYPE.EMAIL; 746 } else if (fi.mMsgType == FilterInfo.TYPE_IM) { 747 type = TYPE.IM; 748 } 749 if (V) Log.d(TAG, "getType: " + type); 750 751 return type; 752 } 753 private void setFolderType(BluetoothMapMessageListingElement e, Cursor c, 754 FilterInfo fi, BluetoothMapAppParams ap) { 755 if ((ap.getParameterMask() & MASK_FOLDER_TYPE) != 0) { 756 String folderType = null; 757 int folderId = 0; 758 if (fi.mMsgType == FilterInfo.TYPE_SMS) { 759 folderId = c.getInt(fi.mSmsColFolder); 760 if (folderId == 1) 761 folderType = BluetoothMapContract.FOLDER_NAME_INBOX; 762 else if (folderId == 2) 763 folderType = BluetoothMapContract.FOLDER_NAME_SENT; 764 else if (folderId == 3) 765 folderType = BluetoothMapContract.FOLDER_NAME_DRAFT; 766 else if (folderId == 4 || folderId == 5 || folderId == 6) 767 folderType = BluetoothMapContract.FOLDER_NAME_OUTBOX; 768 else 769 folderType = BluetoothMapContract.FOLDER_NAME_DELETED; 770 } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { 771 folderId = c.getInt(fi.mMmsColFolder); 772 if (folderId == 1) 773 folderType = BluetoothMapContract.FOLDER_NAME_INBOX; 774 else if (folderId == 2) 775 folderType = BluetoothMapContract.FOLDER_NAME_SENT; 776 else if (folderId == 3) 777 folderType = BluetoothMapContract.FOLDER_NAME_DRAFT; 778 else if (folderId == 4) 779 folderType = BluetoothMapContract.FOLDER_NAME_OUTBOX; 780 else 781 folderType = BluetoothMapContract.FOLDER_NAME_DELETED; 782 } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL) { 783 // TODO: need to find name from id and then set folder type 784 } else if (fi.mMsgType == FilterInfo.TYPE_IM) { 785 folderId = c.getInt(fi.mMessageColFolder); 786 if (folderId == BluetoothMapContract.FOLDER_ID_INBOX) 787 folderType = BluetoothMapContract.FOLDER_NAME_INBOX; 788 else if (folderId == BluetoothMapContract.FOLDER_ID_SENT) 789 folderType = BluetoothMapContract.FOLDER_NAME_SENT; 790 else if (folderId == BluetoothMapContract.FOLDER_ID_DRAFT) 791 folderType = BluetoothMapContract.FOLDER_NAME_DRAFT; 792 else if (folderId == BluetoothMapContract.FOLDER_ID_OUTBOX) 793 folderType = BluetoothMapContract.FOLDER_NAME_OUTBOX; 794 else if (folderId == BluetoothMapContract.FOLDER_ID_DELETED) 795 folderType = BluetoothMapContract.FOLDER_NAME_DELETED; 796 else 797 folderType = BluetoothMapContract.FOLDER_NAME_OTHER; 798 } 799 if (V) Log.d(TAG, "setFolderType: " + folderType); 800 e.setFolderType(folderType); 801 } 802 } 803 804 private String getRecipientNameEmail(BluetoothMapMessageListingElement e, 805 Cursor c, 806 FilterInfo fi) { 807 808 String toAddress, ccAddress, bccAddress; 809 toAddress = c.getString(fi.mMessageColToAddress); 810 ccAddress = c.getString(fi.mMessageColCcAddress); 811 bccAddress = c.getString(fi.mMessageColBccAddress); 812 813 StringBuilder sb = new StringBuilder(); 814 if (toAddress != null) { 815 Rfc822Token tokens[] = Rfc822Tokenizer.tokenize(toAddress); 816 if (tokens.length != 0) { 817 if(D) Log.d(TAG, "toName count= " + tokens.length); 818 int i = 0; 819 boolean first = true; 820 while (i < tokens.length) { 821 if(V) Log.d(TAG, "ToName = " + tokens[i].toString()); 822 String name = tokens[i].getName(); 823 if(!first) sb.append("; "); //Delimiter 824 sb.append(name); 825 first = false; 826 i++; 827 } 828 } 829 830 if (ccAddress != null) { 831 sb.append("; "); 832 } 833 } 834 if (ccAddress != null) { 835 Rfc822Token tokens[] = Rfc822Tokenizer.tokenize(ccAddress); 836 if (tokens.length != 0) { 837 if(D) Log.d(TAG, "ccName count= " + tokens.length); 838 int i = 0; 839 boolean first = true; 840 while (i < tokens.length) { 841 if(V) Log.d(TAG, "ccName = " + tokens[i].toString()); 842 String name = tokens[i].getName(); 843 if(!first) sb.append("; "); //Delimiter 844 sb.append(name); 845 first = false; 846 i++; 847 } 848 } 849 if (bccAddress != null) { 850 sb.append("; "); 851 } 852 } 853 if (bccAddress != null) { 854 Rfc822Token tokens[] = Rfc822Tokenizer.tokenize(bccAddress); 855 if (tokens.length != 0) { 856 if(D) Log.d(TAG, "bccName count= " + tokens.length); 857 int i = 0; 858 boolean first = true; 859 while (i < tokens.length) { 860 if(V) Log.d(TAG, "bccName = " + tokens[i].toString()); 861 String name = tokens[i].getName(); 862 if(!first) sb.append("; "); //Delimiter 863 sb.append(name); 864 first = false; 865 i++; 866 } 867 } 868 } 869 return sb.toString(); 870 } 871 872 private String getRecipientAddressingEmail(BluetoothMapMessageListingElement e, 873 Cursor c, 874 FilterInfo fi) { 875 String toAddress, ccAddress, bccAddress; 876 toAddress = c.getString(fi.mMessageColToAddress); 877 ccAddress = c.getString(fi.mMessageColCcAddress); 878 bccAddress = c.getString(fi.mMessageColBccAddress); 879 880 StringBuilder sb = new StringBuilder(); 881 if (toAddress != null) { 882 Rfc822Token tokens[] = Rfc822Tokenizer.tokenize(toAddress); 883 if (tokens.length != 0) { 884 if(D) Log.d(TAG, "toAddress count= " + tokens.length); 885 int i = 0; 886 boolean first = true; 887 while (i < tokens.length) { 888 if(V) Log.d(TAG, "ToAddress = " + tokens[i].toString()); 889 String email = tokens[i].getAddress(); 890 if(!first) sb.append("; "); //Delimiter 891 sb.append(email); 892 first = false; 893 i++; 894 } 895 } 896 897 if (ccAddress != null) { 898 sb.append("; "); 899 } 900 } 901 if (ccAddress != null) { 902 Rfc822Token tokens[] = Rfc822Tokenizer.tokenize(ccAddress); 903 if (tokens.length != 0) { 904 if(D) Log.d(TAG, "ccAddress count= " + tokens.length); 905 int i = 0; 906 boolean first = true; 907 while (i < tokens.length) { 908 if(V) Log.d(TAG, "ccAddress = " + tokens[i].toString()); 909 String email = tokens[i].getAddress(); 910 if(!first) sb.append("; "); //Delimiter 911 sb.append(email); 912 first = false; 913 i++; 914 } 915 } 916 if (bccAddress != null) { 917 sb.append("; "); 918 } 919 } 920 if (bccAddress != null) { 921 Rfc822Token tokens[] = Rfc822Tokenizer.tokenize(bccAddress); 922 if (tokens.length != 0) { 923 if(D) Log.d(TAG, "bccAddress count= " + tokens.length); 924 int i = 0; 925 boolean first = true; 926 while (i < tokens.length) { 927 if(V) Log.d(TAG, "bccAddress = " + tokens[i].toString()); 928 String email = tokens[i].getAddress(); 929 if(!first) sb.append("; "); //Delimiter 930 sb.append(email); 931 first = false; 932 i++; 933 } 934 } 935 } 936 return sb.toString(); 937 } 938 939 private void setRecipientAddressing(BluetoothMapMessageListingElement e, Cursor c, 940 FilterInfo fi, BluetoothMapAppParams ap) { 941 if ((ap.getParameterMask() & MASK_RECIPIENT_ADDRESSING) != 0) { 942 String address = null; 943 if (fi.mMsgType == FilterInfo.TYPE_SMS) { 944 int msgType = c.getInt(fi.mSmsColType); 945 if (msgType == Sms.MESSAGE_TYPE_INBOX ) { 946 address = fi.mPhoneNum; 947 } else { 948 address = c.getString(c.getColumnIndex(Sms.ADDRESS)); 949 } 950 if ((address == null) && msgType == Sms.MESSAGE_TYPE_DRAFT) { 951 //Fetch address for Drafts folder from "canonical_address" table 952 int threadIdInd = c.getColumnIndex(Sms.THREAD_ID); 953 String threadIdStr = c.getString(threadIdInd); 954 address = getCanonicalAddressSms(mResolver, Integer.valueOf(threadIdStr)); 955 if(V) Log.v(TAG, "threadId = " + threadIdStr + " adress:" + address +"\n"); 956 } 957 } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { 958 long id = c.getLong(c.getColumnIndex(BaseColumns._ID)); 959 address = getAddressMms(mResolver, id, MMS_TO); 960 } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL) { 961 /* Might be another way to handle addresses */ 962 address = getRecipientAddressingEmail(e, c,fi); 963 } 964 if (V) Log.v(TAG, "setRecipientAddressing: " + address); 965 if(address == null) 966 address = ""; 967 e.setRecipientAddressing(address); 968 } 969 } 970 971 private void setRecipientName(BluetoothMapMessageListingElement e, Cursor c, 972 FilterInfo fi, BluetoothMapAppParams ap) { 973 if ((ap.getParameterMask() & MASK_RECIPIENT_NAME) != 0) { 974 String name = null; 975 if (fi.mMsgType == FilterInfo.TYPE_SMS) { 976 int msgType = c.getInt(fi.mSmsColType); 977 if (msgType != 1) { 978 String phone = c.getString(fi.mSmsColAddress); 979 if (phone != null && !phone.isEmpty()) 980 name = getContactNameFromPhone(phone, mResolver); 981 } else { 982 name = fi.mPhoneAlphaTag; 983 } 984 } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { 985 long id = c.getLong(fi.mMmsColId); 986 String phone; 987 if(e.getRecipientAddressing() != null){ 988 phone = getAddressMms(mResolver, id, MMS_TO); 989 } else { 990 phone = e.getRecipientAddressing(); 991 } 992 if (phone != null && !phone.isEmpty()) 993 name = getContactNameFromPhone(phone, mResolver); 994 } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL) { 995 /* Might be another way to handle address and names */ 996 name = getRecipientNameEmail(e,c,fi); 997 } 998 if (V) Log.v(TAG, "setRecipientName: " + name); 999 if(name == null) 1000 name = ""; 1001 e.setRecipientName(name); 1002 } 1003 } 1004 1005 private void setSenderAddressing(BluetoothMapMessageListingElement e, Cursor c, 1006 FilterInfo fi, BluetoothMapAppParams ap) { 1007 if ((ap.getParameterMask() & MASK_SENDER_ADDRESSING) != 0) { 1008 String address = ""; 1009 String tempAddress; 1010 if (fi.mMsgType == FilterInfo.TYPE_SMS) { 1011 int msgType = c.getInt(fi.mSmsColType); 1012 if (msgType == 1) { // INBOX 1013 tempAddress = c.getString(fi.mSmsColAddress); 1014 } else { 1015 tempAddress = fi.mPhoneNum; 1016 } 1017 if(tempAddress == null) { 1018 /* This can only happen on devices with no SIM - 1019 hence will typically not have any SMS messages. */ 1020 } else { 1021 address = PhoneNumberUtils.extractNetworkPortion(tempAddress); 1022 /* extractNetworkPortion can return N if the number is a service "number" = 1023 * a string with the a name in (i.e. "Some-Tele-company" would return N 1024 * because of the N in compaNy) 1025 * Hence we need to check if the number is actually a string with alpha chars. 1026 * */ 1027 Boolean alpha = PhoneNumberUtils.stripSeparators(tempAddress).matches( 1028 "[0-9]*[a-zA-Z]+[0-9]*"); 1029 1030 if(address == null || address.length() < 2 || alpha) { 1031 address = tempAddress; // if the number is a service acsii text just use it 1032 } 1033 } 1034 } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { 1035 long id = c.getLong(fi.mMmsColId); 1036 tempAddress = getAddressMms(mResolver, id, MMS_FROM); 1037 address = PhoneNumberUtils.extractNetworkPortion(tempAddress); 1038 if(address == null || address.length() < 1){ 1039 address = tempAddress; // if the number is a service acsii text just use it 1040 } 1041 } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL/* || 1042 fi.mMsgType == FilterInfo.TYPE_IM*/) { 1043 String nameEmail = c.getString(fi.mMessageColFromAddress); 1044 Rfc822Token tokens[] = Rfc822Tokenizer.tokenize(nameEmail); 1045 if (tokens.length != 0) { 1046 if(D) Log.d(TAG, "Originator count= " + tokens.length); 1047 int i = 0; 1048 boolean first = true; 1049 while (i < tokens.length) { 1050 if(V) Log.d(TAG, "SenderAddress = " + tokens[i].toString()); 1051 String[] emails = new String[1]; 1052 emails[0] = tokens[i].getAddress(); 1053 String name = tokens[i].getName(); 1054 if(!first) address += "; "; //Delimiter 1055 address += emails[0]; 1056 first = false; 1057 i++; 1058 } 1059 } 1060 } else if(fi.mMsgType == FilterInfo.TYPE_IM) { 1061 // TODO: For IM we add the contact ID in the addressing 1062 long contact_id = c.getLong(fi.mMessageColFromAddress); 1063 // TODO: This is a BAD hack, that we map the contact ID to a conversation ID!!! 1064 // We need to reach a conclusion on what to do 1065 Uri contactsUri = Uri.parse(mBaseUri + BluetoothMapContract.TABLE_CONVOCONTACT); 1066 Cursor contacts = mResolver.query(contactsUri, 1067 BluetoothMapContract.BT_CONTACT_PROJECTION, 1068 BluetoothMapContract.ConvoContactColumns.CONVO_ID 1069 + " = " + contact_id, null, null); 1070 try { 1071 // TODO this will not work for group-chats 1072 if(contacts != null && contacts.moveToFirst()){ 1073 address = contacts.getString( 1074 contacts.getColumnIndex( 1075 BluetoothMapContract.ConvoContactColumns.UCI)); 1076 } 1077 } finally { 1078 if (contacts != null) contacts.close(); 1079 } 1080 1081 } 1082 if (V) Log.v(TAG, "setSenderAddressing: " + address); 1083 if(address == null) 1084 address = ""; 1085 e.setSenderAddressing(address); 1086 } 1087 } 1088 1089 private void setSenderName(BluetoothMapMessageListingElement e, Cursor c, 1090 FilterInfo fi, BluetoothMapAppParams ap) { 1091 if ((ap.getParameterMask() & MASK_SENDER_NAME) != 0) { 1092 String name = ""; 1093 if (fi.mMsgType == FilterInfo.TYPE_SMS) { 1094 int msgType = c.getInt(c.getColumnIndex(Sms.TYPE)); 1095 if (msgType == 1) { 1096 String phone = c.getString(fi.mSmsColAddress); 1097 if (phone != null && !phone.isEmpty()) 1098 name = getContactNameFromPhone(phone, mResolver); 1099 } else { 1100 name = fi.mPhoneAlphaTag; 1101 } 1102 } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { 1103 long id = c.getLong(fi.mMmsColId); 1104 String phone; 1105 if(e.getSenderAddressing() != null){ 1106 phone = getAddressMms(mResolver, id, MMS_FROM); 1107 } else { 1108 phone = e.getSenderAddressing(); 1109 } 1110 if (phone != null && !phone.isEmpty() ) 1111 name = getContactNameFromPhone(phone, mResolver); 1112 } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL/* || 1113 fi.mMsgType == FilterInfo.TYPE_IM*/) { 1114 String nameEmail = c.getString(fi.mMessageColFromAddress); 1115 Rfc822Token tokens[] = Rfc822Tokenizer.tokenize(nameEmail); 1116 if (tokens.length != 0) { 1117 if(D) Log.d(TAG, "Originator count= " + tokens.length); 1118 int i = 0; 1119 boolean first = true; 1120 while (i < tokens.length) { 1121 if(V) Log.d(TAG, "senderName = " + tokens[i].toString()); 1122 String[] emails = new String[1]; 1123 emails[0] = tokens[i].getAddress(); 1124 String nameIn = tokens[i].getName(); 1125 if(!first) name += "; "; //Delimiter 1126 name += nameIn; 1127 first = false; 1128 i++; 1129 } 1130 } 1131 } else if(fi.mMsgType == FilterInfo.TYPE_IM) { 1132 // For IM we add the contact ID in the addressing 1133 long contact_id = c.getLong(fi.mMessageColFromAddress); 1134 Uri contactsUri = Uri.parse(mBaseUri + BluetoothMapContract.TABLE_CONVOCONTACT); 1135 Cursor contacts = mResolver.query(contactsUri, 1136 BluetoothMapContract.BT_CONTACT_PROJECTION, 1137 BluetoothMapContract.ConvoContactColumns.CONVO_ID 1138 + " = " + contact_id, null, null); 1139 try { 1140 // TODO this will not work for group-chats 1141 if(contacts != null && contacts.moveToFirst()){ 1142 name = contacts.getString( 1143 contacts.getColumnIndex( 1144 BluetoothMapContract.ConvoContactColumns.NAME)); 1145 } 1146 } finally { 1147 if (contacts != null) contacts.close(); 1148 } 1149 } 1150 if (V) Log.v(TAG, "setSenderName: " + name); 1151 if(name == null) 1152 name = ""; 1153 e.setSenderName(name); 1154 } 1155 } 1156 1157 1158 1159 1160 private void setDateTime(BluetoothMapMessageListingElement e, Cursor c, 1161 FilterInfo fi, BluetoothMapAppParams ap) { 1162 if ((ap.getParameterMask() & MASK_DATETIME) != 0) { 1163 long date = 0; 1164 if (fi.mMsgType == FilterInfo.TYPE_SMS) { 1165 date = c.getLong(fi.mSmsColDate); 1166 } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { 1167 /* Use Mms.DATE for all messages. Although contract class states */ 1168 /* Mms.DATE_SENT are for outgoing messages. But that is not working. */ 1169 date = c.getLong(fi.mMmsColDate) * 1000L; 1170 1171 /* int msgBox = c.getInt(c.getColumnIndex(Mms.MESSAGE_BOX)); */ 1172 /* if (msgBox == Mms.MESSAGE_BOX_INBOX) { */ 1173 /* date = c.getLong(c.getColumnIndex(Mms.DATE)) * 1000L; */ 1174 /* } else { */ 1175 /* date = c.getLong(c.getColumnIndex(Mms.DATE_SENT)) * 1000L; */ 1176 /* } */ 1177 } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL || 1178 fi.mMsgType == FilterInfo.TYPE_IM) { 1179 date = c.getLong(fi.mMessageColDate); 1180 } 1181 e.setDateTime(date); 1182 } 1183 } 1184 1185 1186 private void setLastActivity(BluetoothMapConvoListingElement e, Cursor c, 1187 FilterInfo fi, BluetoothMapAppParams ap) { 1188 long date = 0; 1189 if (fi.mMsgType == FilterInfo.TYPE_SMS || 1190 fi.mMsgType == FilterInfo.TYPE_MMS ) { 1191 date = c.getLong(MMS_SMS_THREAD_COL_DATE); 1192 } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL|| 1193 fi.mMsgType == FilterInfo.TYPE_IM) { 1194 date = c.getLong(fi.mConvoColLastActivity); 1195 } 1196 e.setLastActivity(date); 1197 if (V) Log.v(TAG, "setDateTime: " + e.getLastActivityString()); 1198 1199 } 1200 1201 static public String getTextPartsMms(ContentResolver r, long id) { 1202 String text = ""; 1203 String selection = new String("mid=" + id); 1204 String uriStr = new String(Mms.CONTENT_URI + "/" + id + "/part"); 1205 Uri uriAddress = Uri.parse(uriStr); 1206 // TODO: maybe use a projection with only "ct" and "text" 1207 Cursor c = r.query(uriAddress, null, selection, 1208 null, null); 1209 try { 1210 if (c != null && c.moveToFirst()) { 1211 do { 1212 String ct = c.getString(c.getColumnIndex("ct")); 1213 if (ct.equals("text/plain")) { 1214 String part = c.getString(c.getColumnIndex("text")); 1215 if(part != null) { 1216 text += part; 1217 } 1218 } 1219 } while(c.moveToNext()); 1220 } 1221 } finally { 1222 if (c != null) c.close(); 1223 } 1224 1225 return text; 1226 } 1227 1228 private void setSubject(BluetoothMapMessageListingElement e, Cursor c, 1229 FilterInfo fi, BluetoothMapAppParams ap) { 1230 String subject = ""; 1231 int subLength = ap.getSubjectLength(); 1232 if(subLength == BluetoothMapAppParams.INVALID_VALUE_PARAMETER) 1233 subLength = 256; 1234 1235 if ((ap.getParameterMask() & MASK_SUBJECT) != 0) { 1236 if (fi.mMsgType == FilterInfo.TYPE_SMS) { 1237 subject = c.getString(fi.mSmsColSubject); 1238 } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { 1239 subject = c.getString(fi.mMmsColSubject); 1240 if (subject == null || subject.length() == 0) { 1241 /* Get subject from mms text body parts - if any exists */ 1242 long id = c.getLong(fi.mMmsColId); 1243 subject = getTextPartsMms(mResolver, id); 1244 } 1245 } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL || 1246 fi.mMsgType == FilterInfo.TYPE_IM) { 1247 subject = c.getString(fi.mMessageColSubject); 1248 } 1249 if (subject != null && subject.length() > subLength) { 1250 subject = subject.substring(0, subLength); 1251 } else if (subject == null ) { 1252 subject = ""; 1253 } 1254 if (V) Log.d(TAG, "setSubject: " + subject); 1255 e.setSubject(subject); 1256 } 1257 } 1258 1259 private void setHandle(BluetoothMapMessageListingElement e, Cursor c, 1260 FilterInfo fi, BluetoothMapAppParams ap) { 1261 long handle = -1; 1262 if (fi.mMsgType == FilterInfo.TYPE_SMS) { 1263 handle = c.getLong(fi.mSmsColId); 1264 } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { 1265 handle = c.getLong(fi.mMmsColId); 1266 } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL || 1267 fi.mMsgType == FilterInfo.TYPE_IM) { 1268 handle = c.getLong(fi.mMessageColId); 1269 } 1270 if (V) Log.d(TAG, "setHandle: " + handle ); 1271 e.setHandle(handle); 1272 } 1273 1274 private BluetoothMapMessageListingElement element(Cursor c, FilterInfo fi, 1275 BluetoothMapAppParams ap) { 1276 BluetoothMapMessageListingElement e = new BluetoothMapMessageListingElement(); 1277 setHandle(e, c, fi, ap); 1278 setDateTime(e, c, fi, ap); 1279 e.setType(getType(c, fi), ((ap.getParameterMask() & MASK_TYPE) != 0) ? true : false); 1280 setRead(e, c, fi, ap); 1281 // we set number and name for sender/recipient later 1282 // they require lookup on contacts so no need to 1283 // do it for all elements unless they are to be used. 1284 e.setCursorIndex(c.getPosition()); 1285 return e; 1286 } 1287 1288 private BluetoothMapConvoListingElement createConvoElement(Cursor c, FilterInfo fi, 1289 BluetoothMapAppParams ap) { 1290 BluetoothMapConvoListingElement e = new BluetoothMapConvoListingElement(); 1291 setLastActivity(e, c, fi, ap); 1292 e.setType(getType(c, fi)); 1293// setConvoRead(e, c, fi, ap); 1294 e.setCursorIndex(c.getPosition()); 1295 return e; 1296 } 1297 1298 /* TODO: Change to use SmsMmsContacts.getContactNameFromPhone() with proper use of 1299 * caching. */ 1300 public static String getContactNameFromPhone(String phone, ContentResolver resolver) { 1301 String name = null; 1302 //Handle possible exception for empty phone address 1303 if (TextUtils.isEmpty(phone)) { 1304 return name; 1305 } 1306 1307 Uri uri = Uri.withAppendedPath(PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI, 1308 Uri.encode(phone)); 1309 1310 String[] projection = {Contacts._ID, Contacts.DISPLAY_NAME}; 1311 String selection = Contacts.IN_VISIBLE_GROUP + "=1"; 1312 String orderBy = Contacts.DISPLAY_NAME + " ASC"; 1313 Cursor c = null; 1314 try { 1315 c = resolver.query(uri, projection, selection, null, orderBy); 1316 if(c != null) { 1317 int colIndex = c.getColumnIndex(Contacts.DISPLAY_NAME); 1318 if (c.getCount() >= 1) { 1319 c.moveToFirst(); 1320 name = c.getString(colIndex); 1321 } 1322 } 1323 } finally { 1324 if(c != null) c.close(); 1325 } 1326 return name; 1327 } 1328 /** 1329 * Get SMS RecipientAddresses for DRAFT folder based on threadId 1330 * 1331 */ 1332 static public String getCanonicalAddressSms(ContentResolver r, int threadId) { 1333 String [] RECIPIENT_ID_PROJECTION = { Threads.RECIPIENT_IDS }; 1334 /* 1335 1. Get Recipient Ids from Threads.CONTENT_URI 1336 2. Get Recipient Address for corresponding Id from canonical-addresses table. 1337 */ 1338 1339 //Uri sAllCanonical = Uri.parse("content://mms-sms/canonical-addresses"); 1340 Uri sAllCanonical = 1341 MmsSms.CONTENT_URI.buildUpon().appendPath("canonical-addresses").build(); 1342 Uri sAllThreadsUri = 1343 Threads.CONTENT_URI.buildUpon().appendQueryParameter("simple", "true").build(); 1344 Cursor cr = null; 1345 String recipientAddress = ""; 1346 String recipientIds = null; 1347 String whereClause = "_id="+threadId; 1348 if (V) Log.v(TAG, "whereClause is "+ whereClause); 1349 try { 1350 cr = r.query(sAllThreadsUri, RECIPIENT_ID_PROJECTION, whereClause, null, null); 1351 if (cr != null && cr.moveToFirst()) { 1352 recipientIds = cr.getString(0); 1353 if (V) Log.v(TAG, "cursor.getCount(): " + cr.getCount() + "recipientIds: " 1354 + recipientIds + "selection: "+ whereClause ); 1355 } 1356 } finally { 1357 if(cr != null) { 1358 cr.close(); 1359 cr = null; 1360 } 1361 } 1362 if (V) Log.v(TAG, "recipientIds with spaces: "+ recipientIds +"\n"); 1363 if(recipientIds != null) { 1364 String recipients[] = null; 1365 whereClause = ""; 1366 recipients = recipientIds.split(" "); 1367 for (String id: recipients) { 1368 if(whereClause.length() != 0) 1369 whereClause +=" OR "; 1370 whereClause +="_id="+id; 1371 } 1372 if (V) Log.v(TAG, "whereClause is "+ whereClause); 1373 try { 1374 cr = r.query(sAllCanonical , null, whereClause, null, null); 1375 if (cr != null && cr.moveToFirst()) { 1376 do { 1377 //TODO: Multiple Recipeints are appended with ";" for now. 1378 if(recipientAddress.length() != 0 ) 1379 recipientAddress+=";"; 1380 recipientAddress += cr.getString( 1381 cr.getColumnIndex(CanonicalAddressesColumns.ADDRESS)); 1382 } while(cr.moveToNext()); 1383 } 1384 } finally { 1385 if(cr != null) 1386 cr.close(); 1387 } 1388 } 1389 1390 if(V) Log.v(TAG,"Final recipientAddress : "+ recipientAddress); 1391 return recipientAddress; 1392 } 1393 1394 static public String getAddressMms(ContentResolver r, long id, int type) { 1395 String selection = new String("msg_id=" + id + " AND type=" + type); 1396 String uriStr = new String(Mms.CONTENT_URI + "/" + id + "/addr"); 1397 Uri uriAddress = Uri.parse(uriStr); 1398 String addr = null; 1399 String[] projection = {Mms.Addr.ADDRESS}; 1400 Cursor c = null; 1401 try { 1402 c = r.query(uriAddress, projection, selection, null, null); // TODO: Add projection 1403 int colIndex = c.getColumnIndex(Mms.Addr.ADDRESS); 1404 if (c != null) { 1405 if(c.moveToFirst()) { 1406 addr = c.getString(colIndex); 1407 if(addr.equals(INSERT_ADDRES_TOKEN)) { 1408 addr = ""; 1409 } 1410 } 1411 } 1412 } finally { 1413 if (c != null) c.close(); 1414 } 1415 return addr; 1416 } 1417 1418 /** 1419 * Matching functions for originator and recipient for MMS 1420 * @return true if found a match 1421 */ 1422 private boolean matchRecipientMms(Cursor c, FilterInfo fi, String recip) { 1423 boolean res; 1424 long id = c.getLong(c.getColumnIndex(BaseColumns._ID)); 1425 String phone = getAddressMms(mResolver, id, MMS_TO); 1426 if (phone != null && phone.length() > 0) { 1427 if (phone.matches(recip)) { 1428 if (V) Log.v(TAG, "matchRecipientMms: match recipient phone = " + phone); 1429 res = true; 1430 } else { 1431 String name = getContactNameFromPhone(phone, mResolver); 1432 if (name != null && name.length() > 0 && name.matches(recip)) { 1433 if (V) Log.v(TAG, "matchRecipientMms: match recipient name = " + name); 1434 res = true; 1435 } else { 1436 res = false; 1437 } 1438 } 1439 } else { 1440 res = false; 1441 } 1442 return res; 1443 } 1444 1445 private boolean matchRecipientSms(Cursor c, FilterInfo fi, String recip) { 1446 boolean res; 1447 int msgType = c.getInt(c.getColumnIndex(Sms.TYPE)); 1448 if (msgType == 1) { 1449 String phone = fi.mPhoneNum; 1450 String name = fi.mPhoneAlphaTag; 1451 if (phone != null && phone.length() > 0 && phone.matches(recip)) { 1452 if (V) Log.v(TAG, "matchRecipientSms: match recipient phone = " + phone); 1453 res = true; 1454 } else if (name != null && name.length() > 0 && name.matches(recip)) { 1455 if (V) Log.v(TAG, "matchRecipientSms: match recipient name = " + name); 1456 res = true; 1457 } else { 1458 res = false; 1459 } 1460 } else { 1461 String phone = c.getString(c.getColumnIndex(Sms.ADDRESS)); 1462 if (phone != null && phone.length() > 0) { 1463 if (phone.matches(recip)) { 1464 if (V) Log.v(TAG, "matchRecipientSms: match recipient phone = " + phone); 1465 res = true; 1466 } else { 1467 String name = getContactNameFromPhone(phone, mResolver); 1468 if (name != null && name.length() > 0 && name.matches(recip)) { 1469 if (V) Log.v(TAG, "matchRecipientSms: match recipient name = " + name); 1470 res = true; 1471 } else { 1472 res = false; 1473 } 1474 } 1475 } else { 1476 res = false; 1477 } 1478 } 1479 return res; 1480 } 1481 1482 private boolean matchRecipient(Cursor c, FilterInfo fi, BluetoothMapAppParams ap) { 1483 boolean res; 1484 String recip = ap.getFilterRecipient(); 1485 if (recip != null && recip.length() > 0) { 1486 recip = recip.replace("*", ".*"); 1487 recip = ".*" + recip + ".*"; 1488 if (fi.mMsgType == FilterInfo.TYPE_SMS) { 1489 res = matchRecipientSms(c, fi, recip); 1490 } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { 1491 res = matchRecipientMms(c, fi, recip); 1492 } else { 1493 if (D) Log.d(TAG, "matchRecipient: Unknown msg type: " + fi.mMsgType); 1494 res = false; 1495 } 1496 } else { 1497 res = true; 1498 } 1499 return res; 1500 } 1501 1502 private boolean matchOriginatorMms(Cursor c, FilterInfo fi, String orig) { 1503 boolean res; 1504 long id = c.getLong(c.getColumnIndex(BaseColumns._ID)); 1505 String phone = getAddressMms(mResolver, id, MMS_FROM); 1506 if (phone != null && phone.length() > 0) { 1507 if (phone.matches(orig)) { 1508 if (V) Log.v(TAG, "matchOriginatorMms: match originator phone = " + phone); 1509 res = true; 1510 } else { 1511 String name = getContactNameFromPhone(phone, mResolver); 1512 if (name != null && name.length() > 0 && name.matches(orig)) { 1513 if (V) Log.v(TAG, "matchOriginatorMms: match originator name = " + name); 1514 res = true; 1515 } else { 1516 res = false; 1517 } 1518 } 1519 } else { 1520 res = false; 1521 } 1522 return res; 1523 } 1524 1525 private boolean matchOriginatorSms(Cursor c, FilterInfo fi, String orig) { 1526 boolean res; 1527 int msgType = c.getInt(c.getColumnIndex(Sms.TYPE)); 1528 if (msgType == 1) { 1529 String phone = c.getString(c.getColumnIndex(Sms.ADDRESS)); 1530 if (phone !=null && phone.length() > 0) { 1531 if (phone.matches(orig)) { 1532 if (V) Log.v(TAG, "matchOriginatorSms: match originator phone = " + phone); 1533 res = true; 1534 } else { 1535 String name = getContactNameFromPhone(phone, mResolver); 1536 if (name != null && name.length() > 0 && name.matches(orig)) { 1537 if (V) Log.v(TAG, "matchOriginatorSms: match originator name = " + name); 1538 res = true; 1539 } else { 1540 res = false; 1541 } 1542 } 1543 } else { 1544 res = false; 1545 } 1546 } else { 1547 String phone = fi.mPhoneNum; 1548 String name = fi.mPhoneAlphaTag; 1549 if (phone != null && phone.length() > 0 && phone.matches(orig)) { 1550 if (V) Log.v(TAG, "matchOriginatorSms: match originator phone = " + phone); 1551 res = true; 1552 } else if (name != null && name.length() > 0 && name.matches(orig)) { 1553 if (V) Log.v(TAG, "matchOriginatorSms: match originator name = " + name); 1554 res = true; 1555 } else { 1556 res = false; 1557 } 1558 } 1559 return res; 1560 } 1561 1562 private boolean matchOriginator(Cursor c, FilterInfo fi, BluetoothMapAppParams ap) { 1563 boolean res; 1564 String orig = ap.getFilterOriginator(); 1565 if (orig != null && orig.length() > 0) { 1566 orig = orig.replace("*", ".*"); 1567 orig = ".*" + orig + ".*"; 1568 if (fi.mMsgType == FilterInfo.TYPE_SMS) { 1569 res = matchOriginatorSms(c, fi, orig); 1570 } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { 1571 res = matchOriginatorMms(c, fi, orig); 1572 } else { 1573 if(D) Log.d(TAG, "matchOriginator: Unknown msg type: " + fi.mMsgType); 1574 res = false; 1575 } 1576 } else { 1577 res = true; 1578 } 1579 return res; 1580 } 1581 1582 private boolean matchAddresses(Cursor c, FilterInfo fi, BluetoothMapAppParams ap) { 1583 if (matchOriginator(c, fi, ap) && matchRecipient(c, fi, ap)) { 1584 return true; 1585 } else { 1586 return false; 1587 } 1588 } 1589 1590 /* 1591 * Where filter functions 1592 * */ 1593 private String setWhereFilterFolderTypeSms(String folder) { 1594 String where = ""; 1595 if (BluetoothMapContract.FOLDER_NAME_INBOX.equalsIgnoreCase(folder)) { 1596 where = Sms.TYPE + " = 1 AND " + Sms.THREAD_ID + " <> -1"; 1597 } else if (BluetoothMapContract.FOLDER_NAME_OUTBOX.equalsIgnoreCase(folder)) { 1598 where = "(" + Sms.TYPE + " = 4 OR " + Sms.TYPE + " = 5 OR " 1599 + Sms.TYPE + " = 6) AND " + Sms.THREAD_ID + " <> -1"; 1600 } else if (BluetoothMapContract.FOLDER_NAME_SENT.equalsIgnoreCase(folder)) { 1601 where = Sms.TYPE + " = 2 AND " + Sms.THREAD_ID + " <> -1"; 1602 } else if (BluetoothMapContract.FOLDER_NAME_DRAFT.equalsIgnoreCase(folder)) { 1603 where = Sms.TYPE + " = 3 AND " + Sms.THREAD_ID + " <> -1"; 1604 } else if (BluetoothMapContract.FOLDER_NAME_DELETED.equalsIgnoreCase(folder)) { 1605 where = Sms.THREAD_ID + " = -1"; 1606 } 1607 1608 return where; 1609 } 1610 1611 private String setWhereFilterFolderTypeMms(String folder) { 1612 String where = ""; 1613 if (BluetoothMapContract.FOLDER_NAME_INBOX.equalsIgnoreCase(folder)) { 1614 where = Mms.MESSAGE_BOX + " = 1 AND " + Mms.THREAD_ID + " <> -1"; 1615 } else if (BluetoothMapContract.FOLDER_NAME_OUTBOX.equalsIgnoreCase(folder)) { 1616 where = Mms.MESSAGE_BOX + " = 4 AND " + Mms.THREAD_ID + " <> -1"; 1617 } else if (BluetoothMapContract.FOLDER_NAME_SENT.equalsIgnoreCase(folder)) { 1618 where = Mms.MESSAGE_BOX + " = 2 AND " + Mms.THREAD_ID + " <> -1"; 1619 } else if (BluetoothMapContract.FOLDER_NAME_DRAFT.equalsIgnoreCase(folder)) { 1620 where = Mms.MESSAGE_BOX + " = 3 AND " + Mms.THREAD_ID + " <> -1"; 1621 } else if (BluetoothMapContract.FOLDER_NAME_DELETED.equalsIgnoreCase(folder)) { 1622 where = Mms.THREAD_ID + " = -1"; 1623 } 1624 1625 return where; 1626 } 1627 1628 private String setWhereFilterFolderTypeEmail(long folderId) { 1629 String where = ""; 1630 if (folderId >= 0) { 1631 where = BluetoothMapContract.MessageColumns.FOLDER_ID + " = " + folderId; 1632 } else { 1633 Log.e(TAG, "setWhereFilterFolderTypeEmail: not valid!" ); 1634 throw new IllegalArgumentException("Invalid folder ID"); 1635 } 1636 return where; 1637 } 1638 1639 private String setWhereFilterFolderTypeIm(long folderId) { 1640 String where = ""; 1641 if (folderId > BluetoothMapContract.FOLDER_ID_OTHER) { 1642 where = BluetoothMapContract.MessageColumns.FOLDER_ID + " = " + folderId; 1643 } else { 1644 Log.e(TAG, "setWhereFilterFolderTypeIm: not valid!" ); 1645 throw new IllegalArgumentException("Invalid folder ID"); 1646 } 1647 return where; 1648 } 1649 1650 private String setWhereFilterFolderType(BluetoothMapFolderElement folderElement, 1651 FilterInfo fi) { 1652 String where = ""; 1653 if(folderElement.shouldIgnore()) { 1654 where = "1=1"; 1655 } else { 1656 if (fi.mMsgType == FilterInfo.TYPE_SMS) { 1657 where = setWhereFilterFolderTypeSms(folderElement.getName()); 1658 } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { 1659 where = setWhereFilterFolderTypeMms(folderElement.getName()); 1660 } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL) { 1661 where = setWhereFilterFolderTypeEmail(folderElement.getFolderId()); 1662 } else if (fi.mMsgType == FilterInfo.TYPE_IM) { 1663 where = setWhereFilterFolderTypeIm(folderElement.getFolderId()); 1664 } 1665 } 1666 return where; 1667 } 1668 1669 private String setWhereFilterReadStatus(BluetoothMapAppParams ap, FilterInfo fi) { 1670 String where = ""; 1671 if (ap.getFilterReadStatus() != -1) { 1672 if (fi.mMsgType == FilterInfo.TYPE_SMS) { 1673 if ((ap.getFilterReadStatus() & 0x01) != 0) { 1674 where = " AND " + Sms.READ + "= 0"; 1675 } 1676 1677 if ((ap.getFilterReadStatus() & 0x02) != 0) { 1678 where = " AND " + Sms.READ + "= 1"; 1679 } 1680 } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { 1681 if ((ap.getFilterReadStatus() & 0x01) != 0) { 1682 where = " AND " + Mms.READ + "= 0"; 1683 } 1684 1685 if ((ap.getFilterReadStatus() & 0x02) != 0) { 1686 where = " AND " + Mms.READ + "= 1"; 1687 } 1688 } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL || 1689 fi.mMsgType == FilterInfo.TYPE_IM) { 1690 if ((ap.getFilterReadStatus() & 0x01) != 0) { 1691 where = " AND " + BluetoothMapContract.MessageColumns.FLAG_READ + "= 0"; 1692 } 1693 if ((ap.getFilterReadStatus() & 0x02) != 0) { 1694 where = " AND " + BluetoothMapContract.MessageColumns.FLAG_READ + "= 1"; 1695 } 1696 } 1697 } 1698 return where; 1699 } 1700 1701 private String setWhereFilterPeriod(BluetoothMapAppParams ap, FilterInfo fi) { 1702 String where = ""; 1703 1704 if ((ap.getFilterPeriodBegin() != -1)) { 1705 if (fi.mMsgType == FilterInfo.TYPE_SMS) { 1706 where = " AND " + Sms.DATE + " >= " + ap.getFilterPeriodBegin(); 1707 } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { 1708 where = " AND " + Mms.DATE + " >= " + (ap.getFilterPeriodBegin() / 1000L); 1709 } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL|| 1710 fi.mMsgType == FilterInfo.TYPE_IM) { 1711 where = " AND " + BluetoothMapContract.MessageColumns.DATE + 1712 " >= " + (ap.getFilterPeriodBegin()); 1713 } 1714 } 1715 1716 if ((ap.getFilterPeriodEnd() != -1)) { 1717 if (fi.mMsgType == FilterInfo.TYPE_SMS) { 1718 where += " AND " + Sms.DATE + " < " + ap.getFilterPeriodEnd(); 1719 } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { 1720 where += " AND " + Mms.DATE + " < " + (ap.getFilterPeriodEnd() / 1000L); 1721 } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL|| 1722 fi.mMsgType == FilterInfo.TYPE_IM) { 1723 where += " AND " + BluetoothMapContract.MessageColumns.DATE + 1724 " < " + (ap.getFilterPeriodEnd()); 1725 } 1726 } 1727 return where; 1728 } 1729 private String setWhereFilterLastActivity(BluetoothMapAppParams ap, FilterInfo fi) { 1730 String where = ""; 1731 if ((ap.getFilterLastActivityBegin() != -1)) { 1732 if (fi.mMsgType == FilterInfo.TYPE_SMS) { 1733 where = " AND " + Sms.DATE + " >= " + ap.getFilterLastActivityBegin(); 1734 } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { 1735 where = " AND " + Mms.DATE + " >= " + (ap.getFilterLastActivityBegin() / 1000L); 1736 } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL|| 1737 fi.mMsgType == FilterInfo.TYPE_IM ) { 1738 where = " AND " + BluetoothMapContract.ConversationColumns.LAST_THREAD_ACTIVITY + 1739 " >= " + (ap.getFilterPeriodBegin()); 1740 } 1741 } 1742 if ((ap.getFilterLastActivityEnd() != -1)) { 1743 if (fi.mMsgType == FilterInfo.TYPE_SMS) { 1744 where += " AND " + Sms.DATE + " < " + ap.getFilterLastActivityEnd(); 1745 } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { 1746 where += " AND " + Mms.DATE + " < " + (ap.getFilterPeriodEnd() / 1000L); 1747 } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL||fi.mMsgType == FilterInfo.TYPE_IM) { 1748 where += " AND " + BluetoothMapContract.ConversationColumns.LAST_THREAD_ACTIVITY 1749 + " < " + (ap.getFilterLastActivityEnd()); 1750 } 1751 } 1752 return where; 1753 } 1754 1755 1756 private String setWhereFilterOriginatorEmail(BluetoothMapAppParams ap) { 1757 String where = ""; 1758 String orig = ap.getFilterOriginator(); 1759 1760 /* Be aware of wild cards in the beginning of string, may not be valid? */ 1761 if (orig != null && orig.length() > 0) { 1762 orig = orig.replace("*", "%"); 1763 where = " AND " + BluetoothMapContract.MessageColumns.FROM_LIST 1764 + " LIKE '%" + orig + "%'"; 1765 } 1766 return where; 1767 } 1768 1769 private String setWhereFilterOriginatorIM(BluetoothMapAppParams ap) { 1770 String where = ""; 1771 String orig = ap.getFilterOriginator(); 1772 1773 /* Be aware of wild cards in the beginning of string, may not be valid? */ 1774 if (orig != null && orig.length() > 0) { 1775 orig = orig.replace("*", "%"); 1776 where = " AND " + BluetoothMapContract.MessageColumns.FROM_LIST 1777 + " LIKE '%" + orig + "%'"; 1778 } 1779 return where; 1780 } 1781 1782 private String setWhereFilterPriority(BluetoothMapAppParams ap, FilterInfo fi) { 1783 String where = ""; 1784 int pri = ap.getFilterPriority(); 1785 /*only MMS have priority info */ 1786 if(fi.mMsgType == FilterInfo.TYPE_MMS) 1787 { 1788 if(pri == 0x0002) 1789 { 1790 where += " AND " + Mms.PRIORITY + "<=" + 1791 Integer.toString(PduHeaders.PRIORITY_NORMAL); 1792 }else if(pri == 0x0001) { 1793 where += " AND " + Mms.PRIORITY + "=" + 1794 Integer.toString(PduHeaders.PRIORITY_HIGH); 1795 } 1796 } 1797 if(fi.mMsgType == FilterInfo.TYPE_EMAIL || 1798 fi.mMsgType == FilterInfo.TYPE_IM) 1799 { 1800 if(pri == 0x0002) 1801 { 1802 where += " AND " + BluetoothMapContract.MessageColumns.FLAG_HIGH_PRIORITY + "!=1"; 1803 }else if(pri == 0x0001) { 1804 where += " AND " + BluetoothMapContract.MessageColumns.FLAG_HIGH_PRIORITY + "=1"; 1805 } 1806 } 1807 // TODO: no priority filtering in IM 1808 return where; 1809 } 1810 1811 private String setWhereFilterRecipientEmail(BluetoothMapAppParams ap) { 1812 String where = ""; 1813 String recip = ap.getFilterRecipient(); 1814 1815 /* Be aware of wild cards in the beginning of string, may not be valid? */ 1816 if (recip != null && recip.length() > 0) { 1817 recip = recip.replace("*", "%"); 1818 where = " AND (" 1819 + BluetoothMapContract.MessageColumns.TO_LIST + " LIKE '%" + recip + "%' OR " 1820 + BluetoothMapContract.MessageColumns.CC_LIST + " LIKE '%" + recip + "%' OR " 1821 + BluetoothMapContract.MessageColumns.BCC_LIST + " LIKE '%" + recip + "%' )"; 1822 } 1823 return where; 1824 } 1825 1826 private String setWhereFilterMessageHandle(BluetoothMapAppParams ap, FilterInfo fi) { 1827 String where = ""; 1828 long id = -1; 1829 String msgHandle = ap.getFilterMsgHandleString(); 1830 if(msgHandle != null) { 1831 id = BluetoothMapUtils.getCpHandle(msgHandle); 1832 if(D)Log.d(TAG,"id: " + id); 1833 } 1834 if(id != -1) { 1835 if (fi.mMsgType == FilterInfo.TYPE_SMS) { 1836 where = " AND " + Sms._ID + " = " + id; 1837 } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { 1838 where = " AND " + Mms._ID + " = " + id; 1839 } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL || 1840 fi.mMsgType == FilterInfo.TYPE_IM) { 1841 where = " AND " + BluetoothMapContract.MessageColumns._ID + " = " + id; 1842 } 1843 } 1844 return where; 1845 } 1846 1847 private String setWhereFilterThreadId(BluetoothMapAppParams ap, FilterInfo fi) { 1848 String where = ""; 1849 long id = -1; 1850 String msgHandle = ap.getFilterConvoIdString(); 1851 if(msgHandle != null) { 1852 id = BluetoothMapUtils.getMsgHandleAsLong(msgHandle); 1853 if(D)Log.d(TAG,"id: " + id); 1854 } 1855 if(id > 0) { 1856 if (fi.mMsgType == FilterInfo.TYPE_SMS) { 1857 where = " AND " + Sms.THREAD_ID + " = " + id; 1858 } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { 1859 where = " AND " + Mms.THREAD_ID + " = " + id; 1860 } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL || 1861 fi.mMsgType == FilterInfo.TYPE_IM) { 1862 where = " AND " + BluetoothMapContract.MessageColumns.THREAD_ID + " = " + id; 1863 } 1864 } 1865 1866 return where; 1867 } 1868 1869 private String setWhereFilter(BluetoothMapFolderElement folderElement, 1870 FilterInfo fi, BluetoothMapAppParams ap) { 1871 String where = ""; 1872 where += setWhereFilterFolderType(folderElement, fi); 1873 1874 String msgHandleWhere = setWhereFilterMessageHandle(ap, fi); 1875 /* if message handle filter is available, the other filters should be ignored */ 1876 if(msgHandleWhere.isEmpty()) { 1877 where += setWhereFilterReadStatus(ap, fi); 1878 where += setWhereFilterPriority(ap,fi); 1879 where += setWhereFilterPeriod(ap, fi); 1880 if (fi.mMsgType == FilterInfo.TYPE_EMAIL) { 1881 where += setWhereFilterOriginatorEmail(ap); 1882 where += setWhereFilterRecipientEmail(ap); 1883 } 1884 if (fi.mMsgType == FilterInfo.TYPE_IM) { 1885 where += setWhereFilterOriginatorIM(ap); 1886 // TODO: set 'where' filer recipient? 1887 } 1888 where += setWhereFilterThreadId(ap, fi); 1889 } else { 1890 where += msgHandleWhere; 1891 } 1892 1893 return where; 1894 } 1895 1896 1897 /* Used only for SMS/MMS */ 1898 private void setConvoWhereFilterSmsMms(StringBuilder selection, ArrayList<String> selectionArgs, 1899 FilterInfo fi, BluetoothMapAppParams ap) { 1900 1901 if (smsSelected(fi, ap) || mmsSelected(ap)) { 1902 1903 // Filter Read Status 1904 if(ap.getFilterReadStatus() != BluetoothMapAppParams.INVALID_VALUE_PARAMETER) { 1905 if ((ap.getFilterReadStatus() & FILTER_READ_STATUS_UNREAD_ONLY) != 0) { 1906 selection.append(" AND ").append(Threads.READ).append(" = 0"); 1907 } 1908 if ((ap.getFilterReadStatus() & FILTER_READ_STATUS_READ_ONLY) != 0) { 1909 selection.append(" AND ").append(Threads.READ).append(" = 1"); 1910 } 1911 } 1912 1913 // Filter time 1914 if ((ap.getFilterLastActivityBegin() != BluetoothMapAppParams.INVALID_VALUE_PARAMETER)){ 1915 selection.append(" AND ").append(Threads.DATE).append(" >= ") 1916 .append(ap.getFilterLastActivityBegin()); 1917 } 1918 if ((ap.getFilterLastActivityEnd() != BluetoothMapAppParams.INVALID_VALUE_PARAMETER)) { 1919 selection.append(" AND ").append(Threads.DATE).append(" <= ") 1920 .append(ap.getFilterLastActivityEnd()); 1921 } 1922 1923 // Filter ConvoId 1924 long convoId = -1; 1925 if(ap.getFilterConvoId() != null) { 1926 convoId = ap.getFilterConvoId().getLeastSignificantBits(); 1927 } 1928 if(convoId > 0) { 1929 selection.append(" AND ").append(Threads._ID).append(" = ") 1930 .append(Long.toString(convoId)); 1931 } 1932 } 1933 } 1934 1935 1936 1937 /** 1938 * Determine from application parameter if sms should be included. 1939 * The filter mask is set for message types not selected 1940 * @param fi 1941 * @param ap 1942 * @return boolean true if sms is selected, false if not 1943 */ 1944 private boolean smsSelected(FilterInfo fi, BluetoothMapAppParams ap) { 1945 int msgType = ap.getFilterMessageType(); 1946 int phoneType = fi.mPhoneType; 1947 1948 if (D) Log.d(TAG, "smsSelected msgType: " + msgType); 1949 1950 if (msgType == BluetoothMapAppParams.INVALID_VALUE_PARAMETER) 1951 return true; 1952 1953 if ((msgType & (BluetoothMapAppParams.FILTER_NO_SMS_CDMA 1954 |BluetoothMapAppParams.FILTER_NO_SMS_GSM)) == 0) 1955 return true; 1956 1957 if (((msgType & BluetoothMapAppParams.FILTER_NO_SMS_GSM) == 0) 1958 && (phoneType == TelephonyManager.PHONE_TYPE_GSM)) 1959 return true; 1960 1961 if (((msgType & BluetoothMapAppParams.FILTER_NO_SMS_CDMA) == 0) 1962 && (phoneType == TelephonyManager.PHONE_TYPE_CDMA)) 1963 return true; 1964 1965 return false; 1966 } 1967 1968 /** 1969 * Determine from application parameter if mms should be included. 1970 * The filter mask is set for message types not selected 1971 * @param fi 1972 * @param ap 1973 * @return boolean true if mms is selected, false if not 1974 */ 1975 private boolean mmsSelected(BluetoothMapAppParams ap) { 1976 int msgType = ap.getFilterMessageType(); 1977 1978 if (D) Log.d(TAG, "mmsSelected msgType: " + msgType); 1979 1980 if (msgType == BluetoothMapAppParams.INVALID_VALUE_PARAMETER) 1981 return true; 1982 1983 if ((msgType & BluetoothMapAppParams.FILTER_NO_MMS) == 0) 1984 return true; 1985 1986 return false; 1987 } 1988 1989 /** 1990 * Determine from application parameter if email should be included. 1991 * The filter mask is set for message types not selected 1992 * @param fi 1993 * @param ap 1994 * @return boolean true if email is selected, false if not 1995 */ 1996 private boolean emailSelected(BluetoothMapAppParams ap) { 1997 int msgType = ap.getFilterMessageType(); 1998 1999 if (D) Log.d(TAG, "emailSelected msgType: " + msgType); 2000 2001 if (msgType == BluetoothMapAppParams.INVALID_VALUE_PARAMETER) 2002 return true; 2003 2004 if ((msgType & BluetoothMapAppParams.FILTER_NO_EMAIL) == 0) 2005 return true; 2006 2007 return false; 2008 } 2009 2010 /** 2011 * Determine from application parameter if IM should be included. 2012 * The filter mask is set for message types not selected 2013 * @param fi 2014 * @param ap 2015 * @return boolean true if im is selected, false if not 2016 */ 2017 private boolean imSelected(BluetoothMapAppParams ap) { 2018 int msgType = ap.getFilterMessageType(); 2019 2020 if (D) Log.d(TAG, "imSelected msgType: " + msgType); 2021 2022 if (msgType == BluetoothMapAppParams.INVALID_VALUE_PARAMETER) 2023 return true; 2024 2025 if ((msgType & BluetoothMapAppParams.FILTER_NO_IM) == 0) 2026 return true; 2027 2028 return false; 2029 } 2030 2031 private void setFilterInfo(FilterInfo fi) { 2032 TelephonyManager tm = 2033 (TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE); 2034 if (tm != null) { 2035 fi.mPhoneType = tm.getPhoneType(); 2036 fi.mPhoneNum = tm.getLine1Number(); 2037 fi.mPhoneAlphaTag = tm.getLine1AlphaTag(); 2038 if (D) Log.d(TAG, "phone type = " + fi.mPhoneType + 2039 " phone num = " + fi.mPhoneNum + 2040 " phone alpha tag = " + fi.mPhoneAlphaTag); 2041 } 2042 } 2043 2044 /** 2045 * Get a listing of message in folder after applying filter. 2046 * @param folder Must contain a valid folder string != null 2047 * @param ap Parameters specifying message content and filters 2048 * @return Listing object containing requested messages 2049 */ 2050 public BluetoothMapMessageListing msgListing(BluetoothMapFolderElement folderElement, 2051 BluetoothMapAppParams ap) { 2052 if (D) Log.d(TAG, "msgListing: messageType = " + ap.getFilterMessageType() ); 2053 2054 BluetoothMapMessageListing bmList = new BluetoothMapMessageListing(); 2055 2056 /* We overwrite the parameter mask here if it is 0 or not present, as this 2057 * should cause all parameters to be included in the message list. */ 2058 if(ap.getParameterMask() == BluetoothMapAppParams.INVALID_VALUE_PARAMETER || 2059 ap.getParameterMask() == 0) { 2060 ap.setParameterMask(PARAMETER_MASK_ALL_ENABLED); 2061 if (V) Log.v(TAG, "msgListing(): appParameterMask is zero or not present, " + 2062 "changing to all enabled by default: " + ap.getParameterMask()); 2063 } 2064 if (V) Log.v(TAG, "folderElement hasSmsMmsContent = " + folderElement.hasSmsMmsContent() + 2065 " folderElement.hasEmailContent = " + folderElement.hasEmailContent() + 2066 " folderElement.hasImContent = " + folderElement.hasImContent()); 2067 2068 /* Cache some info used throughout filtering */ 2069 FilterInfo fi = new FilterInfo(); 2070 setFilterInfo(fi); 2071 Cursor smsCursor = null; 2072 Cursor mmsCursor = null; 2073 Cursor emailCursor = null; 2074 Cursor imCursor = null; 2075 String limit = ""; 2076 int countNum = ap.getMaxListCount(); 2077 int offsetNum = ap.getStartOffset(); 2078 if(ap.getMaxListCount()>0){ 2079 limit=" LIMIT "+ (ap.getMaxListCount()+ap.getStartOffset()); 2080 } 2081 try{ 2082 if (smsSelected(fi, ap) && folderElement.hasSmsMmsContent()) { 2083 if(ap.getFilterMessageType() == (BluetoothMapAppParams.FILTER_NO_EMAIL| 2084 BluetoothMapAppParams.FILTER_NO_MMS| 2085 BluetoothMapAppParams.FILTER_NO_SMS_GSM| 2086 BluetoothMapAppParams.FILTER_NO_IM)|| 2087 ap.getFilterMessageType() == (BluetoothMapAppParams.FILTER_NO_EMAIL| 2088 BluetoothMapAppParams.FILTER_NO_MMS| 2089 BluetoothMapAppParams.FILTER_NO_SMS_CDMA| 2090 BluetoothMapAppParams.FILTER_NO_IM)){ 2091 //set real limit and offset if only this type is used 2092 // (only if offset/limit is used) 2093 limit = " LIMIT " + ap.getMaxListCount()+" OFFSET "+ ap.getStartOffset(); 2094 if(D) Log.d(TAG, "SMS Limit => "+limit); 2095 offsetNum = 0; 2096 } 2097 fi.mMsgType = FilterInfo.TYPE_SMS; 2098 if(ap.getFilterPriority() != 1){ /*SMS cannot have high priority*/ 2099 String where = setWhereFilter(folderElement, fi, ap); 2100 if (D) Log.d(TAG, "msgType: " + fi.mMsgType + " where: " + where); 2101 smsCursor = mResolver.query(Sms.CONTENT_URI, 2102 SMS_PROJECTION, where, null, Sms.DATE + " DESC" + limit); 2103 if (smsCursor != null) { 2104 BluetoothMapMessageListingElement e = null; 2105 // store column index so we dont have to look them up anymore (optimization) 2106 if(D) Log.d(TAG, "Found " + smsCursor.getCount() + " sms messages."); 2107 fi.setSmsColumns(smsCursor); 2108 while (smsCursor.moveToNext()) { 2109 if (matchAddresses(smsCursor, fi, ap)) { 2110 if(V) BluetoothMapUtils.printCursor(smsCursor); 2111 e = element(smsCursor, fi, ap); 2112 bmList.add(e); 2113 } 2114 } 2115 } 2116 } 2117 } 2118 2119 if (mmsSelected(ap) && folderElement.hasSmsMmsContent()) { 2120 if(ap.getFilterMessageType() == (BluetoothMapAppParams.FILTER_NO_EMAIL| 2121 BluetoothMapAppParams.FILTER_NO_SMS_CDMA| 2122 BluetoothMapAppParams.FILTER_NO_SMS_GSM| 2123 BluetoothMapAppParams.FILTER_NO_IM)){ 2124 //set real limit and offset if only this type is used 2125 //(only if offset/limit is used) 2126 limit = " LIMIT " + ap.getMaxListCount()+" OFFSET "+ ap.getStartOffset(); 2127 if(D) Log.d(TAG, "MMS Limit => "+limit); 2128 offsetNum = 0; 2129 } 2130 fi.mMsgType = FilterInfo.TYPE_MMS; 2131 String where = setWhereFilter(folderElement, fi, ap); 2132 where += " AND " + INTERESTED_MESSAGE_TYPE_CLAUSE; 2133 if(!where.isEmpty()) { 2134 if (D) Log.d(TAG, "msgType: " + fi.mMsgType + " where: " + where); 2135 mmsCursor = mResolver.query(Mms.CONTENT_URI, 2136 MMS_PROJECTION, where, null, Mms.DATE + " DESC" + limit); 2137 if (mmsCursor != null) { 2138 BluetoothMapMessageListingElement e = null; 2139 // store column index so we dont have to look them up anymore (optimization) 2140 fi.setMmsColumns(mmsCursor); 2141 if(D) Log.d(TAG, "Found " + mmsCursor.getCount() + " mms messages."); 2142 while (mmsCursor.moveToNext()) { 2143 if (matchAddresses(mmsCursor, fi, ap)) { 2144 if(V) BluetoothMapUtils.printCursor(mmsCursor); 2145 e = element(mmsCursor, fi, ap); 2146 bmList.add(e); 2147 } 2148 } 2149 } 2150 } 2151 } 2152 2153 if (emailSelected(ap) && folderElement.hasEmailContent()) { 2154 if(ap.getFilterMessageType() == (BluetoothMapAppParams.FILTER_NO_MMS| 2155 BluetoothMapAppParams.FILTER_NO_SMS_CDMA| 2156 BluetoothMapAppParams.FILTER_NO_SMS_GSM| 2157 BluetoothMapAppParams.FILTER_NO_IM)){ 2158 //set real limit and offset if only this type is used 2159 //(only if offset/limit is used) 2160 limit = " LIMIT " + ap.getMaxListCount()+" OFFSET "+ ap.getStartOffset(); 2161 if(D) Log.d(TAG, "Email Limit => "+limit); 2162 offsetNum = 0; 2163 } 2164 fi.mMsgType = FilterInfo.TYPE_EMAIL; 2165 String where = setWhereFilter(folderElement, fi, ap); 2166 2167 if(!where.isEmpty()) { 2168 if (D) Log.d(TAG, "msgType: " + fi.mMsgType + " where: " + where); 2169 Uri contentUri = Uri.parse(mBaseUri + BluetoothMapContract.TABLE_MESSAGE); 2170 emailCursor = mResolver.query(contentUri, 2171 BluetoothMapContract.BT_MESSAGE_PROJECTION, where, null, 2172 BluetoothMapContract.MessageColumns.DATE + " DESC" + limit); 2173 if (emailCursor != null) { 2174 BluetoothMapMessageListingElement e = null; 2175 // store column index so we dont have to look them up anymore (optimization) 2176 fi.setEmailMessageColumns(emailCursor); 2177 int cnt = 0; 2178 if(D) Log.d(TAG, "Found " + emailCursor.getCount() + " email messages."); 2179 while (emailCursor.moveToNext()) { 2180 if(V) BluetoothMapUtils.printCursor(emailCursor); 2181 e = element(emailCursor, fi, ap); 2182 bmList.add(e); 2183 } 2184 // emailCursor.close(); 2185 } 2186 } 2187 } 2188 2189 if (imSelected(ap) && folderElement.hasImContent()) { 2190 if(ap.getFilterMessageType() == (BluetoothMapAppParams.FILTER_NO_MMS| 2191 BluetoothMapAppParams.FILTER_NO_SMS_CDMA| 2192 BluetoothMapAppParams.FILTER_NO_SMS_GSM| 2193 BluetoothMapAppParams.FILTER_NO_EMAIL)){ 2194 //set real limit and offset if only this type is used 2195 //(only if offset/limit is used) 2196 limit = " LIMIT " + ap.getMaxListCount() + " OFFSET "+ ap.getStartOffset(); 2197 if(D) Log.d(TAG, "IM Limit => "+limit); 2198 offsetNum = 0; 2199 } 2200 fi.mMsgType = FilterInfo.TYPE_IM; 2201 String where = setWhereFilter(folderElement, fi, ap); 2202 if (D) Log.d(TAG, "msgType: " + fi.mMsgType + " where: " + where); 2203 2204 Uri contentUri = Uri.parse(mBaseUri + BluetoothMapContract.TABLE_MESSAGE); 2205 imCursor = mResolver.query(contentUri, 2206 BluetoothMapContract.BT_INSTANT_MESSAGE_PROJECTION, 2207 where, null, BluetoothMapContract.MessageColumns.DATE + " DESC" + limit); 2208 if (imCursor != null) { 2209 BluetoothMapMessageListingElement e = null; 2210 // store column index so we dont have to look them up anymore (optimization) 2211 fi.setImMessageColumns(imCursor); 2212 if (D) Log.d(TAG, "Found " + imCursor.getCount() + " im messages."); 2213 while (imCursor.moveToNext()) { 2214 if (V) BluetoothMapUtils.printCursor(imCursor); 2215 e = element(imCursor, fi, ap); 2216 bmList.add(e); 2217 } 2218 } 2219 } 2220 2221 /* Enable this if post sorting and segmenting needed */ 2222 bmList.sort(); 2223 bmList.segment(ap.getMaxListCount(), offsetNum); 2224 List<BluetoothMapMessageListingElement> list = bmList.getList(); 2225 int listSize = list.size(); 2226 Cursor tmpCursor = null; 2227 for(int x=0;x<listSize;x++){ 2228 BluetoothMapMessageListingElement ele = list.get(x); 2229 /* If OBEX "GET" request header includes "ParameterMask" with 'Type' NOT set, 2230 * then ele.getType() returns "null" even for a valid cursor. 2231 * Avoid NullPointerException in equals() check when 'mType' value is "null" */ 2232 TYPE tmpType = ele.getType(); 2233 if (smsCursor!= null && 2234 ((TYPE.SMS_GSM).equals(tmpType) || (TYPE.SMS_CDMA).equals(tmpType))) { 2235 tmpCursor = smsCursor; 2236 fi.mMsgType = FilterInfo.TYPE_SMS; 2237 } else if(mmsCursor != null && (TYPE.MMS).equals(tmpType)) { 2238 tmpCursor = mmsCursor; 2239 fi.mMsgType = FilterInfo.TYPE_MMS; 2240 } else if(emailCursor != null && ((TYPE.EMAIL).equals(tmpType))) { 2241 tmpCursor = emailCursor; 2242 fi.mMsgType = FilterInfo.TYPE_EMAIL; 2243 } else if(imCursor != null && ((TYPE.IM).equals(tmpType))) { 2244 tmpCursor = imCursor; 2245 fi.mMsgType = FilterInfo.TYPE_IM; 2246 } 2247 if(tmpCursor != null){ 2248 tmpCursor.moveToPosition(ele.getCursorIndex()); 2249 setSenderAddressing(ele, tmpCursor, fi, ap); 2250 setSenderName(ele, tmpCursor, fi, ap); 2251 setRecipientAddressing(ele, tmpCursor, fi, ap); 2252 setRecipientName(ele, tmpCursor, fi, ap); 2253 setSubject(ele, tmpCursor, fi, ap); 2254 setSize(ele, tmpCursor, fi, ap); 2255 setText(ele, tmpCursor, fi, ap); 2256 setPriority(ele, tmpCursor, fi, ap); 2257 setSent(ele, tmpCursor, fi, ap); 2258 setProtected(ele, tmpCursor, fi, ap); 2259 setReceptionStatus(ele, tmpCursor, fi, ap); 2260 setAttachment(ele, tmpCursor, fi, ap); 2261 2262 if(mMsgListingVersion > BluetoothMapUtils.MAP_MESSAGE_LISTING_FORMAT_V10 ){ 2263 setDeliveryStatus(ele, tmpCursor, fi, ap); 2264 setThreadId(ele, tmpCursor, fi, ap); 2265 setThreadName(ele, tmpCursor, fi, ap); 2266 setFolderType(ele, tmpCursor, fi, ap); 2267 } 2268 } 2269 } 2270 } finally { 2271 if(emailCursor != null)emailCursor.close(); 2272 if(smsCursor != null)smsCursor.close(); 2273 if(mmsCursor != null)mmsCursor.close(); 2274 if(imCursor != null)imCursor.close(); 2275 } 2276 2277 2278 if(D)Log.d(TAG, "messagelisting end"); 2279 return bmList; 2280 } 2281 2282 /** 2283 * Get the size of the message listing 2284 * @param folder Must contain a valid folder string != null 2285 * @param ap Parameters specifying message content and filters 2286 * @return Integer equal to message listing size 2287 */ 2288 public int msgListingSize(BluetoothMapFolderElement folderElement, 2289 BluetoothMapAppParams ap) { 2290 if (D) Log.d(TAG, "msgListingSize: folder = " + folderElement.getName()); 2291 int cnt = 0; 2292 2293 /* Cache some info used throughout filtering */ 2294 FilterInfo fi = new FilterInfo(); 2295 setFilterInfo(fi); 2296 2297 if (smsSelected(fi, ap) && folderElement.hasSmsMmsContent()) { 2298 fi.mMsgType = FilterInfo.TYPE_SMS; 2299 String where = setWhereFilter(folderElement, fi, ap); 2300 Cursor c = mResolver.query(Sms.CONTENT_URI, 2301 SMS_PROJECTION, where, null, Sms.DATE + " DESC"); 2302 try { 2303 if (c != null) { 2304 cnt = c.getCount(); 2305 } 2306 } finally { 2307 if (c != null) c.close(); 2308 } 2309 } 2310 2311 if (mmsSelected(ap) && folderElement.hasSmsMmsContent()) { 2312 fi.mMsgType = FilterInfo.TYPE_MMS; 2313 String where = setWhereFilter(folderElement, fi, ap); 2314 Cursor c = mResolver.query(Mms.CONTENT_URI, 2315 MMS_PROJECTION, where, null, Mms.DATE + " DESC"); 2316 try { 2317 if (c != null) { 2318 cnt += c.getCount(); 2319 } 2320 } finally { 2321 if (c != null) c.close(); 2322 } 2323 } 2324 2325 if (emailSelected(ap) && folderElement.hasEmailContent()) { 2326 fi.mMsgType = FilterInfo.TYPE_EMAIL; 2327 String where = setWhereFilter(folderElement, fi, ap); 2328 if(!where.isEmpty()) { 2329 Uri contentUri = Uri.parse(mBaseUri + BluetoothMapContract.TABLE_MESSAGE); 2330 Cursor c = mResolver.query(contentUri, BluetoothMapContract.BT_MESSAGE_PROJECTION, 2331 where, null, BluetoothMapContract.MessageColumns.DATE + " DESC"); 2332 try { 2333 if (c != null) { 2334 cnt += c.getCount(); 2335 } 2336 } finally { 2337 if (c != null) c.close(); 2338 } 2339 } 2340 } 2341 2342 if (imSelected(ap) && folderElement.hasImContent()) { 2343 fi.mMsgType = FilterInfo.TYPE_IM; 2344 String where = setWhereFilter(folderElement, fi, ap); 2345 if(!where.isEmpty()) { 2346 Uri contentUri = Uri.parse(mBaseUri + BluetoothMapContract.TABLE_MESSAGE); 2347 Cursor c = mResolver.query(contentUri, 2348 BluetoothMapContract.BT_INSTANT_MESSAGE_PROJECTION, 2349 where, null, BluetoothMapContract.MessageColumns.DATE + " DESC"); 2350 try { 2351 if (c != null) { 2352 cnt += c.getCount(); 2353 } 2354 } finally { 2355 if (c != null) c.close(); 2356 } 2357 } 2358 } 2359 2360 if (D) Log.d(TAG, "msgListingSize: size = " + cnt); 2361 return cnt; 2362 } 2363 2364 /** 2365 * Return true if there are unread messages in the requested list of messages 2366 * @param folder folder where the message listing should come from 2367 * @param ap application parameter object 2368 * @return true if unread messages are in the list, else false 2369 */ 2370 public boolean msgListingHasUnread(BluetoothMapFolderElement folderElement, 2371 BluetoothMapAppParams ap) { 2372 if (D) Log.d(TAG, "msgListingHasUnread: folder = " + folderElement.getName()); 2373 int cnt = 0; 2374 2375 /* Cache some info used throughout filtering */ 2376 FilterInfo fi = new FilterInfo(); 2377 setFilterInfo(fi); 2378 2379 if (smsSelected(fi, ap) && folderElement.hasSmsMmsContent()) { 2380 fi.mMsgType = FilterInfo.TYPE_SMS; 2381 String where = setWhereFilterFolderType(folderElement, fi); 2382 where += " AND " + Sms.READ + "=0 "; 2383 where += setWhereFilterPeriod(ap, fi); 2384 Cursor c = mResolver.query(Sms.CONTENT_URI, 2385 SMS_PROJECTION, where, null, Sms.DATE + " DESC"); 2386 try { 2387 if (c != null) { 2388 cnt = c.getCount(); 2389 } 2390 } finally { 2391 if (c != null) c.close(); 2392 } 2393 } 2394 2395 if (mmsSelected(ap) && folderElement.hasSmsMmsContent()) { 2396 fi.mMsgType = FilterInfo.TYPE_MMS; 2397 String where = setWhereFilterFolderType(folderElement, fi); 2398 where += " AND " + Mms.READ + "=0 "; 2399 where += setWhereFilterPeriod(ap, fi); 2400 Cursor c = mResolver.query(Mms.CONTENT_URI, 2401 MMS_PROJECTION, where, null, Sms.DATE + " DESC"); 2402 try { 2403 if (c != null) { 2404 cnt += c.getCount(); 2405 } 2406 } finally { 2407 if (c != null) c.close(); 2408 } 2409 } 2410 2411 2412 if (emailSelected(ap) && folderElement.getFolderId() != -1) { 2413 fi.mMsgType = FilterInfo.TYPE_EMAIL; 2414 String where = setWhereFilterFolderType(folderElement, fi); 2415 if(!where.isEmpty()) { 2416 where += " AND " + BluetoothMapContract.MessageColumns.FLAG_READ + "=0 "; 2417 where += setWhereFilterPeriod(ap, fi); 2418 Uri contentUri = Uri.parse(mBaseUri + BluetoothMapContract.TABLE_MESSAGE); 2419 Cursor c = mResolver.query(contentUri, BluetoothMapContract.BT_MESSAGE_PROJECTION, 2420 where, null, BluetoothMapContract.MessageColumns.DATE + " DESC"); 2421 try { 2422 if (c != null) { 2423 cnt += c.getCount(); 2424 } 2425 } finally { 2426 if (c != null) c.close(); 2427 } 2428 } 2429 } 2430 2431 if (imSelected(ap) && folderElement.hasImContent()) { 2432 fi.mMsgType = FilterInfo.TYPE_IM; 2433 String where = setWhereFilter(folderElement, fi, ap); 2434 if(!where.isEmpty()) { 2435 where += " AND " + BluetoothMapContract.MessageColumns.FLAG_READ + "=0 "; 2436 where += setWhereFilterPeriod(ap, fi); 2437 Uri contentUri = Uri.parse(mBaseUri + BluetoothMapContract.TABLE_MESSAGE); 2438 Cursor c = mResolver.query(contentUri, 2439 BluetoothMapContract.BT_INSTANT_MESSAGE_PROJECTION, 2440 where, null, BluetoothMapContract.MessageColumns.DATE + " DESC"); 2441 try { 2442 if (c != null) { 2443 cnt += c.getCount(); 2444 } 2445 } finally { 2446 if (c != null) c.close(); 2447 } 2448 } 2449 } 2450 2451 if (D) Log.d(TAG, "msgListingHasUnread: numUnread = " + cnt); 2452 return (cnt>0)?true:false; 2453 } 2454 2455 /** 2456 * Build the conversation listing. 2457 * @param ap The Application Parameters 2458 * @param sizeOnly TRUE: don't populate the list members, only build the list to get the size. 2459 * @return 2460 */ 2461 public BluetoothMapConvoListing convoListing(BluetoothMapAppParams ap, boolean sizeOnly) { 2462 2463 if (D) Log.d(TAG, "convoListing: " + " messageType = " + ap.getFilterMessageType() ); 2464 BluetoothMapConvoListing convoList = new BluetoothMapConvoListing(); 2465 2466 /* We overwrite the parameter mask here if it is 0 or not present, as this 2467 * should cause all parameters to be included in the message list. */ 2468 if(ap.getConvoParameterMask() == BluetoothMapAppParams.INVALID_VALUE_PARAMETER || 2469 ap.getConvoParameterMask() == 0) { 2470 ap.setConvoParameterMask(CONVO_PARAMETER_MASK_DEFAULT); 2471 if (D) Log.v(TAG, "convoListing(): appParameterMask is zero or not present, " + 2472 "changing to default: " + ap.getConvoParameterMask()); 2473 } 2474 2475 /* Possible filters: 2476 * - Recipient name (contacts DB) or id (for SMS/MMS this is the thread-id contact-id) 2477 * - Activity start/begin 2478 * - Read status 2479 * - Thread_id 2480 * The strategy for SMS/MMS 2481 * With no filter on name - use limit and offset. 2482 * With a filter on name - build the complete list of conversations and create a filter 2483 * mechanism 2484 * 2485 * The strategy for IM: 2486 * Join the conversation table with the contacts table in a way that makes it possible to 2487 * get the data needed in a single query. 2488 * Manually handle limit/offset 2489 * */ 2490 2491 /* Cache some info used throughout filtering */ 2492 FilterInfo fi = new FilterInfo(); 2493 setFilterInfo(fi); 2494 Cursor smsMmsCursor = null; 2495 Cursor imEmailCursor = null; 2496 int offsetNum; 2497 if(sizeOnly) { 2498 offsetNum = 0; 2499 } else { 2500 offsetNum = ap.getStartOffset(); 2501 } 2502 // Inverse meaning - hence a 1 is include. 2503 int msgTypesInclude = ((~ap.getFilterMessageType()) 2504 & BluetoothMapAppParams.FILTER_MSG_TYPE_MASK); 2505 int maxThreads = ap.getMaxListCount()+ap.getStartOffset(); 2506 2507 2508 try { 2509 if (smsSelected(fi, ap) || mmsSelected(ap)) { 2510 String limit = ""; 2511 if((sizeOnly == false) && (ap.getMaxListCount()>0) && 2512 (ap.getFilterRecipient()==null)){ 2513 /* We can only use limit if we do not have a contacts filter */ 2514 limit=" LIMIT " + maxThreads; 2515 } 2516 StringBuilder sortOrder = new StringBuilder(Threads.DATE + " DESC"); 2517 if((sizeOnly == false) && 2518 ((msgTypesInclude & ~(BluetoothMapAppParams.FILTER_NO_SMS_GSM | 2519 BluetoothMapAppParams.FILTER_NO_SMS_CDMA) | 2520 BluetoothMapAppParams.FILTER_NO_MMS) == 0) 2521 && ap.getFilterRecipient() == null){ 2522 // SMS/MMS messages only and no recipient filter - use optimization. 2523 limit = " LIMIT " + ap.getMaxListCount()+" OFFSET "+ ap.getStartOffset(); 2524 if(D) Log.d(TAG, "SMS Limit => "+limit); 2525 offsetNum = 0; 2526 } 2527 StringBuilder selection = new StringBuilder(120); // This covers most cases 2528 ArrayList<String> selectionArgs = new ArrayList<String>(12); // Covers all cases 2529 selection.append("1=1 "); // just to simplify building the where-clause 2530 setConvoWhereFilterSmsMms(selection, selectionArgs, fi, ap); 2531 String[] args = null; 2532 if(selectionArgs.size() > 0) { 2533 args = new String[selectionArgs.size()]; 2534 selectionArgs.toArray(args); 2535 } 2536 Uri uri = Threads.CONTENT_URI.buildUpon() 2537 .appendQueryParameter("simple", "true").build(); 2538 sortOrder.append(limit); 2539 if(D) Log.d(TAG, "Query using selection: " + selection.toString() + 2540 " - sortOrder: " + sortOrder.toString()); 2541 // TODO: Optimize: Reduce projection based on convo parameter mask 2542 smsMmsCursor = mResolver.query(uri, MMS_SMS_THREAD_PROJECTION, selection.toString(), 2543 args, sortOrder.toString()); 2544 if (smsMmsCursor != null) { 2545 // store column index so we don't have to look them up anymore (optimization) 2546 if(D) Log.d(TAG, "Found " + smsMmsCursor.getCount() 2547 + " sms/mms conversations."); 2548 BluetoothMapConvoListingElement convoElement = null; 2549 smsMmsCursor.moveToPosition(-1); 2550 if(ap.getFilterRecipient() == null) { 2551 int count = 0; 2552 // We have no Recipient filter, add contacts after the list is reduced 2553 while (smsMmsCursor.moveToNext()) { 2554 convoElement = createConvoElement(smsMmsCursor, fi, ap); 2555 convoList.add(convoElement); 2556 count++; 2557 if(sizeOnly == false && count >= maxThreads) { 2558 break; 2559 } 2560 } 2561 } else { 2562 // We must be able to filter on recipient, add contacts now 2563 SmsMmsContacts contacts = new SmsMmsContacts(); 2564 while (smsMmsCursor.moveToNext()) { 2565 int count = 0; 2566 convoElement = createConvoElement(smsMmsCursor, fi, ap); 2567 String idsStr = 2568 smsMmsCursor.getString(MMS_SMS_THREAD_COL_RECIPIENT_IDS); 2569 // Add elements only if we do find a contact - if not we cannot apply 2570 // the filter, hence the item is irrelevant 2571 // TODO: Perhaps the spec. should be changes to be able to search on 2572 // phone number as well? 2573 if(addSmsMmsContacts(convoElement, contacts, idsStr, 2574 ap.getFilterRecipient(), ap)) { 2575 convoList.add(convoElement); 2576 if(sizeOnly == false && count >= maxThreads) { 2577 break; 2578 } 2579 } 2580 } 2581 } 2582 } 2583 } 2584 2585 if (emailSelected(ap) || imSelected(ap)) { 2586 int count = 0; 2587 if(emailSelected(ap)) { 2588 fi.mMsgType = FilterInfo.TYPE_EMAIL; 2589 } else if(imSelected(ap)) { 2590 fi.mMsgType = FilterInfo.TYPE_IM; 2591 } 2592 if (D) Log.d(TAG, "msgType: " + fi.mMsgType); 2593 Uri contentUri = Uri.parse(mBaseUri + BluetoothMapContract.TABLE_CONVERSATION); 2594 2595 contentUri = appendConvoListQueryParameters(ap, contentUri); 2596 if(V) Log.v(TAG, "URI with parameters: " + contentUri.toString()); 2597 // TODO: Optimize: Reduce projection based on convo parameter mask 2598 imEmailCursor = mResolver.query(contentUri, 2599 BluetoothMapContract.BT_CONVERSATION_PROJECTION, 2600 null, null, BluetoothMapContract.ConversationColumns.LAST_THREAD_ACTIVITY 2601 + " DESC, " + BluetoothMapContract.ConversationColumns.THREAD_ID 2602 + " ASC"); 2603 if (imEmailCursor != null) { 2604 BluetoothMapConvoListingElement e = null; 2605 // store column index so we don't have to look them up anymore (optimization) 2606 // Here we rely on only a single account-based message type for each MAS. 2607 fi.setEmailImConvoColumns(imEmailCursor); 2608 boolean isValid = imEmailCursor.moveToNext(); 2609 if(D) Log.d(TAG, "Found " + imEmailCursor.getCount() 2610 + " EMAIL/IM conversations. isValid = " + isValid); 2611 while (isValid && ((sizeOnly == true) || (count < maxThreads))) { 2612 long threadId = imEmailCursor.getLong(fi.mConvoColConvoId); 2613 long nextThreadId; 2614 count ++; 2615 e = createConvoElement(imEmailCursor, fi, ap); 2616 convoList.add(e); 2617 2618 do { 2619 nextThreadId = imEmailCursor.getLong(fi.mConvoColConvoId); 2620 if(V) Log.i(TAG, " threadId = " + threadId + " newThreadId = " + 2621 nextThreadId); 2622 // TODO: This seems rather inefficient in the case where we do not need 2623 // to reduce the list. 2624 } while ((nextThreadId == threadId) && 2625 (isValid = imEmailCursor.moveToNext() == true)); 2626 } 2627 } 2628 } 2629 2630 if(D) Log.d(TAG, "Done adding conversations - list size:" + 2631 convoList.getCount()); 2632 2633 // If sizeOnly - we are all done here - return the list as is - no need to populate the 2634 // list. 2635 if(sizeOnly) { 2636 return convoList; 2637 } 2638 2639 /* Enable this if post sorting and segmenting needed */ 2640 /* This is too early */ 2641 convoList.sort(); 2642 convoList.segment(ap.getMaxListCount(), offsetNum); 2643 List<BluetoothMapConvoListingElement> list = convoList.getList(); 2644 int listSize = list.size(); 2645 if(V) Log.i(TAG, "List Size:" + listSize); 2646 Cursor tmpCursor = null; 2647 SmsMmsContacts contacts = new SmsMmsContacts(); 2648 for(int x=0;x<listSize;x++){ 2649 BluetoothMapConvoListingElement ele = list.get(x); 2650 TYPE type = ele.getType(); 2651 switch(type) { 2652 case SMS_CDMA: 2653 case SMS_GSM: 2654 case MMS: { 2655 tmpCursor = null; // SMS/MMS needs special treatment 2656 if(smsMmsCursor != null) { 2657 populateSmsMmsConvoElement(ele, smsMmsCursor, ap, contacts); 2658 } 2659 if(D) fi.mMsgType = FilterInfo.TYPE_IM; 2660 break; 2661 } 2662 case EMAIL: 2663 tmpCursor = imEmailCursor; 2664 fi.mMsgType = FilterInfo.TYPE_EMAIL; 2665 break; 2666 case IM: 2667 tmpCursor = imEmailCursor; 2668 fi.mMsgType = FilterInfo.TYPE_IM; 2669 break; 2670 default: 2671 tmpCursor = null; 2672 break; 2673 } 2674 2675 if(D) Log.d(TAG, "Working on cursor of type " + fi.mMsgType); 2676 2677 if(tmpCursor != null){ 2678 populateImEmailConvoElement(ele, tmpCursor, ap, fi); 2679 }else { 2680 // No, it will be for SMS/MMS at the moment 2681 if(D) Log.d(TAG, "tmpCursor is Null - something is wrong - or the message is" + 2682 " of type SMS/MMS"); 2683 } 2684 } 2685 } finally { 2686 if(imEmailCursor != null)imEmailCursor.close(); 2687 if(smsMmsCursor != null)smsMmsCursor.close(); 2688 if(D)Log.d(TAG, "conversation end"); 2689 } 2690 return convoList; 2691 } 2692 2693 2694 /** 2695 * Refreshes the entire list of SMS/MMS conversation version counters. Use it to generate a 2696 * new ConvoListVersinoCounter in mSmsMmsConvoListVersion 2697 * @return 2698 */ 2699 /* package */ 2700 boolean refreshSmsMmsConvoVersions() { 2701 boolean listChangeDetected = false; 2702 Cursor cursor = null; 2703 Uri uri = Threads.CONTENT_URI.buildUpon() 2704 .appendQueryParameter("simple", "true").build(); 2705 cursor = mResolver.query(uri, MMS_SMS_THREAD_PROJECTION, null, 2706 null, Threads.DATE + " DESC"); 2707 try { 2708 if (cursor != null) { 2709 // store column index so we don't have to look them up anymore (optimization) 2710 if(D) Log.d(TAG, "Found " + cursor.getCount() 2711 + " sms/mms conversations."); 2712 BluetoothMapConvoListingElement convoElement = null; 2713 cursor.moveToPosition(-1); 2714 synchronized (getSmsMmsConvoList()) { 2715 int size = Math.max(getSmsMmsConvoList().size(), cursor.getCount()); 2716 HashMap<Long,BluetoothMapConvoListingElement> newList = 2717 new HashMap<Long,BluetoothMapConvoListingElement>(size); 2718 while (cursor.moveToNext()) { 2719 // TODO: Extract to function, that can be called at listing, which returns 2720 // the versionCounter(existing or new). 2721 boolean convoChanged = false; 2722 Long id = cursor.getLong(MMS_SMS_THREAD_COL_ID); 2723 convoElement = getSmsMmsConvoList().remove(id); 2724 if(convoElement == null) { 2725 // New conversation added 2726 convoElement = new BluetoothMapConvoListingElement(); 2727 convoElement.setConvoId(BluetoothMapUtils.CONVO_ID_TYPE_SMS_MMS, id); 2728 listChangeDetected = true; 2729 convoElement.setVersionCounter(0); 2730 } 2731 // Currently we only need to compare name, last_activity and read_status, and 2732 // name is not used for SMS/MMS. 2733 // msg delete will be handled by update folderVersionCounter(). 2734 long last_activity = cursor.getLong(MMS_SMS_THREAD_COL_DATE); 2735 boolean read = (cursor.getInt(MMS_SMS_THREAD_COL_READ) == 1) ? 2736 true : false; 2737 2738 if(last_activity != convoElement.getLastActivity()) { 2739 convoChanged = true; 2740 convoElement.setLastActivity(last_activity); 2741 } 2742 2743 if(read != convoElement.getReadBool()) { 2744 convoChanged = true; 2745 convoElement.setRead(read, false); 2746 } 2747 2748 String idsStr = cursor.getString(MMS_SMS_THREAD_COL_RECIPIENT_IDS); 2749 if(!idsStr.equals(convoElement.getSmsMmsContacts())) { 2750 // This should not trigger a change in conversationVersionCounter only the 2751 // ConvoListVersionCounter. 2752 listChangeDetected = true; 2753 convoElement.setSmsMmsContacts(idsStr); 2754 } 2755 2756 if(convoChanged) { 2757 listChangeDetected = true; 2758 convoElement.incrementVersionCounter(); 2759 } 2760 newList.put(id, convoElement); 2761 } 2762 // If we still have items on the old list, something was deleted 2763 if(getSmsMmsConvoList().size() != 0) { 2764 listChangeDetected = true; 2765 } 2766 setSmsMmsConvoList(newList); 2767 } 2768 2769 if(listChangeDetected) { 2770 mMasInstance.updateSmsMmsConvoListVersionCounter(); 2771 } 2772 } 2773 } finally { 2774 if(cursor != null) { 2775 cursor.close(); 2776 } 2777 } 2778 return listChangeDetected; 2779 } 2780 2781 /** 2782 * Refreshes the entire list of SMS/MMS conversation version counters. Use it to generate a 2783 * new ConvoListVersinoCounter in mSmsMmsConvoListVersion 2784 * @return 2785 */ 2786 /* package */ 2787 boolean refreshImEmailConvoVersions() { 2788 boolean listChangeDetected = false; 2789 FilterInfo fi = new FilterInfo(); 2790 2791 Uri contentUri = Uri.parse(mBaseUri + BluetoothMapContract.TABLE_CONVERSATION); 2792 2793 if(V) Log.v(TAG, "URI with parameters: " + contentUri.toString()); 2794 Cursor imEmailCursor = mResolver.query(contentUri, 2795 CONVO_VERSION_PROJECTION, 2796 null, null, BluetoothMapContract.ConversationColumns.LAST_THREAD_ACTIVITY 2797 + " DESC, " + BluetoothMapContract.ConversationColumns.THREAD_ID 2798 + " ASC"); 2799 try { 2800 if (imEmailCursor != null) { 2801 BluetoothMapConvoListingElement convoElement = null; 2802 // store column index so we don't have to look them up anymore (optimization) 2803 // Here we rely on only a single account-based message type for each MAS. 2804 fi.setEmailImConvoColumns(imEmailCursor); 2805 boolean isValid = imEmailCursor.moveToNext(); 2806 if(V) Log.d(TAG, "Found " + imEmailCursor.getCount() 2807 + " EMAIL/IM conversations. isValid = " + isValid); 2808 synchronized (getImEmailConvoList()) { 2809 int size = Math.max(getImEmailConvoList().size(), imEmailCursor.getCount()); 2810 boolean convoChanged = false; 2811 HashMap<Long,BluetoothMapConvoListingElement> newList = 2812 new HashMap<Long,BluetoothMapConvoListingElement>(size); 2813 while (isValid) { 2814 long id = imEmailCursor.getLong(fi.mConvoColConvoId); 2815 long nextThreadId; 2816 convoElement = getImEmailConvoList().remove(id); 2817 if(convoElement == null) { 2818 // New conversation added 2819 convoElement = new BluetoothMapConvoListingElement(); 2820 convoElement.setConvoId(BluetoothMapUtils.CONVO_ID_TYPE_EMAIL_IM, id); 2821 listChangeDetected = true; 2822 convoElement.setVersionCounter(0); 2823 } 2824 String name = imEmailCursor.getString(fi.mConvoColName); 2825 String summary = imEmailCursor.getString(fi.mConvoColSummary); 2826 long last_activity = imEmailCursor.getLong(fi.mConvoColLastActivity); 2827 boolean read = (imEmailCursor.getInt(fi.mConvoColRead) == 1) ? 2828 true : false; 2829 2830 if(last_activity != convoElement.getLastActivity()) { 2831 convoChanged = true; 2832 convoElement.setLastActivity(last_activity); 2833 } 2834 2835 if(read != convoElement.getReadBool()) { 2836 convoChanged = true; 2837 convoElement.setRead(read, false); 2838 } 2839 2840 if(name != null && !name.equals(convoElement.getName())) { 2841 convoChanged = true; 2842 convoElement.setName(name); 2843 } 2844 2845 if(summary != null && !summary.equals(convoElement.getFullSummary())) { 2846 convoChanged = true; 2847 convoElement.setSummary(summary); 2848 } 2849 /* If the query returned one row for each contact, skip all the dublicates */ 2850 do { 2851 nextThreadId = imEmailCursor.getLong(fi.mConvoColConvoId); 2852 if(V) Log.i(TAG, " threadId = " + id + " newThreadId = " + 2853 nextThreadId); 2854 } while ((nextThreadId == id) && 2855 (isValid = imEmailCursor.moveToNext() == true)); 2856 2857 if(convoChanged) { 2858 listChangeDetected = true; 2859 convoElement.incrementVersionCounter(); 2860 } 2861 newList.put(id, convoElement); 2862 } 2863 // If we still have items on the old list, something was deleted 2864 if(getImEmailConvoList().size() != 0) { 2865 listChangeDetected = true; 2866 } 2867 setImEmailConvoList(newList); 2868 } 2869 } 2870 } finally { 2871 if(imEmailCursor != null) { 2872 imEmailCursor.close(); 2873 } 2874 } 2875 2876 if(listChangeDetected) { 2877 mMasInstance.updateImEmailConvoListVersionCounter(); 2878 } 2879 return listChangeDetected; 2880 } 2881 2882 /** 2883 * Update the convoVersionCounter within the element passed as parameter. 2884 * This function has the side effect to update the ConvoListVersionCounter if needed. 2885 * This function ignores changes to contacts as this shall not change the convoVersionCounter, 2886 * only the convoListVersion counter, which will be updated upon request. 2887 * @param ele Element to update shall not be null. 2888 */ 2889 private void updateSmsMmsConvoVersion(Cursor cursor, BluetoothMapConvoListingElement ele) { 2890 long id = ele.getCpConvoId(); 2891 BluetoothMapConvoListingElement convoElement = getSmsMmsConvoList().get(id); 2892 boolean listChangeDetected = false; 2893 boolean convoChanged = false; 2894 if(convoElement == null) { 2895 // New conversation added 2896 convoElement = new BluetoothMapConvoListingElement(); 2897 getSmsMmsConvoList().put(id, convoElement); 2898 convoElement.setConvoId(BluetoothMapUtils.CONVO_ID_TYPE_SMS_MMS, id); 2899 listChangeDetected = true; 2900 convoElement.setVersionCounter(0); 2901 } 2902 long last_activity = cursor.getLong(MMS_SMS_THREAD_COL_DATE); 2903 boolean read = (cursor.getInt(MMS_SMS_THREAD_COL_READ) == 1) ? 2904 true : false; 2905 2906 if(last_activity != convoElement.getLastActivity()) { 2907 convoChanged = true; 2908 convoElement.setLastActivity(last_activity); 2909 } 2910 2911 if(read != convoElement.getReadBool()) { 2912 convoChanged = true; 2913 convoElement.setRead(read, false); 2914 } 2915 2916 if(convoChanged) { 2917 listChangeDetected = true; 2918 convoElement.incrementVersionCounter(); 2919 } 2920 if(listChangeDetected) { 2921 mMasInstance.updateSmsMmsConvoListVersionCounter(); 2922 } 2923 ele.setVersionCounter(convoElement.getVersionCounter()); 2924 } 2925 2926 /** 2927 * Update the convoVersionCounter within the element passed as parameter. 2928 * This function has the side effect to update the ConvoListVersionCounter if needed. 2929 * This function ignores changes to contacts as this shall not change the convoVersionCounter, 2930 * only the convoListVersion counter, which will be updated upon request. 2931 * @param ele Element to update shall not be null. 2932 */ 2933 private void updateImEmailConvoVersion(Cursor cursor, FilterInfo fi, 2934 BluetoothMapConvoListingElement ele) { 2935 long id = ele.getCpConvoId(); 2936 BluetoothMapConvoListingElement convoElement = getImEmailConvoList().get(id); 2937 boolean listChangeDetected = false; 2938 boolean convoChanged = false; 2939 if(convoElement == null) { 2940 // New conversation added 2941 if(V) Log.d(TAG, "Added new conversation with ID = " + id); 2942 convoElement = new BluetoothMapConvoListingElement(); 2943 convoElement.setConvoId(BluetoothMapUtils.CONVO_ID_TYPE_EMAIL_IM, id); 2944 getImEmailConvoList().put(id, convoElement); 2945 listChangeDetected = true; 2946 convoElement.setVersionCounter(0); 2947 } 2948 String name = cursor.getString(fi.mConvoColName); 2949 long last_activity = cursor.getLong(fi.mConvoColLastActivity); 2950 boolean read = (cursor.getInt(fi.mConvoColRead) == 1) ? 2951 true : false; 2952 2953 if(last_activity != convoElement.getLastActivity()) { 2954 convoChanged = true; 2955 convoElement.setLastActivity(last_activity); 2956 } 2957 2958 if(read != convoElement.getReadBool()) { 2959 convoChanged = true; 2960 convoElement.setRead(read, false); 2961 } 2962 2963 if(name != null && !name.equals(convoElement.getName())) { 2964 convoChanged = true; 2965 convoElement.setName(name); 2966 } 2967 2968 if(convoChanged) { 2969 listChangeDetected = true; 2970 if(V) Log.d(TAG, "conversation with ID = " + id + " changed"); 2971 convoElement.incrementVersionCounter(); 2972 } 2973 if(listChangeDetected) { 2974 mMasInstance.updateImEmailConvoListVersionCounter(); 2975 } 2976 ele.setVersionCounter(convoElement.getVersionCounter()); 2977 } 2978 2979 /** 2980 * @param ele 2981 * @param smsMmsCursor 2982 * @param ap 2983 * @param contacts 2984 */ 2985 private void populateSmsMmsConvoElement(BluetoothMapConvoListingElement ele, 2986 Cursor smsMmsCursor, BluetoothMapAppParams ap, 2987 SmsMmsContacts contacts) { 2988 smsMmsCursor.moveToPosition(ele.getCursorIndex()); 2989 // TODO: If we ever get beyond 31 bit, change to long 2990 int parameterMask = (int) ap.getConvoParameterMask(); // We always set a default value 2991 2992 // TODO: How to determine whether the convo-IDs can be used across message 2993 // types? 2994 ele.setConvoId(BluetoothMapUtils.CONVO_ID_TYPE_SMS_MMS, 2995 smsMmsCursor.getLong(MMS_SMS_THREAD_COL_ID)); 2996 2997 boolean read = (smsMmsCursor.getInt(MMS_SMS_THREAD_COL_READ) == 1) ? 2998 true : false; 2999 if((parameterMask & CONVO_PARAM_MASK_CONVO_READ_STATUS) != 0) { 3000 ele.setRead(read, true); 3001 } else { 3002 ele.setRead(read, false); 3003 } 3004 3005 if((parameterMask & CONVO_PARAM_MASK_CONVO_LAST_ACTIVITY) != 0) { 3006 long timeStamp = smsMmsCursor.getLong(MMS_SMS_THREAD_COL_DATE); 3007 ele.setLastActivity(timeStamp); 3008 } else { 3009 // We need to delete the time stamp, if it was added for multi msg-type 3010 ele.setLastActivity(-1); 3011 } 3012 3013 if((parameterMask & CONVO_PARAM_MASK_CONVO_VERSION_COUNTER) != 0) { 3014 updateSmsMmsConvoVersion(smsMmsCursor, ele); 3015 } 3016 3017 if((parameterMask & CONVO_PARAM_MASK_CONVO_NAME) != 0) { 3018 ele.setName(""); // We never have a thread name for SMS/MMS 3019 } 3020 3021 if((parameterMask & CONVO_PARAM_MASK_CONVO_SUMMARY) != 0) { 3022 String summary = smsMmsCursor.getString(MMS_SMS_THREAD_COL_SNIPPET); 3023 String cs = smsMmsCursor.getString(MMS_SMS_THREAD_COL_SNIPPET_CS); 3024 if(summary != null && cs != null && !cs.equals("UTF-8")) { 3025 try { 3026 // TODO: Not sure this is how to convert to UTF-8 3027 summary = new String(summary.getBytes(cs),"UTF-8"); 3028 } catch (UnsupportedEncodingException e){/*Cannot happen*/} 3029 } 3030 ele.setSummary(summary); 3031 } 3032 3033 if((parameterMask & CONVO_PARAM_MASK_PARTTICIPANTS) != 0) { 3034 if(ap.getFilterRecipient() == null) { 3035 // Add contacts only if not already added 3036 String idsStr = 3037 smsMmsCursor.getString(MMS_SMS_THREAD_COL_RECIPIENT_IDS); 3038 addSmsMmsContacts(ele, contacts, idsStr, null, ap); 3039 } 3040 } 3041 } 3042 3043 /** 3044 * @param ele 3045 * @param tmpCursor 3046 * @param fi 3047 */ 3048 private void populateImEmailConvoElement( BluetoothMapConvoListingElement ele, 3049 Cursor tmpCursor, BluetoothMapAppParams ap, FilterInfo fi) { 3050 tmpCursor.moveToPosition(ele.getCursorIndex()); 3051 // TODO: If we ever get beyond 31 bit, change to long 3052 int parameterMask = (int) ap.getConvoParameterMask(); // We always set a default value 3053 long threadId = tmpCursor.getLong(fi.mConvoColConvoId); 3054 3055 // Mandatory field 3056 ele.setConvoId(BluetoothMapUtils.CONVO_ID_TYPE_EMAIL_IM, threadId); 3057 3058 if((parameterMask & CONVO_PARAM_MASK_CONVO_NAME) != 0) { 3059 ele.setName(tmpCursor.getString(fi.mConvoColName)); 3060 } 3061 3062 boolean reportRead = false; 3063 if((parameterMask & CONVO_PARAM_MASK_CONVO_READ_STATUS) != 0) { 3064 reportRead = true; 3065 } 3066 ele.setRead(((1==tmpCursor.getInt(fi.mConvoColRead))?true:false), reportRead); 3067 3068 long timestamp = tmpCursor.getLong(fi.mConvoColLastActivity); 3069 if((parameterMask & CONVO_PARAM_MASK_CONVO_LAST_ACTIVITY) != 0) { 3070 ele.setLastActivity(timestamp); 3071 } else { 3072 // We need to delete the time stamp, if it was added for multi msg-type 3073 ele.setLastActivity(-1); 3074 } 3075 3076 3077 if((parameterMask & CONVO_PARAM_MASK_CONVO_VERSION_COUNTER) != 0) { 3078 updateImEmailConvoVersion(tmpCursor, fi, ele); 3079 } 3080 if((parameterMask & CONVO_PARAM_MASK_CONVO_SUMMARY) != 0) { 3081 ele.setSummary(tmpCursor.getString(fi.mConvoColSummary)); 3082 } 3083 // TODO: For optimization, we could avoid joining the contact and convo tables 3084 // if we have no filter nor this bit is set. 3085 if((parameterMask & CONVO_PARAM_MASK_PARTTICIPANTS) != 0) { 3086 do { 3087 BluetoothMapConvoContactElement c = new BluetoothMapConvoContactElement(); 3088 if((parameterMask & CONVO_PARAM_MASK_PART_X_BT_UID) != 0) { 3089 c.setBtUid(new SignedLongLong(tmpCursor.getLong(fi.mContactColBtUid),0)); 3090 } 3091 if((parameterMask & CONVO_PARAM_MASK_PART_CHAT_STATE) != 0) { 3092 c.setChatState(tmpCursor.getInt(fi.mContactColChatState)); 3093 } 3094 if((parameterMask & CONVO_PARAM_MASK_PART_PRESENCE) != 0) { 3095 c.setPresenceAvailability(tmpCursor.getInt(fi.mContactColPresenceState)); 3096 } 3097 if((parameterMask & CONVO_PARAM_MASK_PART_PRESENCE_TEXT) != 0) { 3098 c.setPresenceStatus(tmpCursor.getString(fi.mContactColPresenceText)); 3099 } 3100 if((parameterMask & CONVO_PARAM_MASK_PART_PRIORITY) != 0) { 3101 c.setPriority(tmpCursor.getInt(fi.mContactColPriority)); 3102 } 3103 if((parameterMask & CONVO_PARAM_MASK_PART_DISP_NAME) != 0) { 3104 c.setDisplayName(tmpCursor.getString(fi.mContactColNickname)); 3105 } 3106 if((parameterMask & CONVO_PARAM_MASK_PART_UCI) != 0) { 3107 c.setContactId(tmpCursor.getString(fi.mContactColContactUci)); 3108 } 3109 if((parameterMask & CONVO_PARAM_MASK_PART_LAST_ACTIVITY) != 0) { 3110 c.setLastActivity(tmpCursor.getLong(fi.mContactColLastActive)); 3111 } 3112 if((parameterMask & CONVO_PARAM_MASK_PART_NAME) != 0) { 3113 c.setName(tmpCursor.getString(fi.mContactColName)); 3114 } 3115 ele.addContact(c); 3116 } while (tmpCursor.moveToNext() == true 3117 && tmpCursor.getLong(fi.mConvoColConvoId) == threadId); 3118 } 3119 } 3120 3121 /** 3122 * Extract the ConvoList parameters from appParams and build the matching URI with 3123 * query parameters. 3124 * @param ap the appParams from the request 3125 * @param contentUri the URI to append parameters to 3126 * @return the new URI with the appended parameters (if any) 3127 */ 3128 private Uri appendConvoListQueryParameters(BluetoothMapAppParams ap, 3129 Uri contentUri) { 3130 Builder newUri = contentUri.buildUpon(); 3131 String str = ap.getFilterRecipient(); 3132 if(str != null) { 3133 str = str.trim(); 3134 str = str.replace("*", "%"); 3135 newUri.appendQueryParameter(BluetoothMapContract.FILTER_ORIGINATOR_SUBSTRING, str); 3136 } 3137 long time = ap.getFilterLastActivityBegin(); 3138 if(time > 0) { 3139 newUri.appendQueryParameter(BluetoothMapContract.FILTER_PERIOD_BEGIN, 3140 Long.toString(time)); 3141 } 3142 time = ap.getFilterLastActivityEnd(); 3143 if(time > 0) { 3144 newUri.appendQueryParameter(BluetoothMapContract.FILTER_PERIOD_END, 3145 Long.toString(time)); 3146 } 3147 int readStatus = ap.getFilterReadStatus(); 3148 if(readStatus > 0) { 3149 if(readStatus == 1) { 3150 // Conversations with Unread messages only 3151 newUri.appendQueryParameter(BluetoothMapContract.FILTER_READ_STATUS, 3152 "false"); 3153 }else if(readStatus == 2) { 3154 // Conversations with all read messages only 3155 newUri.appendQueryParameter(BluetoothMapContract.FILTER_READ_STATUS, 3156 "true"); 3157 } 3158 // if both are set it will be the same as requesting an empty list, but 3159 // as it makes no sense with such a structure in a bit mask, we treat 3160 // requesting both the same as no filtering. 3161 } 3162 long convoId = -1; 3163 if(ap.getFilterConvoId() != null) { 3164 convoId = ap.getFilterConvoId().getLeastSignificantBits(); 3165 } 3166 if(convoId > 0) { 3167 newUri.appendQueryParameter(BluetoothMapContract.FILTER_THREAD_ID, 3168 Long.toString(convoId)); 3169 } 3170 return newUri.build(); 3171 } 3172 3173 /** 3174 * Procedure if we have a filter: 3175 * - loop through all ids to examine if there is a match (this will build the cache) 3176 * - If there is a match loop again to add all contacts. 3177 * 3178 * Procedure if we don't have a filter 3179 * - Add all contacts 3180 * 3181 * @param convoElement 3182 * @param contacts 3183 * @param idsStr 3184 * @param recipientFilter 3185 * @return 3186 */ 3187 private boolean addSmsMmsContacts( BluetoothMapConvoListingElement convoElement, 3188 SmsMmsContacts contacts, String idsStr, String recipientFilter, 3189 BluetoothMapAppParams ap) { 3190 BluetoothMapConvoContactElement contactElement; 3191 int parameterMask = (int) ap.getConvoParameterMask(); // We always set a default value 3192 boolean foundContact = false; 3193 String[] ids = idsStr.split(" "); 3194 long[] longIds = new long[ids.length]; 3195 if(recipientFilter != null) { 3196 recipientFilter = recipientFilter.trim(); 3197 } 3198 3199 for (int i = 0; i < ids.length; i++) { 3200 long longId; 3201 try { 3202 longId = Long.parseLong(ids[i]); 3203 longIds[i] = longId; 3204 if(recipientFilter == null) { 3205 // If there is not filter, all we need to do is to parse the ids 3206 foundContact = true; 3207 continue; 3208 } 3209 String addr = contacts.getPhoneNumber(mResolver, longId); 3210 if(addr == null) { 3211 // This can only happen if all messages from a contact is deleted while 3212 // performing the query. 3213 continue; 3214 } 3215 MapContact contact = 3216 contacts.getContactNameFromPhone(addr, mResolver, recipientFilter); 3217 if(D) { 3218 Log.d(TAG, " id " + longId + ": " + addr); 3219 if(contact != null) { 3220 Log.d(TAG," contact name: " + contact.getName() + " X-BT-UID: " 3221 + contact.getXBtUid()); 3222 } 3223 } 3224 if(contact == null) { 3225 continue; 3226 } 3227 foundContact = true; 3228 } catch (NumberFormatException ex) { 3229 // skip this id 3230 continue; 3231 } 3232 } 3233 3234 if(foundContact == true) { 3235 foundContact = false; 3236 for (long id : longIds) { 3237 String addr = contacts.getPhoneNumber(mResolver, id); 3238 if(addr == null) { 3239 // This can only happen if all messages from a contact is deleted while 3240 // performing the query. 3241 continue; 3242 } 3243 foundContact = true; 3244 MapContact contact = contacts.getContactNameFromPhone(addr, mResolver); 3245 3246 if(contact == null) { 3247 // We do not have a contact, we need to manually add one 3248 contactElement = new BluetoothMapConvoContactElement(); 3249 if((parameterMask & CONVO_PARAM_MASK_PART_NAME) != 0) { 3250 contactElement.setName(addr); // Use the phone number as name 3251 } 3252 if((parameterMask & CONVO_PARAM_MASK_PART_UCI) != 0) { 3253 contactElement.setContactId(addr); 3254 } 3255 } else { 3256 contactElement = BluetoothMapConvoContactElement 3257 .createFromMapContact(contact, addr); 3258 // Remove the parameters not to be reported 3259 if((parameterMask & CONVO_PARAM_MASK_PART_UCI) == 0) { 3260 contactElement.setContactId(null); 3261 } 3262 if((parameterMask & CONVO_PARAM_MASK_PART_X_BT_UID) == 0) { 3263 contactElement.setBtUid(null); 3264 } 3265 if((parameterMask & CONVO_PARAM_MASK_PART_DISP_NAME) == 0) { 3266 contactElement.setDisplayName(null); 3267 } 3268 } 3269 convoElement.addContact(contactElement); 3270 } 3271 } 3272 return foundContact; 3273 } 3274 3275 /** 3276 * Get the folder name of an SMS message or MMS message. 3277 * @param c the cursor pointing at the message 3278 * @return the folder name. 3279 */ 3280 private String getFolderName(int type, int threadId) { 3281 3282 if(threadId == -1) 3283 return BluetoothMapContract.FOLDER_NAME_DELETED; 3284 3285 switch(type) { 3286 case 1: 3287 return BluetoothMapContract.FOLDER_NAME_INBOX; 3288 case 2: 3289 return BluetoothMapContract.FOLDER_NAME_SENT; 3290 case 3: 3291 return BluetoothMapContract.FOLDER_NAME_DRAFT; 3292 case 4: // Just name outbox, failed and queued "outbox" 3293 case 5: 3294 case 6: 3295 return BluetoothMapContract.FOLDER_NAME_OUTBOX; 3296 } 3297 return ""; 3298 } 3299 3300 public byte[] getMessage(String handle, BluetoothMapAppParams appParams, 3301 BluetoothMapFolderElement folderElement, String version) 3302 throws UnsupportedEncodingException{ 3303 TYPE type = BluetoothMapUtils.getMsgTypeFromHandle(handle); 3304 mMessageVersion = version; 3305 long id = BluetoothMapUtils.getCpHandle(handle); 3306 if(appParams.getFractionRequest() == BluetoothMapAppParams.FRACTION_REQUEST_NEXT) { 3307 throw new IllegalArgumentException("FRACTION_REQUEST_NEXT does not make sence as" + 3308 " we always return the full message."); 3309 } 3310 switch(type) { 3311 case SMS_GSM: 3312 case SMS_CDMA: 3313 return getSmsMessage(id, appParams.getCharset()); 3314 case MMS: 3315 return getMmsMessage(id, appParams); 3316 case EMAIL: 3317 return getEmailMessage(id, appParams, folderElement); 3318 case IM: 3319 return getIMMessage(id, appParams, folderElement); 3320 } 3321 throw new IllegalArgumentException("Invalid message handle."); 3322 } 3323 3324 private String setVCardFromPhoneNumber(BluetoothMapbMessage message, 3325 String phone, boolean incoming) { 3326 String contactId = null, contactName = null; 3327 String[] phoneNumbers = new String[1]; 3328 //Handle possible exception for empty phone address 3329 if (TextUtils.isEmpty(phone)) { 3330 return contactName; 3331 } 3332 // 3333 // Use only actual phone number, because the MCE cannot know which 3334 // number the message is from. 3335 // 3336 phoneNumbers[0] = phone; 3337 String[] emailAddresses = null; 3338 Cursor p; 3339 3340 Uri uri = Uri 3341 .withAppendedPath(PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI, 3342 Uri.encode(phone)); 3343 3344 String[] projection = {Contacts._ID, Contacts.DISPLAY_NAME}; 3345 String selection = Contacts.IN_VISIBLE_GROUP + "=1"; 3346 String orderBy = Contacts._ID + " ASC"; 3347 3348 // Get the contact _ID and name 3349 p = mResolver.query(uri, projection, selection, null, orderBy); 3350 try { 3351 if (p != null && p.moveToFirst()) { 3352 contactId = p.getString(p.getColumnIndex(Contacts._ID)); 3353 contactName = p.getString(p.getColumnIndex(Contacts.DISPLAY_NAME)); 3354 } 3355 } finally { 3356 close(p); 3357 } 3358 // Bail out if we are unable to find a contact, based on the phone number 3359 if (contactId != null) { 3360 Cursor q = null; 3361 // Fetch the contact e-mail addresses 3362 try { 3363 q = mResolver.query(ContactsContract.CommonDataKinds.Email.CONTENT_URI, null, 3364 ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?", 3365 new String[]{contactId}, 3366 null); 3367 if (q != null && q.moveToFirst()) { 3368 int i = 0; 3369 emailAddresses = new String[q.getCount()]; 3370 do { 3371 String emailAddress = q.getString(q.getColumnIndex( 3372 ContactsContract.CommonDataKinds.Email.ADDRESS)); 3373 emailAddresses[i++] = emailAddress; 3374 } while (q != null && q.moveToNext()); 3375 } 3376 } finally { 3377 close(q); 3378 } 3379 } 3380 3381 if (incoming == true) { 3382 if(V) Log.d(TAG, "Adding originator for phone:" + phone); 3383 // Use version 3.0 as we only have a formatted name 3384 message.addOriginator(contactName, contactName, phoneNumbers, emailAddresses,null,null); 3385 } else { 3386 if(V) Log.d(TAG, "Adding recipient for phone:" + phone); 3387 // Use version 3.0 as we only have a formatted name 3388 message.addRecipient(contactName, contactName, phoneNumbers, emailAddresses,null,null); 3389 } 3390 return contactName; 3391 } 3392 3393 public static final int MAP_MESSAGE_CHARSET_NATIVE = 0; 3394 public static final int MAP_MESSAGE_CHARSET_UTF8 = 1; 3395 3396 public byte[] getSmsMessage(long id, int charset) throws UnsupportedEncodingException{ 3397 int type, threadId; 3398 long time = -1; 3399 String msgBody; 3400 BluetoothMapbMessageSms message = new BluetoothMapbMessageSms(); 3401 TelephonyManager tm = (TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE); 3402 3403 Cursor c = mResolver.query(Sms.CONTENT_URI, SMS_PROJECTION, "_ID = " + id, null, null); 3404 if (c == null || !c.moveToFirst()) { 3405 throw new IllegalArgumentException("SMS handle not found"); 3406 } 3407 3408 try{ 3409 if(c != null && c.moveToFirst()) 3410 { 3411 if(V) Log.v(TAG,"c.count: " + c.getCount()); 3412 3413 if (tm.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM) { 3414 message.setType(TYPE.SMS_GSM); 3415 } else if (tm.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) { 3416 message.setType(TYPE.SMS_CDMA); 3417 } 3418 message.setVersionString(mMessageVersion); 3419 String read = c.getString(c.getColumnIndex(Sms.READ)); 3420 if (read.equalsIgnoreCase("1")) 3421 message.setStatus(true); 3422 else 3423 message.setStatus(false); 3424 3425 type = c.getInt(c.getColumnIndex(Sms.TYPE)); 3426 threadId = c.getInt(c.getColumnIndex(Sms.THREAD_ID)); 3427 message.setFolder(getFolderName(type, threadId)); 3428 3429 msgBody = c.getString(c.getColumnIndex(Sms.BODY)); 3430 3431 String phone = c.getString(c.getColumnIndex(Sms.ADDRESS)); 3432 if ((phone == null) && type == Sms.MESSAGE_TYPE_DRAFT) { 3433 //Fetch address for Drafts folder from "canonical_address" table 3434 phone = getCanonicalAddressSms(mResolver, threadId); 3435 } 3436 time = c.getLong(c.getColumnIndex(Sms.DATE)); 3437 if(type == 1) // Inbox message needs to set the vCard as originator 3438 setVCardFromPhoneNumber(message, phone, true); 3439 else // Other messages sets the vCard as the recipient 3440 setVCardFromPhoneNumber(message, phone, false); 3441 3442 if(charset == MAP_MESSAGE_CHARSET_NATIVE) { 3443 if(type == 1) //Inbox 3444 message.setSmsBodyPdus(BluetoothMapSmsPdu.getDeliverPdus(msgBody, 3445 phone, time)); 3446 else 3447 message.setSmsBodyPdus(BluetoothMapSmsPdu.getSubmitPdus(msgBody, phone)); 3448 } else /*if (charset == MAP_MESSAGE_CHARSET_UTF8)*/ { 3449 message.setSmsBody(msgBody); 3450 } 3451 return message.encode(); 3452 } 3453 } finally { 3454 if (c != null) c.close(); 3455 } 3456 3457 return message.encode(); 3458 } 3459 3460 private void extractMmsAddresses(long id, BluetoothMapbMessageMime message) { 3461 final String[] projection = null; 3462 String selection = new String(Mms.Addr.MSG_ID + "=" + id); 3463 String uriStr = new String(Mms.CONTENT_URI + "/" + id + "/addr"); 3464 Uri uriAddress = Uri.parse(uriStr); 3465 String contactName = null; 3466 3467 Cursor c = mResolver.query( uriAddress, projection, selection, null, null); 3468 try { 3469 if (c.moveToFirst()) { 3470 do { 3471 String address = c.getString(c.getColumnIndex(Mms.Addr.ADDRESS)); 3472 if(address.equals(INSERT_ADDRES_TOKEN)) 3473 continue; 3474 Integer type = c.getInt(c.getColumnIndex(Mms.Addr.TYPE)); 3475 switch(type) { 3476 case MMS_FROM: 3477 contactName = setVCardFromPhoneNumber(message, address, true); 3478 message.addFrom(contactName, address); 3479 break; 3480 case MMS_TO: 3481 contactName = setVCardFromPhoneNumber(message, address, false); 3482 message.addTo(contactName, address); 3483 break; 3484 case MMS_CC: 3485 contactName = setVCardFromPhoneNumber(message, address, false); 3486 message.addCc(contactName, address); 3487 break; 3488 case MMS_BCC: 3489 contactName = setVCardFromPhoneNumber(message, address, false); 3490 message.addBcc(contactName, address); 3491 break; 3492 default: 3493 break; 3494 } 3495 } while(c.moveToNext()); 3496 } 3497 } finally { 3498 if (c != null) c.close(); 3499 } 3500 } 3501 3502 3503 /** 3504 * Read out a mime data part and return the data in a byte array. 3505 * @param contentPartUri TODO 3506 * @param partid the content provider id of the Mime Part. 3507 * @return 3508 */ 3509 private byte[] readRawDataPart(Uri contentPartUri, long partid) { 3510 String uriStr = new String(contentPartUri+"/"+ partid); 3511 Uri uriAddress = Uri.parse(uriStr); 3512 InputStream is = null; 3513 ByteArrayOutputStream os = new ByteArrayOutputStream(); 3514 int bufferSize = 8192; 3515 byte[] buffer = new byte[bufferSize]; 3516 byte[] retVal = null; 3517 3518 try { 3519 is = mResolver.openInputStream(uriAddress); 3520 int len = 0; 3521 while ((len = is.read(buffer)) != -1) { 3522 os.write(buffer, 0, len); // We need to specify the len, as it can be != bufferSize 3523 } 3524 retVal = os.toByteArray(); 3525 } catch (IOException e) { 3526 // do nothing for now 3527 Log.w(TAG,"Error reading part data",e); 3528 } finally { 3529 close(os); 3530 close(is); 3531 } 3532 return retVal; 3533 } 3534 3535 /** 3536 * Read out the mms parts and update the bMessage object provided i {@linkplain message} 3537 * @param id the content provider ID of the message 3538 * @param message the bMessage object to add the information to 3539 */ 3540 private void extractMmsParts(long id, BluetoothMapbMessageMime message) 3541 { 3542 /* Handling of filtering out non-text parts for exclude 3543 * attachments is handled within the bMessage object. */ 3544 final String[] projection = null; 3545 String selection = new String(Mms.Part.MSG_ID + "=" + id); 3546 String uriStr = new String(Mms.CONTENT_URI + "/"+ id + "/part"); 3547 Uri uriAddress = Uri.parse(uriStr); 3548 BluetoothMapbMessageMime.MimePart part; 3549 Cursor c = mResolver.query(uriAddress, projection, selection, null, null); 3550 try { 3551 if (c.moveToFirst()) { 3552 do { 3553 Long partId = c.getLong(c.getColumnIndex(BaseColumns._ID)); 3554 String contentType = c.getString(c.getColumnIndex(Mms.Part.CONTENT_TYPE)); 3555 String name = c.getString(c.getColumnIndex(Mms.Part.NAME)); 3556 String charset = c.getString(c.getColumnIndex(Mms.Part.CHARSET)); 3557 String filename = c.getString(c.getColumnIndex(Mms.Part.FILENAME)); 3558 String text = c.getString(c.getColumnIndex(Mms.Part.TEXT)); 3559 Integer fd = c.getInt(c.getColumnIndex(Mms.Part._DATA)); 3560 String cid = c.getString(c.getColumnIndex(Mms.Part.CONTENT_ID)); 3561 String cl = c.getString(c.getColumnIndex(Mms.Part.CONTENT_LOCATION)); 3562 String cdisp = c.getString(c.getColumnIndex(Mms.Part.CONTENT_DISPOSITION)); 3563 3564 if(V) Log.d(TAG, " _id : " + partId + 3565 "\n ct : " + contentType + 3566 "\n partname : " + name + 3567 "\n charset : " + charset + 3568 "\n filename : " + filename + 3569 "\n text : " + text + 3570 "\n fd : " + fd + 3571 "\n cid : " + cid + 3572 "\n cl : " + cl + 3573 "\n cdisp : " + cdisp); 3574 3575 part = message.addMimePart(); 3576 part.mContentType = contentType; 3577 part.mPartName = name; 3578 part.mContentId = cid; 3579 part.mContentLocation = cl; 3580 part.mContentDisposition = cdisp; 3581 3582 try { 3583 if(text != null) { 3584 part.mData = text.getBytes("UTF-8"); 3585 part.mCharsetName = "utf-8"; 3586 } else { 3587 part.mData = 3588 readRawDataPart(Uri.parse(Mms.CONTENT_URI+"/part"), partId); 3589 if(charset != null) { 3590 part.mCharsetName = 3591 CharacterSets.getMimeName(Integer.parseInt(charset)); 3592 } 3593 } 3594 } catch (NumberFormatException e) { 3595 Log.d(TAG,"extractMmsParts",e); 3596 part.mData = null; 3597 part.mCharsetName = null; 3598 } catch (UnsupportedEncodingException e) { 3599 Log.d(TAG,"extractMmsParts",e); 3600 part.mData = null; 3601 part.mCharsetName = null; 3602 } finally { 3603 } 3604 part.mFileName = filename; 3605 } while(c.moveToNext()); 3606 message.updateCharset(); 3607 } 3608 3609 } finally { 3610 if(c != null) c.close(); 3611 } 3612 } 3613 /** 3614 * Read out the mms parts and update the bMessage object provided i {@linkplain message} 3615 * @param id the content provider ID of the message 3616 * @param message the bMessage object to add the information to 3617 */ 3618 private void extractIMParts(long id, BluetoothMapbMessageMime message) 3619 { 3620 /* Handling of filtering out non-text parts for exclude 3621 * attachments is handled within the bMessage object. */ 3622 final String[] projection = null; 3623 String selection = new String(BluetoothMapContract.MessageColumns._ID + "=" + id); 3624 String uriStr = new String(mBaseUri 3625 + BluetoothMapContract.TABLE_MESSAGE + "/"+ id + "/part"); 3626 Uri uriAddress = Uri.parse(uriStr); 3627 BluetoothMapbMessageMime.MimePart part; 3628 Cursor c = mResolver.query(uriAddress, projection, selection, null, null); 3629 try{ 3630 if (c.moveToFirst()) { 3631 do { 3632 Long partId = c.getLong( 3633 c.getColumnIndex(BluetoothMapContract.MessagePartColumns._ID)); 3634 String charset = c.getString( 3635 c.getColumnIndex(BluetoothMapContract.MessagePartColumns.CHARSET)); 3636 String filename = c.getString( 3637 c.getColumnIndex(BluetoothMapContract.MessagePartColumns.FILENAME)); 3638 String text = c.getString( 3639 c.getColumnIndex(BluetoothMapContract.MessagePartColumns.TEXT)); 3640 String body = c.getString( 3641 c.getColumnIndex(BluetoothMapContract.MessagePartColumns.RAW_DATA)); 3642 String cid = c.getString( 3643 c.getColumnIndex(BluetoothMapContract.MessagePartColumns.CONTENT_ID)); 3644 3645 if(V) Log.d(TAG, " _id : " + partId + 3646 "\n charset : " + charset + 3647 "\n filename : " + filename + 3648 "\n text : " + text + 3649 "\n cid : " + cid); 3650 3651 part = message.addMimePart(); 3652 part.mContentId = cid; 3653 try { 3654 if(text.equalsIgnoreCase("yes")) { 3655 part.mData = body.getBytes("UTF-8"); 3656 part.mCharsetName = "utf-8"; 3657 } else { 3658 part.mData = readRawDataPart(Uri.parse(mBaseUri 3659 + BluetoothMapContract.TABLE_MESSAGE_PART) , partId); 3660 if(charset != null) 3661 part.mCharsetName = CharacterSets.getMimeName( 3662 Integer.parseInt(charset)); 3663 } 3664 } catch (NumberFormatException e) { 3665 Log.d(TAG,"extractIMParts",e); 3666 part.mData = null; 3667 part.mCharsetName = null; 3668 } catch (UnsupportedEncodingException e) { 3669 Log.d(TAG,"extractIMParts",e); 3670 part.mData = null; 3671 part.mCharsetName = null; 3672 } finally { 3673 } 3674 part.mFileName = filename; 3675 } while(c.moveToNext()); 3676 } 3677 } finally { 3678 if(c != null) c.close(); 3679 } 3680 3681 message.updateCharset(); 3682 } 3683 3684 /** 3685 * 3686 * @param id the content provider id for the message to fetch. 3687 * @param appParams The application parameter object received from the client. 3688 * @return a byte[] containing the utf-8 encoded bMessage to send to the client. 3689 * @throws UnsupportedEncodingException if UTF-8 is not supported, 3690 * which is guaranteed to be supported on an android device 3691 */ 3692 public byte[] getMmsMessage(long id,BluetoothMapAppParams appParams) 3693 throws UnsupportedEncodingException { 3694 int msgBox, threadId; 3695 if (appParams.getCharset() == MAP_MESSAGE_CHARSET_NATIVE) 3696 throw new IllegalArgumentException("MMS charset native not allowed for MMS" 3697 +" - must be utf-8"); 3698 3699 BluetoothMapbMessageMime message = new BluetoothMapbMessageMime(); 3700 Cursor c = mResolver.query(Mms.CONTENT_URI, MMS_PROJECTION, "_ID = " + id, null, null); 3701 try { 3702 if(c != null && c.moveToFirst()) 3703 { 3704 message.setType(TYPE.MMS); 3705 message.setVersionString(mMessageVersion); 3706 3707 // The MMS info: 3708 String read = c.getString(c.getColumnIndex(Mms.READ)); 3709 if (read.equalsIgnoreCase("1")) 3710 message.setStatus(true); 3711 else 3712 message.setStatus(false); 3713 3714 msgBox = c.getInt(c.getColumnIndex(Mms.MESSAGE_BOX)); 3715 threadId = c.getInt(c.getColumnIndex(Mms.THREAD_ID)); 3716 message.setFolder(getFolderName(msgBox, threadId)); 3717 message.setSubject(c.getString(c.getColumnIndex(Mms.SUBJECT))); 3718 message.setMessageId(c.getString(c.getColumnIndex(Mms.MESSAGE_ID))); 3719 message.setContentType(c.getString(c.getColumnIndex(Mms.CONTENT_TYPE))); 3720 message.setDate(c.getLong(c.getColumnIndex(Mms.DATE)) * 1000L); 3721 message.setTextOnly(c.getInt(c.getColumnIndex(Mms.TEXT_ONLY)) == 0 ? false : true); 3722 message.setIncludeAttachments(appParams.getAttachment() == 0 ? false : true); 3723 // c.getLong(c.getColumnIndex(Mms.DATE_SENT)); - this is never used 3724 // c.getInt(c.getColumnIndex(Mms.STATUS)); - don't know what this is 3725 3726 // The parts 3727 extractMmsParts(id, message); 3728 3729 // The addresses 3730 extractMmsAddresses(id, message); 3731 3732 3733 return message.encode(); 3734 } 3735 } finally { 3736 if (c != null) c.close(); 3737 } 3738 3739 return message.encode(); 3740 } 3741 3742 /** 3743 * 3744 * @param id the content provider id for the message to fetch. 3745 * @param appParams The application parameter object received from the client. 3746 * @return a byte[] containing the utf-8 encoded bMessage to send to the client. 3747 * @throws UnsupportedEncodingException if UTF-8 is not supported, 3748 * which is guaranteed to be supported on an android device 3749 */ 3750 public byte[] getEmailMessage(long id, BluetoothMapAppParams appParams, 3751 BluetoothMapFolderElement currentFolder) throws UnsupportedEncodingException { 3752 // Log print out of application parameters set 3753 if(D && appParams != null) { 3754 Log.d(TAG,"TYPE_MESSAGE (GET): Attachment = " + appParams.getAttachment() + 3755 ", Charset = " + appParams.getCharset() + 3756 ", FractionRequest = " + appParams.getFractionRequest()); 3757 } 3758 3759 // Throw exception if requester NATIVE charset for Email 3760 // Exception is caught by MapObexServer sendGetMessageResp 3761 if (appParams.getCharset() == MAP_MESSAGE_CHARSET_NATIVE) 3762 throw new IllegalArgumentException("EMAIL charset not UTF-8"); 3763 3764 BluetoothMapbMessageEmail message = new BluetoothMapbMessageEmail(); 3765 Uri contentUri = Uri.parse(mBaseUri + BluetoothMapContract.TABLE_MESSAGE); 3766 Cursor c = mResolver.query(contentUri, BluetoothMapContract.BT_MESSAGE_PROJECTION, "_ID = " 3767 + id, null, null); 3768 try { 3769 if(c != null && c.moveToFirst()) 3770 { 3771 BluetoothMapFolderElement folderElement; 3772 FileInputStream is = null; 3773 ParcelFileDescriptor fd = null; 3774 try { 3775 // Handle fraction requests 3776 int fractionRequest = appParams.getFractionRequest(); 3777 if (fractionRequest != BluetoothMapAppParams.INVALID_VALUE_PARAMETER) { 3778 // Fraction requested 3779 if(V) { 3780 String fractionStr = (fractionRequest == 0) ? "FIRST" : "NEXT"; 3781 Log.v(TAG, "getEmailMessage - FractionRequest " + fractionStr 3782 + " - send compete message" ); 3783 } 3784 // Check if message is complete and if not - request message from server 3785 if (c.getString(c.getColumnIndex( 3786 BluetoothMapContract.MessageColumns.RECEPTION_STATE)).equalsIgnoreCase( 3787 BluetoothMapContract.RECEPTION_STATE_COMPLETE) == false) { 3788 // TODO: request message from server 3789 Log.w(TAG, "getEmailMessage - receptionState not COMPLETE - Not Implemented!" ); 3790 } 3791 } 3792 // Set read status: 3793 String read = c.getString( 3794 c.getColumnIndex(BluetoothMapContract.MessageColumns.FLAG_READ)); 3795 if (read != null && read.equalsIgnoreCase("1")) 3796 message.setStatus(true); 3797 else 3798 message.setStatus(false); 3799 3800 // Set message type: 3801 message.setType(TYPE.EMAIL); 3802 message.setVersionString(mMessageVersion); 3803 // Set folder: 3804 long folderId = c.getLong( 3805 c.getColumnIndex(BluetoothMapContract.MessageColumns.FOLDER_ID)); 3806 folderElement = currentFolder.getFolderById(folderId); 3807 message.setCompleteFolder(folderElement.getFullPath()); 3808 3809 // Set recipient: 3810 String nameEmail = c.getString( 3811 c.getColumnIndex(BluetoothMapContract.MessageColumns.TO_LIST)); 3812 Rfc822Token tokens[] = Rfc822Tokenizer.tokenize(nameEmail); 3813 if (tokens.length != 0) { 3814 if(D) Log.d(TAG, "Recipient count= " + tokens.length); 3815 int i = 0; 3816 while (i < tokens.length) { 3817 if(V) Log.d(TAG, "Recipient = " + tokens[i].toString()); 3818 String[] emails = new String[1]; 3819 emails[0] = tokens[i].getAddress(); 3820 String name = tokens[i].getName(); 3821 message.addRecipient(name, name, null, emails, null, null); 3822 i++; 3823 } 3824 } 3825 3826 // Set originator: 3827 nameEmail = c.getString(c.getColumnIndex(BluetoothMapContract.MessageColumns.FROM_LIST)); 3828 tokens = Rfc822Tokenizer.tokenize(nameEmail); 3829 if (tokens.length != 0) { 3830 if(D) Log.d(TAG, "Originator count= " + tokens.length); 3831 int i = 0; 3832 while (i < tokens.length) { 3833 if(V) Log.d(TAG, "Originator = " + tokens[i].toString()); 3834 String[] emails = new String[1]; 3835 emails[0] = tokens[i].getAddress(); 3836 String name = tokens[i].getName(); 3837 message.addOriginator(name, name, null, emails, null, null); 3838 i++; 3839 } 3840 } 3841 } finally { 3842 if(c != null) c.close(); 3843 } 3844 // Find out if we get attachments 3845 String attStr = (appParams.getAttachment() == 0) ? 3846 "/" + BluetoothMapContract.FILE_MSG_NO_ATTACHMENTS : ""; 3847 Uri uri = Uri.parse(contentUri + "/" + id + attStr); 3848 3849 // Get email message body content 3850 int count = 0; 3851 try { 3852 fd = mResolver.openFileDescriptor(uri, "r"); 3853 is = new FileInputStream(fd.getFileDescriptor()); 3854 StringBuilder email = new StringBuilder(""); 3855 byte[] buffer = new byte[1024]; 3856 while((count = is.read(buffer)) != -1) { 3857 // TODO: Handle breaks within a UTF8 character 3858 email.append(new String(buffer,0,count)); 3859 if(V) Log.d(TAG, "Email part = " 3860 + new String(buffer,0,count) 3861 + " count=" + count); 3862 } 3863 // Set email message body: 3864 message.setEmailBody(email.toString()); 3865 } catch (FileNotFoundException e) { 3866 Log.w(TAG, e); 3867 } catch (NullPointerException e) { 3868 Log.w(TAG, e); 3869 } catch (IOException e) { 3870 Log.w(TAG, e); 3871 } finally { 3872 try { 3873 if(is != null) is.close(); 3874 } catch (IOException e) {} 3875 try { 3876 if(fd != null) fd.close(); 3877 } catch (IOException e) {} 3878 } 3879 return message.encode(); 3880 } 3881 } finally { 3882 if (c != null) c.close(); 3883 } 3884 throw new IllegalArgumentException("EMAIL handle not found"); 3885 } 3886 /** 3887 * 3888 * @param id the content provider id for the message to fetch. 3889 * @param appParams The application parameter object received from the client. 3890 * @return a byte[] containing the UTF-8 encoded bMessage to send to the client. 3891 * @throws UnsupportedEncodingException if UTF-8 is not supported, 3892 * which is guaranteed to be supported on an android device 3893 */ 3894 3895 /** 3896 * 3897 * @param id the content provider id for the message to fetch. 3898 * @param appParams The application parameter object received from the client. 3899 * @return a byte[] containing the utf-8 encoded bMessage to send to the client. 3900 * @throws UnsupportedEncodingException if UTF-8 is not supported, 3901 * which is guaranteed to be supported on an android device 3902 */ 3903 public byte[] getIMMessage(long id, 3904 BluetoothMapAppParams appParams, 3905 BluetoothMapFolderElement folderElement) 3906 throws UnsupportedEncodingException { 3907 long threadId, folderId; 3908 3909 if (appParams.getCharset() == MAP_MESSAGE_CHARSET_NATIVE) 3910 throw new IllegalArgumentException( 3911 "IM charset native not allowed for IM - must be utf-8"); 3912 3913 BluetoothMapbMessageMime message = new BluetoothMapbMessageMime(); 3914 Uri contentUri = Uri.parse(mBaseUri + BluetoothMapContract.TABLE_MESSAGE); 3915 Cursor c = mResolver.query(contentUri, 3916 BluetoothMapContract.BT_MESSAGE_PROJECTION, "_ID = " + id, null, null); 3917 Cursor contacts = null; 3918 try { 3919 if(c != null && c.moveToFirst()) { 3920 message.setType(TYPE.IM); 3921 message.setVersionString(mMessageVersion); 3922 3923 // The IM message info: 3924 int read = 3925 c.getInt(c.getColumnIndex(BluetoothMapContract.MessageColumns.FLAG_READ)); 3926 if (read == 1) 3927 message.setStatus(true); 3928 else 3929 message.setStatus(false); 3930 3931 threadId = 3932 c.getInt(c.getColumnIndex(BluetoothMapContract.MessageColumns.THREAD_ID)); 3933 folderId = 3934 c.getLong(c.getColumnIndex(BluetoothMapContract.MessageColumns.FOLDER_ID)); 3935 folderElement = folderElement.getFolderById(folderId); 3936 message.setCompleteFolder(folderElement.getFullPath()); 3937 message.setSubject(c.getString( 3938 c.getColumnIndex(BluetoothMapContract.MessageColumns.SUBJECT))); 3939 message.setMessageId(c.getString( 3940 c.getColumnIndex(BluetoothMapContract.MessageColumns._ID))); 3941 message.setDate(c.getLong( 3942 c.getColumnIndex(BluetoothMapContract.MessageColumns.DATE))); 3943 message.setTextOnly(c.getInt(c.getColumnIndex( 3944 BluetoothMapContract.MessageColumns.ATTACHMENT_SIZE)) != 0 ? false : true); 3945 3946 message.setIncludeAttachments(appParams.getAttachment() == 0 ? false : true); 3947 3948 // c.getLong(c.getColumnIndex(Mms.DATE_SENT)); - this is never used 3949 // c.getInt(c.getColumnIndex(Mms.STATUS)); - don't know what this is 3950 3951 // The parts 3952 3953 //FIXME use the parts when ready - until then use the body column for text-only 3954 // extractIMParts(id, message); 3955 //FIXME next few lines are temporary code 3956 MimePart part = message.addMimePart(); 3957 part.mData = c.getString((c.getColumnIndex( 3958 BluetoothMapContract.MessageColumns.BODY))).getBytes("UTF-8"); 3959 part.mCharsetName = "utf-8"; 3960 part.mContentId = "0"; 3961 part.mContentType = "text/plain"; 3962 message.updateCharset(); 3963 // FIXME end temp code 3964 3965 Uri contactsUri = Uri.parse(mBaseUri + BluetoothMapContract.TABLE_CONVOCONTACT); 3966 contacts = mResolver.query(contactsUri, 3967 BluetoothMapContract.BT_CONTACT_PROJECTION, 3968 BluetoothMapContract.ConvoContactColumns.CONVO_ID 3969 + " = " + threadId, null, null); 3970 // TODO this will not work for group-chats 3971 if(contacts != null && contacts.moveToFirst()){ 3972 String name = contacts.getString(contacts.getColumnIndex( 3973 BluetoothMapContract.ConvoContactColumns.NAME)); 3974 String btUid[] = new String[1]; 3975 btUid[0]= contacts.getString(contacts.getColumnIndex( 3976 BluetoothMapContract.ConvoContactColumns.X_BT_UID)); 3977 String nickname = contacts.getString(contacts.getColumnIndex( 3978 BluetoothMapContract.ConvoContactColumns.NICKNAME)); 3979 String btUci[] = new String[1]; 3980 String btOwnUci[] = new String[1]; 3981 btOwnUci[0] = mAccount.getUciFull(); 3982 btUci[0] = contacts.getString(contacts.getColumnIndex( 3983 BluetoothMapContract.ConvoContactColumns.UCI)); 3984 if(folderId == BluetoothMapContract.FOLDER_ID_SENT 3985 || folderId == BluetoothMapContract.FOLDER_ID_OUTBOX) { 3986 message.addRecipient(nickname,name,null, null, btUid, btUci); 3987 message.addOriginator(null, btOwnUci); 3988 3989 }else { 3990 message.addOriginator(nickname,name,null, null, btUid, btUci); 3991 message.addRecipient(null, btOwnUci); 3992 3993 } 3994 } 3995 return message.encode(); 3996 } 3997 } finally { 3998 if(c != null) c.close(); 3999 if(contacts != null) contacts.close(); 4000 } 4001 4002 throw new IllegalArgumentException("IM handle not found"); 4003 } 4004 4005 public void setRemoteFeatureMask(int featureMask){ 4006 this.mRemoteFeatureMask = featureMask; 4007 if(V) Log.d(TAG, "setRemoteFeatureMask"); 4008 if((this.mRemoteFeatureMask & BluetoothMapUtils.MAP_FEATURE_MESSAGE_LISTING_FORMAT_V11_BIT) 4009 == BluetoothMapUtils.MAP_FEATURE_MESSAGE_LISTING_FORMAT_V11_BIT) { 4010 if(V) Log.d(TAG, "setRemoteFeatureMask MAP_MESSAGE_LISTING_FORMAT_V11"); 4011 this.mMsgListingVersion = BluetoothMapUtils.MAP_MESSAGE_LISTING_FORMAT_V11; 4012 } 4013 } 4014 4015 public int getRemoteFeatureMask(){ 4016 return this.mRemoteFeatureMask; 4017 } 4018 4019 HashMap<Long,BluetoothMapConvoListingElement> getSmsMmsConvoList() { 4020 return mMasInstance.getSmsMmsConvoList(); 4021 } 4022 4023 void setSmsMmsConvoList(HashMap<Long,BluetoothMapConvoListingElement> smsMmsConvoList) { 4024 mMasInstance.setSmsMmsConvoList(smsMmsConvoList); 4025 } 4026 4027 HashMap<Long,BluetoothMapConvoListingElement> getImEmailConvoList() { 4028 return mMasInstance.getImEmailConvoList(); 4029 } 4030 4031 void setImEmailConvoList(HashMap<Long,BluetoothMapConvoListingElement> imEmailConvoList) { 4032 mMasInstance.setImEmailConvoList(imEmailConvoList); 4033 } 4034} 4035