BluetoothMapContent.java revision 1ce48531cb795d2ca180e3d92c044d3fac72e022
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 } 1252 if (V) Log.d(TAG, "setSubject: " + subject); 1253 e.setSubject(subject); 1254 } 1255 } 1256 1257 private void setHandle(BluetoothMapMessageListingElement e, Cursor c, 1258 FilterInfo fi, BluetoothMapAppParams ap) { 1259 long handle = -1; 1260 if (fi.mMsgType == FilterInfo.TYPE_SMS) { 1261 handle = c.getLong(fi.mSmsColId); 1262 } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { 1263 handle = c.getLong(fi.mMmsColId); 1264 } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL || 1265 fi.mMsgType == FilterInfo.TYPE_IM) { 1266 handle = c.getLong(fi.mMessageColId); 1267 } 1268 if (V) Log.d(TAG, "setHandle: " + handle ); 1269 e.setHandle(handle); 1270 } 1271 1272 private BluetoothMapMessageListingElement element(Cursor c, FilterInfo fi, 1273 BluetoothMapAppParams ap) { 1274 BluetoothMapMessageListingElement e = new BluetoothMapMessageListingElement(); 1275 setHandle(e, c, fi, ap); 1276 setDateTime(e, c, fi, ap); 1277 e.setType(getType(c, fi), ((ap.getParameterMask() & MASK_TYPE) != 0) ? true : false); 1278 setRead(e, c, fi, ap); 1279 // we set number and name for sender/recipient later 1280 // they require lookup on contacts so no need to 1281 // do it for all elements unless they are to be used. 1282 e.setCursorIndex(c.getPosition()); 1283 return e; 1284 } 1285 1286 private BluetoothMapConvoListingElement createConvoElement(Cursor c, FilterInfo fi, 1287 BluetoothMapAppParams ap) { 1288 BluetoothMapConvoListingElement e = new BluetoothMapConvoListingElement(); 1289 setLastActivity(e, c, fi, ap); 1290 e.setType(getType(c, fi)); 1291// setConvoRead(e, c, fi, ap); 1292 e.setCursorIndex(c.getPosition()); 1293 return e; 1294 } 1295 1296 /* TODO: Change to use SmsMmsContacts.getContactNameFromPhone() with proper use of 1297 * caching. */ 1298 public static String getContactNameFromPhone(String phone, ContentResolver resolver) { 1299 String name = null; 1300 //Handle possible exception for empty phone address 1301 if (TextUtils.isEmpty(phone)) { 1302 return name; 1303 } 1304 1305 Uri uri = Uri.withAppendedPath(PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI, 1306 Uri.encode(phone)); 1307 1308 String[] projection = {Contacts._ID, Contacts.DISPLAY_NAME}; 1309 String selection = Contacts.IN_VISIBLE_GROUP + "=1"; 1310 String orderBy = Contacts.DISPLAY_NAME + " ASC"; 1311 Cursor c = null; 1312 try { 1313 c = resolver.query(uri, projection, selection, null, orderBy); 1314 if(c != null) { 1315 int colIndex = c.getColumnIndex(Contacts.DISPLAY_NAME); 1316 if (c.getCount() >= 1) { 1317 c.moveToFirst(); 1318 name = c.getString(colIndex); 1319 } 1320 } 1321 } finally { 1322 if(c != null) c.close(); 1323 } 1324 return name; 1325 } 1326 /** 1327 * Get SMS RecipientAddresses for DRAFT folder based on threadId 1328 * 1329 */ 1330 static public String getCanonicalAddressSms(ContentResolver r, int threadId) { 1331 String [] RECIPIENT_ID_PROJECTION = { Threads.RECIPIENT_IDS }; 1332 /* 1333 1. Get Recipient Ids from Threads.CONTENT_URI 1334 2. Get Recipient Address for corresponding Id from canonical-addresses table. 1335 */ 1336 1337 //Uri sAllCanonical = Uri.parse("content://mms-sms/canonical-addresses"); 1338 Uri sAllCanonical = 1339 MmsSms.CONTENT_URI.buildUpon().appendPath("canonical-addresses").build(); 1340 Uri sAllThreadsUri = 1341 Threads.CONTENT_URI.buildUpon().appendQueryParameter("simple", "true").build(); 1342 Cursor cr = null; 1343 String recipientAddress = ""; 1344 String recipientIds = null; 1345 String whereClause = "_id="+threadId; 1346 if (V) Log.v(TAG, "whereClause is "+ whereClause); 1347 try { 1348 cr = r.query(sAllThreadsUri, RECIPIENT_ID_PROJECTION, whereClause, null, null); 1349 if (cr != null && cr.moveToFirst()) { 1350 recipientIds = cr.getString(0); 1351 if (V) Log.v(TAG, "cursor.getCount(): " + cr.getCount() + "recipientIds: " 1352 + recipientIds + "selection: "+ whereClause ); 1353 } 1354 } finally { 1355 if(cr != null) { 1356 cr.close(); 1357 cr = null; 1358 } 1359 } 1360 if (V) Log.v(TAG, "recipientIds with spaces: "+ recipientIds +"\n"); 1361 if(recipientIds != null) { 1362 String recipients[] = null; 1363 whereClause = ""; 1364 recipients = recipientIds.split(" "); 1365 for (String id: recipients) { 1366 if(whereClause.length() != 0) 1367 whereClause +=" OR "; 1368 whereClause +="_id="+id; 1369 } 1370 if (V) Log.v(TAG, "whereClause is "+ whereClause); 1371 try { 1372 cr = r.query(sAllCanonical , null, whereClause, null, null); 1373 if (cr != null && cr.moveToFirst()) { 1374 do { 1375 //TODO: Multiple Recipeints are appended with ";" for now. 1376 if(recipientAddress.length() != 0 ) 1377 recipientAddress+=";"; 1378 recipientAddress += cr.getString( 1379 cr.getColumnIndex(CanonicalAddressesColumns.ADDRESS)); 1380 } while(cr.moveToNext()); 1381 } 1382 } finally { 1383 if(cr != null) 1384 cr.close(); 1385 } 1386 } 1387 1388 if(V) Log.v(TAG,"Final recipientAddress : "+ recipientAddress); 1389 return recipientAddress; 1390 } 1391 1392 static public String getAddressMms(ContentResolver r, long id, int type) { 1393 String selection = new String("msg_id=" + id + " AND type=" + type); 1394 String uriStr = new String(Mms.CONTENT_URI + "/" + id + "/addr"); 1395 Uri uriAddress = Uri.parse(uriStr); 1396 String addr = null; 1397 String[] projection = {Mms.Addr.ADDRESS}; 1398 Cursor c = null; 1399 try { 1400 c = r.query(uriAddress, projection, selection, null, null); // TODO: Add projection 1401 int colIndex = c.getColumnIndex(Mms.Addr.ADDRESS); 1402 if (c != null) { 1403 if(c.moveToFirst()) { 1404 addr = c.getString(colIndex); 1405 if(addr.equals(INSERT_ADDRES_TOKEN)) { 1406 addr = ""; 1407 } 1408 } 1409 } 1410 } finally { 1411 if (c != null) c.close(); 1412 } 1413 return addr; 1414 } 1415 1416 /** 1417 * Matching functions for originator and recipient for MMS 1418 * @return true if found a match 1419 */ 1420 private boolean matchRecipientMms(Cursor c, FilterInfo fi, String recip) { 1421 boolean res; 1422 long id = c.getLong(c.getColumnIndex(BaseColumns._ID)); 1423 String phone = getAddressMms(mResolver, id, MMS_TO); 1424 if (phone != null && phone.length() > 0) { 1425 if (phone.matches(recip)) { 1426 if (V) Log.v(TAG, "matchRecipientMms: match recipient phone = " + phone); 1427 res = true; 1428 } else { 1429 String name = getContactNameFromPhone(phone, mResolver); 1430 if (name != null && name.length() > 0 && name.matches(recip)) { 1431 if (V) Log.v(TAG, "matchRecipientMms: match recipient name = " + name); 1432 res = true; 1433 } else { 1434 res = false; 1435 } 1436 } 1437 } else { 1438 res = false; 1439 } 1440 return res; 1441 } 1442 1443 private boolean matchRecipientSms(Cursor c, FilterInfo fi, String recip) { 1444 boolean res; 1445 int msgType = c.getInt(c.getColumnIndex(Sms.TYPE)); 1446 if (msgType == 1) { 1447 String phone = fi.mPhoneNum; 1448 String name = fi.mPhoneAlphaTag; 1449 if (phone != null && phone.length() > 0 && phone.matches(recip)) { 1450 if (V) Log.v(TAG, "matchRecipientSms: match recipient phone = " + phone); 1451 res = true; 1452 } else if (name != null && name.length() > 0 && name.matches(recip)) { 1453 if (V) Log.v(TAG, "matchRecipientSms: match recipient name = " + name); 1454 res = true; 1455 } else { 1456 res = false; 1457 } 1458 } else { 1459 String phone = c.getString(c.getColumnIndex(Sms.ADDRESS)); 1460 if (phone != null && phone.length() > 0) { 1461 if (phone.matches(recip)) { 1462 if (V) Log.v(TAG, "matchRecipientSms: match recipient phone = " + phone); 1463 res = true; 1464 } else { 1465 String name = getContactNameFromPhone(phone, mResolver); 1466 if (name != null && name.length() > 0 && name.matches(recip)) { 1467 if (V) Log.v(TAG, "matchRecipientSms: match recipient name = " + name); 1468 res = true; 1469 } else { 1470 res = false; 1471 } 1472 } 1473 } else { 1474 res = false; 1475 } 1476 } 1477 return res; 1478 } 1479 1480 private boolean matchRecipient(Cursor c, FilterInfo fi, BluetoothMapAppParams ap) { 1481 boolean res; 1482 String recip = ap.getFilterRecipient(); 1483 if (recip != null && recip.length() > 0) { 1484 recip = recip.replace("*", ".*"); 1485 recip = ".*" + recip + ".*"; 1486 if (fi.mMsgType == FilterInfo.TYPE_SMS) { 1487 res = matchRecipientSms(c, fi, recip); 1488 } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { 1489 res = matchRecipientMms(c, fi, recip); 1490 } else { 1491 if (D) Log.d(TAG, "matchRecipient: Unknown msg type: " + fi.mMsgType); 1492 res = false; 1493 } 1494 } else { 1495 res = true; 1496 } 1497 return res; 1498 } 1499 1500 private boolean matchOriginatorMms(Cursor c, FilterInfo fi, String orig) { 1501 boolean res; 1502 long id = c.getLong(c.getColumnIndex(BaseColumns._ID)); 1503 String phone = getAddressMms(mResolver, id, MMS_FROM); 1504 if (phone != null && phone.length() > 0) { 1505 if (phone.matches(orig)) { 1506 if (V) Log.v(TAG, "matchOriginatorMms: match originator phone = " + phone); 1507 res = true; 1508 } else { 1509 String name = getContactNameFromPhone(phone, mResolver); 1510 if (name != null && name.length() > 0 && name.matches(orig)) { 1511 if (V) Log.v(TAG, "matchOriginatorMms: match originator name = " + name); 1512 res = true; 1513 } else { 1514 res = false; 1515 } 1516 } 1517 } else { 1518 res = false; 1519 } 1520 return res; 1521 } 1522 1523 private boolean matchOriginatorSms(Cursor c, FilterInfo fi, String orig) { 1524 boolean res; 1525 int msgType = c.getInt(c.getColumnIndex(Sms.TYPE)); 1526 if (msgType == 1) { 1527 String phone = c.getString(c.getColumnIndex(Sms.ADDRESS)); 1528 if (phone !=null && phone.length() > 0) { 1529 if (phone.matches(orig)) { 1530 if (V) Log.v(TAG, "matchOriginatorSms: match originator phone = " + phone); 1531 res = true; 1532 } else { 1533 String name = getContactNameFromPhone(phone, mResolver); 1534 if (name != null && name.length() > 0 && name.matches(orig)) { 1535 if (V) Log.v(TAG, "matchOriginatorSms: match originator name = " + name); 1536 res = true; 1537 } else { 1538 res = false; 1539 } 1540 } 1541 } else { 1542 res = false; 1543 } 1544 } else { 1545 String phone = fi.mPhoneNum; 1546 String name = fi.mPhoneAlphaTag; 1547 if (phone != null && phone.length() > 0 && phone.matches(orig)) { 1548 if (V) Log.v(TAG, "matchOriginatorSms: match originator phone = " + phone); 1549 res = true; 1550 } else if (name != null && name.length() > 0 && name.matches(orig)) { 1551 if (V) Log.v(TAG, "matchOriginatorSms: match originator name = " + name); 1552 res = true; 1553 } else { 1554 res = false; 1555 } 1556 } 1557 return res; 1558 } 1559 1560 private boolean matchOriginator(Cursor c, FilterInfo fi, BluetoothMapAppParams ap) { 1561 boolean res; 1562 String orig = ap.getFilterOriginator(); 1563 if (orig != null && orig.length() > 0) { 1564 orig = orig.replace("*", ".*"); 1565 orig = ".*" + orig + ".*"; 1566 if (fi.mMsgType == FilterInfo.TYPE_SMS) { 1567 res = matchOriginatorSms(c, fi, orig); 1568 } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { 1569 res = matchOriginatorMms(c, fi, orig); 1570 } else { 1571 if(D) Log.d(TAG, "matchOriginator: Unknown msg type: " + fi.mMsgType); 1572 res = false; 1573 } 1574 } else { 1575 res = true; 1576 } 1577 return res; 1578 } 1579 1580 private boolean matchAddresses(Cursor c, FilterInfo fi, BluetoothMapAppParams ap) { 1581 if (matchOriginator(c, fi, ap) && matchRecipient(c, fi, ap)) { 1582 return true; 1583 } else { 1584 return false; 1585 } 1586 } 1587 1588 /* 1589 * Where filter functions 1590 * */ 1591 private String setWhereFilterFolderTypeSms(String folder) { 1592 String where = ""; 1593 if (BluetoothMapContract.FOLDER_NAME_INBOX.equalsIgnoreCase(folder)) { 1594 where = Sms.TYPE + " = 1 AND " + Sms.THREAD_ID + " <> -1"; 1595 } else if (BluetoothMapContract.FOLDER_NAME_OUTBOX.equalsIgnoreCase(folder)) { 1596 where = "(" + Sms.TYPE + " = 4 OR " + Sms.TYPE + " = 5 OR " 1597 + Sms.TYPE + " = 6) AND " + Sms.THREAD_ID + " <> -1"; 1598 } else if (BluetoothMapContract.FOLDER_NAME_SENT.equalsIgnoreCase(folder)) { 1599 where = Sms.TYPE + " = 2 AND " + Sms.THREAD_ID + " <> -1"; 1600 } else if (BluetoothMapContract.FOLDER_NAME_DRAFT.equalsIgnoreCase(folder)) { 1601 where = Sms.TYPE + " = 3 AND " + Sms.THREAD_ID + " <> -1"; 1602 } else if (BluetoothMapContract.FOLDER_NAME_DELETED.equalsIgnoreCase(folder)) { 1603 where = Sms.THREAD_ID + " = -1"; 1604 } 1605 1606 return where; 1607 } 1608 1609 private String setWhereFilterFolderTypeMms(String folder) { 1610 String where = ""; 1611 if (BluetoothMapContract.FOLDER_NAME_INBOX.equalsIgnoreCase(folder)) { 1612 where = Mms.MESSAGE_BOX + " = 1 AND " + Mms.THREAD_ID + " <> -1"; 1613 } else if (BluetoothMapContract.FOLDER_NAME_OUTBOX.equalsIgnoreCase(folder)) { 1614 where = Mms.MESSAGE_BOX + " = 4 AND " + Mms.THREAD_ID + " <> -1"; 1615 } else if (BluetoothMapContract.FOLDER_NAME_SENT.equalsIgnoreCase(folder)) { 1616 where = Mms.MESSAGE_BOX + " = 2 AND " + Mms.THREAD_ID + " <> -1"; 1617 } else if (BluetoothMapContract.FOLDER_NAME_DRAFT.equalsIgnoreCase(folder)) { 1618 where = Mms.MESSAGE_BOX + " = 3 AND " + Mms.THREAD_ID + " <> -1"; 1619 } else if (BluetoothMapContract.FOLDER_NAME_DELETED.equalsIgnoreCase(folder)) { 1620 where = Mms.THREAD_ID + " = -1"; 1621 } 1622 1623 return where; 1624 } 1625 1626 private String setWhereFilterFolderTypeEmail(long folderId) { 1627 String where = ""; 1628 if (folderId >= 0) { 1629 where = BluetoothMapContract.MessageColumns.FOLDER_ID + " = " + folderId; 1630 } else { 1631 Log.e(TAG, "setWhereFilterFolderTypeEmail: not valid!" ); 1632 throw new IllegalArgumentException("Invalid folder ID"); 1633 } 1634 return where; 1635 } 1636 1637 private String setWhereFilterFolderTypeIm(long folderId) { 1638 String where = ""; 1639 if (folderId > BluetoothMapContract.FOLDER_ID_OTHER) { 1640 where = BluetoothMapContract.MessageColumns.FOLDER_ID + " = " + folderId; 1641 } else { 1642 Log.e(TAG, "setWhereFilterFolderTypeIm: not valid!" ); 1643 throw new IllegalArgumentException("Invalid folder ID"); 1644 } 1645 return where; 1646 } 1647 1648 private String setWhereFilterFolderType(BluetoothMapFolderElement folderElement, 1649 FilterInfo fi) { 1650 String where = ""; 1651 if(folderElement.shouldIgnore()) { 1652 where = "1=1"; 1653 } else { 1654 if (fi.mMsgType == FilterInfo.TYPE_SMS) { 1655 where = setWhereFilterFolderTypeSms(folderElement.getName()); 1656 } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { 1657 where = setWhereFilterFolderTypeMms(folderElement.getName()); 1658 } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL) { 1659 where = setWhereFilterFolderTypeEmail(folderElement.getFolderId()); 1660 } else if (fi.mMsgType == FilterInfo.TYPE_IM) { 1661 where = setWhereFilterFolderTypeIm(folderElement.getFolderId()); 1662 } 1663 } 1664 return where; 1665 } 1666 1667 private String setWhereFilterReadStatus(BluetoothMapAppParams ap, FilterInfo fi) { 1668 String where = ""; 1669 if (ap.getFilterReadStatus() != -1) { 1670 if (fi.mMsgType == FilterInfo.TYPE_SMS) { 1671 if ((ap.getFilterReadStatus() & 0x01) != 0) { 1672 where = " AND " + Sms.READ + "= 0"; 1673 } 1674 1675 if ((ap.getFilterReadStatus() & 0x02) != 0) { 1676 where = " AND " + Sms.READ + "= 1"; 1677 } 1678 } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { 1679 if ((ap.getFilterReadStatus() & 0x01) != 0) { 1680 where = " AND " + Mms.READ + "= 0"; 1681 } 1682 1683 if ((ap.getFilterReadStatus() & 0x02) != 0) { 1684 where = " AND " + Mms.READ + "= 1"; 1685 } 1686 } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL || 1687 fi.mMsgType == FilterInfo.TYPE_IM) { 1688 if ((ap.getFilterReadStatus() & 0x01) != 0) { 1689 where = " AND " + BluetoothMapContract.MessageColumns.FLAG_READ + "= 0"; 1690 } 1691 if ((ap.getFilterReadStatus() & 0x02) != 0) { 1692 where = " AND " + BluetoothMapContract.MessageColumns.FLAG_READ + "= 1"; 1693 } 1694 } 1695 } 1696 return where; 1697 } 1698 1699 private String setWhereFilterPeriod(BluetoothMapAppParams ap, FilterInfo fi) { 1700 String where = ""; 1701 1702 if ((ap.getFilterPeriodBegin() != -1)) { 1703 if (fi.mMsgType == FilterInfo.TYPE_SMS) { 1704 where = " AND " + Sms.DATE + " >= " + ap.getFilterPeriodBegin(); 1705 } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { 1706 where = " AND " + Mms.DATE + " >= " + (ap.getFilterPeriodBegin() / 1000L); 1707 } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL|| 1708 fi.mMsgType == FilterInfo.TYPE_IM) { 1709 where = " AND " + BluetoothMapContract.MessageColumns.DATE + 1710 " >= " + (ap.getFilterPeriodBegin()); 1711 } 1712 } 1713 1714 if ((ap.getFilterPeriodEnd() != -1)) { 1715 if (fi.mMsgType == FilterInfo.TYPE_SMS) { 1716 where += " AND " + Sms.DATE + " < " + ap.getFilterPeriodEnd(); 1717 } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { 1718 where += " AND " + Mms.DATE + " < " + (ap.getFilterPeriodEnd() / 1000L); 1719 } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL|| 1720 fi.mMsgType == FilterInfo.TYPE_IM) { 1721 where += " AND " + BluetoothMapContract.MessageColumns.DATE + 1722 " < " + (ap.getFilterPeriodEnd()); 1723 } 1724 } 1725 return where; 1726 } 1727 private String setWhereFilterLastActivity(BluetoothMapAppParams ap, FilterInfo fi) { 1728 String where = ""; 1729 if ((ap.getFilterLastActivityBegin() != -1)) { 1730 if (fi.mMsgType == FilterInfo.TYPE_SMS) { 1731 where = " AND " + Sms.DATE + " >= " + ap.getFilterLastActivityBegin(); 1732 } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { 1733 where = " AND " + Mms.DATE + " >= " + (ap.getFilterLastActivityBegin() / 1000L); 1734 } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL|| 1735 fi.mMsgType == FilterInfo.TYPE_IM ) { 1736 where = " AND " + BluetoothMapContract.ConversationColumns.LAST_THREAD_ACTIVITY + 1737 " >= " + (ap.getFilterPeriodBegin()); 1738 } 1739 } 1740 if ((ap.getFilterLastActivityEnd() != -1)) { 1741 if (fi.mMsgType == FilterInfo.TYPE_SMS) { 1742 where += " AND " + Sms.DATE + " < " + ap.getFilterLastActivityEnd(); 1743 } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { 1744 where += " AND " + Mms.DATE + " < " + (ap.getFilterPeriodEnd() / 1000L); 1745 } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL||fi.mMsgType == FilterInfo.TYPE_IM) { 1746 where += " AND " + BluetoothMapContract.ConversationColumns.LAST_THREAD_ACTIVITY 1747 + " < " + (ap.getFilterLastActivityEnd()); 1748 } 1749 } 1750 return where; 1751 } 1752 1753 1754 private String setWhereFilterOriginatorEmail(BluetoothMapAppParams ap) { 1755 String where = ""; 1756 String orig = ap.getFilterOriginator(); 1757 1758 /* Be aware of wild cards in the beginning of string, may not be valid? */ 1759 if (orig != null && orig.length() > 0) { 1760 orig = orig.replace("*", "%"); 1761 where = " AND " + BluetoothMapContract.MessageColumns.FROM_LIST 1762 + " LIKE '%" + orig + "%'"; 1763 } 1764 return where; 1765 } 1766 1767 private String setWhereFilterOriginatorIM(BluetoothMapAppParams ap) { 1768 String where = ""; 1769 String orig = ap.getFilterOriginator(); 1770 1771 /* Be aware of wild cards in the beginning of string, may not be valid? */ 1772 if (orig != null && orig.length() > 0) { 1773 orig = orig.replace("*", "%"); 1774 where = " AND " + BluetoothMapContract.MessageColumns.FROM_LIST 1775 + " LIKE '%" + orig + "%'"; 1776 } 1777 return where; 1778 } 1779 1780 private String setWhereFilterPriority(BluetoothMapAppParams ap, FilterInfo fi) { 1781 String where = ""; 1782 int pri = ap.getFilterPriority(); 1783 /*only MMS have priority info */ 1784 if(fi.mMsgType == FilterInfo.TYPE_MMS) 1785 { 1786 if(pri == 0x0002) 1787 { 1788 where += " AND " + Mms.PRIORITY + "<=" + 1789 Integer.toString(PduHeaders.PRIORITY_NORMAL); 1790 }else if(pri == 0x0001) { 1791 where += " AND " + Mms.PRIORITY + "=" + 1792 Integer.toString(PduHeaders.PRIORITY_HIGH); 1793 } 1794 } 1795 if(fi.mMsgType == FilterInfo.TYPE_EMAIL || 1796 fi.mMsgType == FilterInfo.TYPE_IM) 1797 { 1798 if(pri == 0x0002) 1799 { 1800 where += " AND " + BluetoothMapContract.MessageColumns.FLAG_HIGH_PRIORITY + "!=1"; 1801 }else if(pri == 0x0001) { 1802 where += " AND " + BluetoothMapContract.MessageColumns.FLAG_HIGH_PRIORITY + "=1"; 1803 } 1804 } 1805 // TODO: no priority filtering in IM 1806 return where; 1807 } 1808 1809 private String setWhereFilterRecipientEmail(BluetoothMapAppParams ap) { 1810 String where = ""; 1811 String recip = ap.getFilterRecipient(); 1812 1813 /* Be aware of wild cards in the beginning of string, may not be valid? */ 1814 if (recip != null && recip.length() > 0) { 1815 recip = recip.replace("*", "%"); 1816 where = " AND (" 1817 + BluetoothMapContract.MessageColumns.TO_LIST + " LIKE '%" + recip + "%' OR " 1818 + BluetoothMapContract.MessageColumns.CC_LIST + " LIKE '%" + recip + "%' OR " 1819 + BluetoothMapContract.MessageColumns.BCC_LIST + " LIKE '%" + recip + "%' )"; 1820 } 1821 return where; 1822 } 1823 1824 private String setWhereFilterMessageHandle(BluetoothMapAppParams ap, FilterInfo fi) { 1825 String where = ""; 1826 long id = -1; 1827 String msgHandle = ap.getFilterMsgHandleString(); 1828 if(msgHandle != null) { 1829 id = BluetoothMapUtils.getCpHandle(msgHandle); 1830 if(D)Log.d(TAG,"id: " + id); 1831 } 1832 if(id != -1) { 1833 if (fi.mMsgType == FilterInfo.TYPE_SMS) { 1834 where = " AND " + Sms._ID + " = " + id; 1835 } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { 1836 where = " AND " + Mms._ID + " = " + id; 1837 } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL || 1838 fi.mMsgType == FilterInfo.TYPE_IM) { 1839 where = " AND " + BluetoothMapContract.MessageColumns._ID + " = " + id; 1840 } 1841 } 1842 return where; 1843 } 1844 1845 private String setWhereFilterThreadId(BluetoothMapAppParams ap, FilterInfo fi) { 1846 String where = ""; 1847 long id = -1; 1848 String msgHandle = ap.getFilterConvoIdString(); 1849 if(msgHandle != null) { 1850 id = BluetoothMapUtils.getMsgHandleAsLong(msgHandle); 1851 if(D)Log.d(TAG,"id: " + id); 1852 } 1853 if(id > 0) { 1854 if (fi.mMsgType == FilterInfo.TYPE_SMS) { 1855 where = " AND " + Sms.THREAD_ID + " = " + id; 1856 } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { 1857 where = " AND " + Mms.THREAD_ID + " = " + id; 1858 } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL || 1859 fi.mMsgType == FilterInfo.TYPE_IM) { 1860 where = " AND " + BluetoothMapContract.MessageColumns.THREAD_ID + " = " + id; 1861 } 1862 } 1863 1864 return where; 1865 } 1866 1867 private String setWhereFilter(BluetoothMapFolderElement folderElement, 1868 FilterInfo fi, BluetoothMapAppParams ap) { 1869 String where = ""; 1870 where += setWhereFilterFolderType(folderElement, fi); 1871 1872 String msgHandleWhere = setWhereFilterMessageHandle(ap, fi); 1873 /* if message handle filter is available, the other filters should be ignored */ 1874 if(msgHandleWhere.isEmpty()) { 1875 where += setWhereFilterReadStatus(ap, fi); 1876 where += setWhereFilterPriority(ap,fi); 1877 where += setWhereFilterPeriod(ap, fi); 1878 if (fi.mMsgType == FilterInfo.TYPE_EMAIL) { 1879 where += setWhereFilterOriginatorEmail(ap); 1880 where += setWhereFilterRecipientEmail(ap); 1881 } 1882 if (fi.mMsgType == FilterInfo.TYPE_IM) { 1883 where += setWhereFilterOriginatorIM(ap); 1884 // TODO: set 'where' filer recipient? 1885 } 1886 where += setWhereFilterThreadId(ap, fi); 1887 } else { 1888 where += msgHandleWhere; 1889 } 1890 1891 return where; 1892 } 1893 1894 1895 /* Used only for SMS/MMS */ 1896 private void setConvoWhereFilterSmsMms(StringBuilder selection, ArrayList<String> selectionArgs, 1897 FilterInfo fi, BluetoothMapAppParams ap) { 1898 1899 if (smsSelected(fi, ap) || mmsSelected(ap)) { 1900 1901 // Filter Read Status 1902 if(ap.getFilterReadStatus() != BluetoothMapAppParams.INVALID_VALUE_PARAMETER) { 1903 if ((ap.getFilterReadStatus() & FILTER_READ_STATUS_UNREAD_ONLY) != 0) { 1904 selection.append(" AND ").append(Threads.READ).append(" = 0"); 1905 } 1906 if ((ap.getFilterReadStatus() & FILTER_READ_STATUS_READ_ONLY) != 0) { 1907 selection.append(" AND ").append(Threads.READ).append(" = 1"); 1908 } 1909 } 1910 1911 // Filter time 1912 if ((ap.getFilterLastActivityBegin() != BluetoothMapAppParams.INVALID_VALUE_PARAMETER)){ 1913 selection.append(" AND ").append(Threads.DATE).append(" >= ") 1914 .append(ap.getFilterLastActivityBegin()); 1915 } 1916 if ((ap.getFilterLastActivityEnd() != BluetoothMapAppParams.INVALID_VALUE_PARAMETER)) { 1917 selection.append(" AND ").append(Threads.DATE).append(" <= ") 1918 .append(ap.getFilterLastActivityEnd()); 1919 } 1920 1921 // Filter ConvoId 1922 long convoId = -1; 1923 if(ap.getFilterConvoId() != null) { 1924 convoId = ap.getFilterConvoId().getLeastSignificantBits(); 1925 } 1926 if(convoId > 0) { 1927 selection.append(" AND ").append(Threads._ID).append(" = ") 1928 .append(Long.toString(convoId)); 1929 } 1930 } 1931 } 1932 1933 1934 1935 /** 1936 * Determine from application parameter if sms should be included. 1937 * The filter mask is set for message types not selected 1938 * @param fi 1939 * @param ap 1940 * @return boolean true if sms is selected, false if not 1941 */ 1942 private boolean smsSelected(FilterInfo fi, BluetoothMapAppParams ap) { 1943 int msgType = ap.getFilterMessageType(); 1944 int phoneType = fi.mPhoneType; 1945 1946 if (D) Log.d(TAG, "smsSelected msgType: " + msgType); 1947 1948 if (msgType == BluetoothMapAppParams.INVALID_VALUE_PARAMETER) 1949 return true; 1950 1951 if ((msgType & (BluetoothMapAppParams.FILTER_NO_SMS_CDMA 1952 |BluetoothMapAppParams.FILTER_NO_SMS_GSM)) == 0) 1953 return true; 1954 1955 if (((msgType & BluetoothMapAppParams.FILTER_NO_SMS_GSM) == 0) 1956 && (phoneType == TelephonyManager.PHONE_TYPE_GSM)) 1957 return true; 1958 1959 if (((msgType & BluetoothMapAppParams.FILTER_NO_SMS_CDMA) == 0) 1960 && (phoneType == TelephonyManager.PHONE_TYPE_CDMA)) 1961 return true; 1962 1963 return false; 1964 } 1965 1966 /** 1967 * Determine from application parameter if mms should be included. 1968 * The filter mask is set for message types not selected 1969 * @param fi 1970 * @param ap 1971 * @return boolean true if mms is selected, false if not 1972 */ 1973 private boolean mmsSelected(BluetoothMapAppParams ap) { 1974 int msgType = ap.getFilterMessageType(); 1975 1976 if (D) Log.d(TAG, "mmsSelected msgType: " + msgType); 1977 1978 if (msgType == BluetoothMapAppParams.INVALID_VALUE_PARAMETER) 1979 return true; 1980 1981 if ((msgType & BluetoothMapAppParams.FILTER_NO_MMS) == 0) 1982 return true; 1983 1984 return false; 1985 } 1986 1987 /** 1988 * Determine from application parameter if email should be included. 1989 * The filter mask is set for message types not selected 1990 * @param fi 1991 * @param ap 1992 * @return boolean true if email is selected, false if not 1993 */ 1994 private boolean emailSelected(BluetoothMapAppParams ap) { 1995 int msgType = ap.getFilterMessageType(); 1996 1997 if (D) Log.d(TAG, "emailSelected msgType: " + msgType); 1998 1999 if (msgType == BluetoothMapAppParams.INVALID_VALUE_PARAMETER) 2000 return true; 2001 2002 if ((msgType & BluetoothMapAppParams.FILTER_NO_EMAIL) == 0) 2003 return true; 2004 2005 return false; 2006 } 2007 2008 /** 2009 * Determine from application parameter if IM should be included. 2010 * The filter mask is set for message types not selected 2011 * @param fi 2012 * @param ap 2013 * @return boolean true if im is selected, false if not 2014 */ 2015 private boolean imSelected(BluetoothMapAppParams ap) { 2016 int msgType = ap.getFilterMessageType(); 2017 2018 if (D) Log.d(TAG, "imSelected msgType: " + msgType); 2019 2020 if (msgType == BluetoothMapAppParams.INVALID_VALUE_PARAMETER) 2021 return true; 2022 2023 if ((msgType & BluetoothMapAppParams.FILTER_NO_IM) == 0) 2024 return true; 2025 2026 return false; 2027 } 2028 2029 private void setFilterInfo(FilterInfo fi) { 2030 TelephonyManager tm = 2031 (TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE); 2032 if (tm != null) { 2033 fi.mPhoneType = tm.getPhoneType(); 2034 fi.mPhoneNum = tm.getLine1Number(); 2035 fi.mPhoneAlphaTag = tm.getLine1AlphaTag(); 2036 if (D) Log.d(TAG, "phone type = " + fi.mPhoneType + 2037 " phone num = " + fi.mPhoneNum + 2038 " phone alpha tag = " + fi.mPhoneAlphaTag); 2039 } 2040 } 2041 2042 /** 2043 * Get a listing of message in folder after applying filter. 2044 * @param folder Must contain a valid folder string != null 2045 * @param ap Parameters specifying message content and filters 2046 * @return Listing object containing requested messages 2047 */ 2048 public BluetoothMapMessageListing msgListing(BluetoothMapFolderElement folderElement, 2049 BluetoothMapAppParams ap) { 2050 if (D) Log.d(TAG, "msgListing: messageType = " + ap.getFilterMessageType() ); 2051 2052 BluetoothMapMessageListing bmList = new BluetoothMapMessageListing(); 2053 2054 /* We overwrite the parameter mask here if it is 0 or not present, as this 2055 * should cause all parameters to be included in the message list. */ 2056 if(ap.getParameterMask() == BluetoothMapAppParams.INVALID_VALUE_PARAMETER || 2057 ap.getParameterMask() == 0) { 2058 ap.setParameterMask(PARAMETER_MASK_ALL_ENABLED); 2059 if (V) Log.v(TAG, "msgListing(): appParameterMask is zero or not present, " + 2060 "changing to all enabled by default: " + ap.getParameterMask()); 2061 } 2062 if (V) Log.v(TAG, "folderElement hasSmsMmsContent = " + folderElement.hasSmsMmsContent() + 2063 " folderElement.hasEmailContent = " + folderElement.hasEmailContent() + 2064 " folderElement.hasImContent = " + folderElement.hasImContent()); 2065 2066 /* Cache some info used throughout filtering */ 2067 FilterInfo fi = new FilterInfo(); 2068 setFilterInfo(fi); 2069 Cursor smsCursor = null; 2070 Cursor mmsCursor = null; 2071 Cursor emailCursor = null; 2072 Cursor imCursor = null; 2073 String limit = ""; 2074 int countNum = ap.getMaxListCount(); 2075 int offsetNum = ap.getStartOffset(); 2076 if(ap.getMaxListCount()>0){ 2077 limit=" LIMIT "+ (ap.getMaxListCount()+ap.getStartOffset()); 2078 } 2079 try{ 2080 if (smsSelected(fi, ap) && folderElement.hasSmsMmsContent()) { 2081 if(ap.getFilterMessageType() == (BluetoothMapAppParams.FILTER_NO_EMAIL| 2082 BluetoothMapAppParams.FILTER_NO_MMS| 2083 BluetoothMapAppParams.FILTER_NO_SMS_GSM| 2084 BluetoothMapAppParams.FILTER_NO_IM)|| 2085 ap.getFilterMessageType() == (BluetoothMapAppParams.FILTER_NO_EMAIL| 2086 BluetoothMapAppParams.FILTER_NO_MMS| 2087 BluetoothMapAppParams.FILTER_NO_SMS_CDMA| 2088 BluetoothMapAppParams.FILTER_NO_IM)){ 2089 //set real limit and offset if only this type is used 2090 // (only if offset/limit is used) 2091 limit = " LIMIT " + ap.getMaxListCount()+" OFFSET "+ ap.getStartOffset(); 2092 if(D) Log.d(TAG, "SMS Limit => "+limit); 2093 offsetNum = 0; 2094 } 2095 fi.mMsgType = FilterInfo.TYPE_SMS; 2096 if(ap.getFilterPriority() != 1){ /*SMS cannot have high priority*/ 2097 String where = setWhereFilter(folderElement, fi, ap); 2098 if (D) Log.d(TAG, "msgType: " + fi.mMsgType + " where: " + where); 2099 smsCursor = mResolver.query(Sms.CONTENT_URI, 2100 SMS_PROJECTION, where, null, Sms.DATE + " DESC" + limit); 2101 if (smsCursor != null) { 2102 BluetoothMapMessageListingElement e = null; 2103 // store column index so we dont have to look them up anymore (optimization) 2104 if(D) Log.d(TAG, "Found " + smsCursor.getCount() + " sms messages."); 2105 fi.setSmsColumns(smsCursor); 2106 while (smsCursor.moveToNext()) { 2107 if (matchAddresses(smsCursor, fi, ap)) { 2108 if(V) BluetoothMapUtils.printCursor(smsCursor); 2109 e = element(smsCursor, fi, ap); 2110 bmList.add(e); 2111 } 2112 } 2113 } 2114 } 2115 } 2116 2117 if (mmsSelected(ap) && folderElement.hasSmsMmsContent()) { 2118 if(ap.getFilterMessageType() == (BluetoothMapAppParams.FILTER_NO_EMAIL| 2119 BluetoothMapAppParams.FILTER_NO_SMS_CDMA| 2120 BluetoothMapAppParams.FILTER_NO_SMS_GSM| 2121 BluetoothMapAppParams.FILTER_NO_IM)){ 2122 //set real limit and offset if only this type is used 2123 //(only if offset/limit is used) 2124 limit = " LIMIT " + ap.getMaxListCount()+" OFFSET "+ ap.getStartOffset(); 2125 if(D) Log.d(TAG, "MMS Limit => "+limit); 2126 offsetNum = 0; 2127 } 2128 fi.mMsgType = FilterInfo.TYPE_MMS; 2129 String where = setWhereFilter(folderElement, fi, ap); 2130 where += " AND " + INTERESTED_MESSAGE_TYPE_CLAUSE; 2131 if(!where.isEmpty()) { 2132 if (D) Log.d(TAG, "msgType: " + fi.mMsgType + " where: " + where); 2133 mmsCursor = mResolver.query(Mms.CONTENT_URI, 2134 MMS_PROJECTION, where, null, Mms.DATE + " DESC" + limit); 2135 if (mmsCursor != null) { 2136 BluetoothMapMessageListingElement e = null; 2137 // store column index so we dont have to look them up anymore (optimization) 2138 fi.setMmsColumns(mmsCursor); 2139 if(D) Log.d(TAG, "Found " + mmsCursor.getCount() + " mms messages."); 2140 while (mmsCursor.moveToNext()) { 2141 if (matchAddresses(mmsCursor, fi, ap)) { 2142 if(V) BluetoothMapUtils.printCursor(mmsCursor); 2143 e = element(mmsCursor, fi, ap); 2144 bmList.add(e); 2145 } 2146 } 2147 } 2148 } 2149 } 2150 2151 if (emailSelected(ap) && folderElement.hasEmailContent()) { 2152 if(ap.getFilterMessageType() == (BluetoothMapAppParams.FILTER_NO_MMS| 2153 BluetoothMapAppParams.FILTER_NO_SMS_CDMA| 2154 BluetoothMapAppParams.FILTER_NO_SMS_GSM| 2155 BluetoothMapAppParams.FILTER_NO_IM)){ 2156 //set real limit and offset if only this type is used 2157 //(only if offset/limit is used) 2158 limit = " LIMIT " + ap.getMaxListCount()+" OFFSET "+ ap.getStartOffset(); 2159 if(D) Log.d(TAG, "Email Limit => "+limit); 2160 offsetNum = 0; 2161 } 2162 fi.mMsgType = FilterInfo.TYPE_EMAIL; 2163 String where = setWhereFilter(folderElement, fi, ap); 2164 2165 if(!where.isEmpty()) { 2166 if (D) Log.d(TAG, "msgType: " + fi.mMsgType + " where: " + where); 2167 Uri contentUri = Uri.parse(mBaseUri + BluetoothMapContract.TABLE_MESSAGE); 2168 emailCursor = mResolver.query(contentUri, 2169 BluetoothMapContract.BT_MESSAGE_PROJECTION, where, null, 2170 BluetoothMapContract.MessageColumns.DATE + " DESC" + limit); 2171 if (emailCursor != null) { 2172 BluetoothMapMessageListingElement e = null; 2173 // store column index so we dont have to look them up anymore (optimization) 2174 fi.setEmailMessageColumns(emailCursor); 2175 int cnt = 0; 2176 if(D) Log.d(TAG, "Found " + emailCursor.getCount() + " email messages."); 2177 while (emailCursor.moveToNext()) { 2178 if(V) BluetoothMapUtils.printCursor(emailCursor); 2179 e = element(emailCursor, fi, ap); 2180 bmList.add(e); 2181 } 2182 // emailCursor.close(); 2183 } 2184 } 2185 } 2186 2187 if (imSelected(ap) && folderElement.hasImContent()) { 2188 if(ap.getFilterMessageType() == (BluetoothMapAppParams.FILTER_NO_MMS| 2189 BluetoothMapAppParams.FILTER_NO_SMS_CDMA| 2190 BluetoothMapAppParams.FILTER_NO_SMS_GSM| 2191 BluetoothMapAppParams.FILTER_NO_EMAIL)){ 2192 //set real limit and offset if only this type is used 2193 //(only if offset/limit is used) 2194 limit = " LIMIT " + ap.getMaxListCount() + " OFFSET "+ ap.getStartOffset(); 2195 if(D) Log.d(TAG, "IM Limit => "+limit); 2196 offsetNum = 0; 2197 } 2198 fi.mMsgType = FilterInfo.TYPE_IM; 2199 String where = setWhereFilter(folderElement, fi, ap); 2200 if (D) Log.d(TAG, "msgType: " + fi.mMsgType + " where: " + where); 2201 2202 Uri contentUri = Uri.parse(mBaseUri + BluetoothMapContract.TABLE_MESSAGE); 2203 imCursor = mResolver.query(contentUri, 2204 BluetoothMapContract.BT_INSTANT_MESSAGE_PROJECTION, 2205 where, null, BluetoothMapContract.MessageColumns.DATE + " DESC" + limit); 2206 if (imCursor != null) { 2207 BluetoothMapMessageListingElement e = null; 2208 // store column index so we dont have to look them up anymore (optimization) 2209 fi.setImMessageColumns(imCursor); 2210 if (D) Log.d(TAG, "Found " + imCursor.getCount() + " im messages."); 2211 while (imCursor.moveToNext()) { 2212 if (V) BluetoothMapUtils.printCursor(imCursor); 2213 e = element(imCursor, fi, ap); 2214 bmList.add(e); 2215 } 2216 } 2217 } 2218 2219 /* Enable this if post sorting and segmenting needed */ 2220 bmList.sort(); 2221 bmList.segment(ap.getMaxListCount(), offsetNum); 2222 List<BluetoothMapMessageListingElement> list = bmList.getList(); 2223 int listSize = list.size(); 2224 Cursor tmpCursor = null; 2225 for(int x=0;x<listSize;x++){ 2226 BluetoothMapMessageListingElement ele = list.get(x); 2227 /* If OBEX "GET" request header includes "ParameterMask" with 'Type' NOT set, 2228 * then ele.getType() returns "null" even for a valid cursor. 2229 * Avoid NullPointerException in equals() check when 'mType' value is "null" */ 2230 TYPE tmpType = ele.getType(); 2231 if (smsCursor!= null && 2232 ((TYPE.SMS_GSM).equals(tmpType) || (TYPE.SMS_CDMA).equals(tmpType))) { 2233 tmpCursor = smsCursor; 2234 fi.mMsgType = FilterInfo.TYPE_SMS; 2235 } else if(mmsCursor != null && (TYPE.MMS).equals(tmpType)) { 2236 tmpCursor = mmsCursor; 2237 fi.mMsgType = FilterInfo.TYPE_MMS; 2238 } else if(emailCursor != null && ((TYPE.EMAIL).equals(tmpType))) { 2239 tmpCursor = emailCursor; 2240 fi.mMsgType = FilterInfo.TYPE_EMAIL; 2241 } else if(imCursor != null && ((TYPE.IM).equals(tmpType))) { 2242 tmpCursor = imCursor; 2243 fi.mMsgType = FilterInfo.TYPE_IM; 2244 } 2245 if(tmpCursor != null){ 2246 tmpCursor.moveToPosition(ele.getCursorIndex()); 2247 setSenderAddressing(ele, tmpCursor, fi, ap); 2248 setSenderName(ele, tmpCursor, fi, ap); 2249 setRecipientAddressing(ele, tmpCursor, fi, ap); 2250 setRecipientName(ele, tmpCursor, fi, ap); 2251 setSubject(ele, tmpCursor, fi, ap); 2252 setSize(ele, tmpCursor, fi, ap); 2253 setText(ele, tmpCursor, fi, ap); 2254 setPriority(ele, tmpCursor, fi, ap); 2255 setSent(ele, tmpCursor, fi, ap); 2256 setProtected(ele, tmpCursor, fi, ap); 2257 setReceptionStatus(ele, tmpCursor, fi, ap); 2258 setAttachment(ele, tmpCursor, fi, ap); 2259 2260 if(mMsgListingVersion > BluetoothMapUtils.MAP_MESSAGE_LISTING_FORMAT_V10 ){ 2261 setDeliveryStatus(ele, tmpCursor, fi, ap); 2262 setThreadId(ele, tmpCursor, fi, ap); 2263 setThreadName(ele, tmpCursor, fi, ap); 2264 setFolderType(ele, tmpCursor, fi, ap); 2265 } 2266 } 2267 } 2268 } finally { 2269 if(emailCursor != null)emailCursor.close(); 2270 if(smsCursor != null)smsCursor.close(); 2271 if(mmsCursor != null)mmsCursor.close(); 2272 if(imCursor != null)imCursor.close(); 2273 } 2274 2275 2276 if(D)Log.d(TAG, "messagelisting end"); 2277 return bmList; 2278 } 2279 2280 /** 2281 * Get the size of the message listing 2282 * @param folder Must contain a valid folder string != null 2283 * @param ap Parameters specifying message content and filters 2284 * @return Integer equal to message listing size 2285 */ 2286 public int msgListingSize(BluetoothMapFolderElement folderElement, 2287 BluetoothMapAppParams ap) { 2288 if (D) Log.d(TAG, "msgListingSize: folder = " + folderElement.getName()); 2289 int cnt = 0; 2290 2291 /* Cache some info used throughout filtering */ 2292 FilterInfo fi = new FilterInfo(); 2293 setFilterInfo(fi); 2294 2295 if (smsSelected(fi, ap) && folderElement.hasSmsMmsContent()) { 2296 fi.mMsgType = FilterInfo.TYPE_SMS; 2297 String where = setWhereFilter(folderElement, fi, ap); 2298 Cursor c = mResolver.query(Sms.CONTENT_URI, 2299 SMS_PROJECTION, where, null, Sms.DATE + " DESC"); 2300 try { 2301 if (c != null) { 2302 cnt = c.getCount(); 2303 } 2304 } finally { 2305 if (c != null) c.close(); 2306 } 2307 } 2308 2309 if (mmsSelected(ap) && folderElement.hasSmsMmsContent()) { 2310 fi.mMsgType = FilterInfo.TYPE_MMS; 2311 String where = setWhereFilter(folderElement, fi, ap); 2312 Cursor c = mResolver.query(Mms.CONTENT_URI, 2313 MMS_PROJECTION, where, null, Mms.DATE + " DESC"); 2314 try { 2315 if (c != null) { 2316 cnt += c.getCount(); 2317 } 2318 } finally { 2319 if (c != null) c.close(); 2320 } 2321 } 2322 2323 if (emailSelected(ap) && folderElement.hasEmailContent()) { 2324 fi.mMsgType = FilterInfo.TYPE_EMAIL; 2325 String where = setWhereFilter(folderElement, fi, ap); 2326 if(!where.isEmpty()) { 2327 Uri contentUri = Uri.parse(mBaseUri + BluetoothMapContract.TABLE_MESSAGE); 2328 Cursor c = mResolver.query(contentUri, BluetoothMapContract.BT_MESSAGE_PROJECTION, 2329 where, null, BluetoothMapContract.MessageColumns.DATE + " DESC"); 2330 try { 2331 if (c != null) { 2332 cnt += c.getCount(); 2333 } 2334 } finally { 2335 if (c != null) c.close(); 2336 } 2337 } 2338 } 2339 2340 if (imSelected(ap) && folderElement.hasImContent()) { 2341 fi.mMsgType = FilterInfo.TYPE_IM; 2342 String where = setWhereFilter(folderElement, fi, ap); 2343 if(!where.isEmpty()) { 2344 Uri contentUri = Uri.parse(mBaseUri + BluetoothMapContract.TABLE_MESSAGE); 2345 Cursor c = mResolver.query(contentUri, 2346 BluetoothMapContract.BT_INSTANT_MESSAGE_PROJECTION, 2347 where, null, BluetoothMapContract.MessageColumns.DATE + " DESC"); 2348 try { 2349 if (c != null) { 2350 cnt += c.getCount(); 2351 } 2352 } finally { 2353 if (c != null) c.close(); 2354 } 2355 } 2356 } 2357 2358 if (D) Log.d(TAG, "msgListingSize: size = " + cnt); 2359 return cnt; 2360 } 2361 2362 /** 2363 * Return true if there are unread messages in the requested list of messages 2364 * @param folder folder where the message listing should come from 2365 * @param ap application parameter object 2366 * @return true if unread messages are in the list, else false 2367 */ 2368 public boolean msgListingHasUnread(BluetoothMapFolderElement folderElement, 2369 BluetoothMapAppParams ap) { 2370 if (D) Log.d(TAG, "msgListingHasUnread: folder = " + folderElement.getName()); 2371 int cnt = 0; 2372 2373 /* Cache some info used throughout filtering */ 2374 FilterInfo fi = new FilterInfo(); 2375 setFilterInfo(fi); 2376 2377 if (smsSelected(fi, ap) && folderElement.hasSmsMmsContent()) { 2378 fi.mMsgType = FilterInfo.TYPE_SMS; 2379 String where = setWhereFilterFolderType(folderElement, fi); 2380 where += " AND " + Sms.READ + "=0 "; 2381 where += setWhereFilterPeriod(ap, fi); 2382 Cursor c = mResolver.query(Sms.CONTENT_URI, 2383 SMS_PROJECTION, where, null, Sms.DATE + " DESC"); 2384 try { 2385 if (c != null) { 2386 cnt = c.getCount(); 2387 } 2388 } finally { 2389 if (c != null) c.close(); 2390 } 2391 } 2392 2393 if (mmsSelected(ap) && folderElement.hasSmsMmsContent()) { 2394 fi.mMsgType = FilterInfo.TYPE_MMS; 2395 String where = setWhereFilterFolderType(folderElement, fi); 2396 where += " AND " + Mms.READ + "=0 "; 2397 where += setWhereFilterPeriod(ap, fi); 2398 Cursor c = mResolver.query(Mms.CONTENT_URI, 2399 MMS_PROJECTION, where, null, Sms.DATE + " DESC"); 2400 try { 2401 if (c != null) { 2402 cnt += c.getCount(); 2403 } 2404 } finally { 2405 if (c != null) c.close(); 2406 } 2407 } 2408 2409 2410 if (emailSelected(ap) && folderElement.getFolderId() != -1) { 2411 fi.mMsgType = FilterInfo.TYPE_EMAIL; 2412 String where = setWhereFilterFolderType(folderElement, fi); 2413 if(!where.isEmpty()) { 2414 where += " AND " + BluetoothMapContract.MessageColumns.FLAG_READ + "=0 "; 2415 where += setWhereFilterPeriod(ap, fi); 2416 Uri contentUri = Uri.parse(mBaseUri + BluetoothMapContract.TABLE_MESSAGE); 2417 Cursor c = mResolver.query(contentUri, BluetoothMapContract.BT_MESSAGE_PROJECTION, 2418 where, null, BluetoothMapContract.MessageColumns.DATE + " DESC"); 2419 try { 2420 if (c != null) { 2421 cnt += c.getCount(); 2422 } 2423 } finally { 2424 if (c != null) c.close(); 2425 } 2426 } 2427 } 2428 2429 if (imSelected(ap) && folderElement.hasImContent()) { 2430 fi.mMsgType = FilterInfo.TYPE_IM; 2431 String where = setWhereFilter(folderElement, fi, ap); 2432 if(!where.isEmpty()) { 2433 where += " AND " + BluetoothMapContract.MessageColumns.FLAG_READ + "=0 "; 2434 where += setWhereFilterPeriod(ap, fi); 2435 Uri contentUri = Uri.parse(mBaseUri + BluetoothMapContract.TABLE_MESSAGE); 2436 Cursor c = mResolver.query(contentUri, 2437 BluetoothMapContract.BT_INSTANT_MESSAGE_PROJECTION, 2438 where, null, BluetoothMapContract.MessageColumns.DATE + " DESC"); 2439 try { 2440 if (c != null) { 2441 cnt += c.getCount(); 2442 } 2443 } finally { 2444 if (c != null) c.close(); 2445 } 2446 } 2447 } 2448 2449 if (D) Log.d(TAG, "msgListingHasUnread: numUnread = " + cnt); 2450 return (cnt>0)?true:false; 2451 } 2452 2453 /** 2454 * Build the conversation listing. 2455 * @param ap The Application Parameters 2456 * @param sizeOnly TRUE: don't populate the list members, only build the list to get the size. 2457 * @return 2458 */ 2459 public BluetoothMapConvoListing convoListing(BluetoothMapAppParams ap, boolean sizeOnly) { 2460 2461 if (D) Log.d(TAG, "convoListing: " + " messageType = " + ap.getFilterMessageType() ); 2462 BluetoothMapConvoListing convoList = new BluetoothMapConvoListing(); 2463 2464 /* We overwrite the parameter mask here if it is 0 or not present, as this 2465 * should cause all parameters to be included in the message list. */ 2466 if(ap.getConvoParameterMask() == BluetoothMapAppParams.INVALID_VALUE_PARAMETER || 2467 ap.getConvoParameterMask() == 0) { 2468 ap.setConvoParameterMask(CONVO_PARAMETER_MASK_DEFAULT); 2469 if (D) Log.v(TAG, "convoListing(): appParameterMask is zero or not present, " + 2470 "changing to default: " + ap.getConvoParameterMask()); 2471 } 2472 2473 /* Possible filters: 2474 * - Recipient name (contacts DB) or id (for SMS/MMS this is the thread-id contact-id) 2475 * - Activity start/begin 2476 * - Read status 2477 * - Thread_id 2478 * The strategy for SMS/MMS 2479 * With no filter on name - use limit and offset. 2480 * With a filter on name - build the complete list of conversations and create a filter 2481 * mechanism 2482 * 2483 * The strategy for IM: 2484 * Join the conversation table with the contacts table in a way that makes it possible to 2485 * get the data needed in a single query. 2486 * Manually handle limit/offset 2487 * */ 2488 2489 /* Cache some info used throughout filtering */ 2490 FilterInfo fi = new FilterInfo(); 2491 setFilterInfo(fi); 2492 Cursor smsMmsCursor = null; 2493 Cursor imEmailCursor = null; 2494 int offsetNum; 2495 if(sizeOnly) { 2496 offsetNum = 0; 2497 } else { 2498 offsetNum = ap.getStartOffset(); 2499 } 2500 // Inverse meaning - hence a 1 is include. 2501 int msgTypesInclude = ((~ap.getFilterMessageType()) 2502 & BluetoothMapAppParams.FILTER_MSG_TYPE_MASK); 2503 int maxThreads = ap.getMaxListCount()+ap.getStartOffset(); 2504 2505 2506 try { 2507 if (smsSelected(fi, ap) || mmsSelected(ap)) { 2508 String limit = ""; 2509 if((sizeOnly == false) && (ap.getMaxListCount()>0) && 2510 (ap.getFilterRecipient()==null)){ 2511 /* We can only use limit if we do not have a contacts filter */ 2512 limit=" LIMIT " + maxThreads; 2513 } 2514 StringBuilder sortOrder = new StringBuilder(Threads.DATE + " DESC"); 2515 if((sizeOnly == false) && 2516 ((msgTypesInclude & ~(BluetoothMapAppParams.FILTER_NO_SMS_GSM | 2517 BluetoothMapAppParams.FILTER_NO_SMS_CDMA) | 2518 BluetoothMapAppParams.FILTER_NO_MMS) == 0) 2519 && ap.getFilterRecipient() == null){ 2520 // SMS/MMS messages only and no recipient filter - use optimization. 2521 limit = " LIMIT " + ap.getMaxListCount()+" OFFSET "+ ap.getStartOffset(); 2522 if(D) Log.d(TAG, "SMS Limit => "+limit); 2523 offsetNum = 0; 2524 } 2525 StringBuilder selection = new StringBuilder(120); // This covers most cases 2526 ArrayList<String> selectionArgs = new ArrayList<String>(12); // Covers all cases 2527 selection.append("1=1 "); // just to simplify building the where-clause 2528 setConvoWhereFilterSmsMms(selection, selectionArgs, fi, ap); 2529 String[] args = null; 2530 if(selectionArgs.size() > 0) { 2531 args = new String[selectionArgs.size()]; 2532 selectionArgs.toArray(args); 2533 } 2534 Uri uri = Threads.CONTENT_URI.buildUpon() 2535 .appendQueryParameter("simple", "true").build(); 2536 sortOrder.append(limit); 2537 if(D) Log.d(TAG, "Query using selection: " + selection.toString() + 2538 " - sortOrder: " + sortOrder.toString()); 2539 // TODO: Optimize: Reduce projection based on convo parameter mask 2540 smsMmsCursor = mResolver.query(uri, MMS_SMS_THREAD_PROJECTION, selection.toString(), 2541 args, sortOrder.toString()); 2542 if (smsMmsCursor != null) { 2543 // store column index so we don't have to look them up anymore (optimization) 2544 if(D) Log.d(TAG, "Found " + smsMmsCursor.getCount() 2545 + " sms/mms conversations."); 2546 BluetoothMapConvoListingElement convoElement = null; 2547 smsMmsCursor.moveToPosition(-1); 2548 if(ap.getFilterRecipient() == null) { 2549 int count = 0; 2550 // We have no Recipient filter, add contacts after the list is reduced 2551 while (smsMmsCursor.moveToNext()) { 2552 convoElement = createConvoElement(smsMmsCursor, fi, ap); 2553 convoList.add(convoElement); 2554 count++; 2555 if(sizeOnly == false && count >= maxThreads) { 2556 break; 2557 } 2558 } 2559 } else { 2560 // We must be able to filter on recipient, add contacts now 2561 SmsMmsContacts contacts = new SmsMmsContacts(); 2562 while (smsMmsCursor.moveToNext()) { 2563 int count = 0; 2564 convoElement = createConvoElement(smsMmsCursor, fi, ap); 2565 String idsStr = 2566 smsMmsCursor.getString(MMS_SMS_THREAD_COL_RECIPIENT_IDS); 2567 // Add elements only if we do find a contact - if not we cannot apply 2568 // the filter, hence the item is irrelevant 2569 // TODO: Perhaps the spec. should be changes to be able to search on 2570 // phone number as well? 2571 if(addSmsMmsContacts(convoElement, contacts, idsStr, 2572 ap.getFilterRecipient(), ap)) { 2573 convoList.add(convoElement); 2574 if(sizeOnly == false && count >= maxThreads) { 2575 break; 2576 } 2577 } 2578 } 2579 } 2580 } 2581 } 2582 2583 if (emailSelected(ap) || imSelected(ap)) { 2584 int count = 0; 2585 if(emailSelected(ap)) { 2586 fi.mMsgType = FilterInfo.TYPE_EMAIL; 2587 } else if(imSelected(ap)) { 2588 fi.mMsgType = FilterInfo.TYPE_IM; 2589 } 2590 if (D) Log.d(TAG, "msgType: " + fi.mMsgType); 2591 Uri contentUri = Uri.parse(mBaseUri + BluetoothMapContract.TABLE_CONVERSATION); 2592 2593 contentUri = appendConvoListQueryParameters(ap, contentUri); 2594 if(V) Log.v(TAG, "URI with parameters: " + contentUri.toString()); 2595 // TODO: Optimize: Reduce projection based on convo parameter mask 2596 imEmailCursor = mResolver.query(contentUri, 2597 BluetoothMapContract.BT_CONVERSATION_PROJECTION, 2598 null, null, BluetoothMapContract.ConversationColumns.LAST_THREAD_ACTIVITY 2599 + " DESC, " + BluetoothMapContract.ConversationColumns.THREAD_ID 2600 + " ASC"); 2601 if (imEmailCursor != null) { 2602 BluetoothMapConvoListingElement e = null; 2603 // store column index so we don't have to look them up anymore (optimization) 2604 // Here we rely on only a single account-based message type for each MAS. 2605 fi.setEmailImConvoColumns(imEmailCursor); 2606 boolean isValid = imEmailCursor.moveToNext(); 2607 if(D) Log.d(TAG, "Found " + imEmailCursor.getCount() 2608 + " EMAIL/IM conversations. isValid = " + isValid); 2609 while (isValid && ((sizeOnly == true) || (count < maxThreads))) { 2610 long threadId = imEmailCursor.getLong(fi.mConvoColConvoId); 2611 long nextThreadId; 2612 count ++; 2613 e = createConvoElement(imEmailCursor, fi, ap); 2614 convoList.add(e); 2615 2616 do { 2617 nextThreadId = imEmailCursor.getLong(fi.mConvoColConvoId); 2618 if(V) Log.i(TAG, " threadId = " + threadId + " newThreadId = " + 2619 nextThreadId); 2620 // TODO: This seems rather inefficient in the case where we do not need 2621 // to reduce the list. 2622 } while ((nextThreadId == threadId) && 2623 (isValid = imEmailCursor.moveToNext() == true)); 2624 } 2625 } 2626 } 2627 2628 if(D) Log.d(TAG, "Done adding conversations - list size:" + 2629 convoList.getCount()); 2630 2631 // If sizeOnly - we are all done here - return the list as is - no need to populate the 2632 // list. 2633 if(sizeOnly) { 2634 return convoList; 2635 } 2636 2637 /* Enable this if post sorting and segmenting needed */ 2638 /* This is too early */ 2639 convoList.sort(); 2640 convoList.segment(ap.getMaxListCount(), offsetNum); 2641 List<BluetoothMapConvoListingElement> list = convoList.getList(); 2642 int listSize = list.size(); 2643 if(V) Log.i(TAG, "List Size:" + listSize); 2644 Cursor tmpCursor = null; 2645 SmsMmsContacts contacts = new SmsMmsContacts(); 2646 for(int x=0;x<listSize;x++){ 2647 BluetoothMapConvoListingElement ele = list.get(x); 2648 TYPE type = ele.getType(); 2649 switch(type) { 2650 case SMS_CDMA: 2651 case SMS_GSM: 2652 case MMS: { 2653 tmpCursor = null; // SMS/MMS needs special treatment 2654 if(smsMmsCursor != null) { 2655 populateSmsMmsConvoElement(ele, smsMmsCursor, ap, contacts); 2656 } 2657 if(D) fi.mMsgType = FilterInfo.TYPE_IM; 2658 break; 2659 } 2660 case EMAIL: 2661 tmpCursor = imEmailCursor; 2662 fi.mMsgType = FilterInfo.TYPE_EMAIL; 2663 break; 2664 case IM: 2665 tmpCursor = imEmailCursor; 2666 fi.mMsgType = FilterInfo.TYPE_IM; 2667 break; 2668 default: 2669 tmpCursor = null; 2670 break; 2671 } 2672 2673 if(D) Log.d(TAG, "Working on cursor of type " + fi.mMsgType); 2674 2675 if(tmpCursor != null){ 2676 populateImEmailConvoElement(ele, tmpCursor, ap, fi); 2677 }else { 2678 // No, it will be for SMS/MMS at the moment 2679 if(D) Log.d(TAG, "tmpCursor is Null - something is wrong - or the message is" + 2680 " of type SMS/MMS"); 2681 } 2682 } 2683 } finally { 2684 if(imEmailCursor != null)imEmailCursor.close(); 2685 if(smsMmsCursor != null)smsMmsCursor.close(); 2686 if(D)Log.d(TAG, "conversation end"); 2687 } 2688 return convoList; 2689 } 2690 2691 2692 /** 2693 * Refreshes the entire list of SMS/MMS conversation version counters. Use it to generate a 2694 * new ConvoListVersinoCounter in mSmsMmsConvoListVersion 2695 * @return 2696 */ 2697 /* package */ 2698 boolean refreshSmsMmsConvoVersions() { 2699 boolean listChangeDetected = false; 2700 Cursor cursor = null; 2701 Uri uri = Threads.CONTENT_URI.buildUpon() 2702 .appendQueryParameter("simple", "true").build(); 2703 cursor = mResolver.query(uri, MMS_SMS_THREAD_PROJECTION, null, 2704 null, Threads.DATE + " DESC"); 2705 try { 2706 if (cursor != null) { 2707 // store column index so we don't have to look them up anymore (optimization) 2708 if(D) Log.d(TAG, "Found " + cursor.getCount() 2709 + " sms/mms conversations."); 2710 BluetoothMapConvoListingElement convoElement = null; 2711 cursor.moveToPosition(-1); 2712 synchronized (getSmsMmsConvoList()) { 2713 int size = Math.max(getSmsMmsConvoList().size(), cursor.getCount()); 2714 HashMap<Long,BluetoothMapConvoListingElement> newList = 2715 new HashMap<Long,BluetoothMapConvoListingElement>(size); 2716 while (cursor.moveToNext()) { 2717 // TODO: Extract to function, that can be called at listing, which returns 2718 // the versionCounter(existing or new). 2719 boolean convoChanged = false; 2720 Long id = cursor.getLong(MMS_SMS_THREAD_COL_ID); 2721 convoElement = getSmsMmsConvoList().remove(id); 2722 if(convoElement == null) { 2723 // New conversation added 2724 convoElement = new BluetoothMapConvoListingElement(); 2725 convoElement.setConvoId(BluetoothMapUtils.CONVO_ID_TYPE_SMS_MMS, id); 2726 listChangeDetected = true; 2727 convoElement.setVersionCounter(0); 2728 } 2729 // Currently we only need to compare name, last_activity and read_status, and 2730 // name is not used for SMS/MMS. 2731 // msg delete will be handled by update folderVersionCounter(). 2732 long last_activity = cursor.getLong(MMS_SMS_THREAD_COL_DATE); 2733 boolean read = (cursor.getInt(MMS_SMS_THREAD_COL_READ) == 1) ? 2734 true : false; 2735 2736 if(last_activity != convoElement.getLastActivity()) { 2737 convoChanged = true; 2738 convoElement.setLastActivity(last_activity); 2739 } 2740 2741 if(read != convoElement.getReadBool()) { 2742 convoChanged = true; 2743 convoElement.setRead(read, false); 2744 } 2745 2746 String idsStr = cursor.getString(MMS_SMS_THREAD_COL_RECIPIENT_IDS); 2747 if(!idsStr.equals(convoElement.getSmsMmsContacts())) { 2748 // This should not trigger a change in conversationVersionCounter only the 2749 // ConvoListVersionCounter. 2750 listChangeDetected = true; 2751 convoElement.setSmsMmsContacts(idsStr); 2752 } 2753 2754 if(convoChanged) { 2755 listChangeDetected = true; 2756 convoElement.incrementVersionCounter(); 2757 } 2758 newList.put(id, convoElement); 2759 } 2760 // If we still have items on the old list, something was deleted 2761 if(getSmsMmsConvoList().size() != 0) { 2762 listChangeDetected = true; 2763 } 2764 setSmsMmsConvoList(newList); 2765 } 2766 2767 if(listChangeDetected) { 2768 mMasInstance.updateSmsMmsConvoListVersionCounter(); 2769 } 2770 } 2771 } finally { 2772 if(cursor != null) { 2773 cursor.close(); 2774 } 2775 } 2776 return listChangeDetected; 2777 } 2778 2779 /** 2780 * Refreshes the entire list of SMS/MMS conversation version counters. Use it to generate a 2781 * new ConvoListVersinoCounter in mSmsMmsConvoListVersion 2782 * @return 2783 */ 2784 /* package */ 2785 boolean refreshImEmailConvoVersions() { 2786 boolean listChangeDetected = false; 2787 FilterInfo fi = new FilterInfo(); 2788 2789 Uri contentUri = Uri.parse(mBaseUri + BluetoothMapContract.TABLE_CONVERSATION); 2790 2791 if(V) Log.v(TAG, "URI with parameters: " + contentUri.toString()); 2792 Cursor imEmailCursor = mResolver.query(contentUri, 2793 CONVO_VERSION_PROJECTION, 2794 null, null, BluetoothMapContract.ConversationColumns.LAST_THREAD_ACTIVITY 2795 + " DESC, " + BluetoothMapContract.ConversationColumns.THREAD_ID 2796 + " ASC"); 2797 try { 2798 if (imEmailCursor != null) { 2799 BluetoothMapConvoListingElement convoElement = null; 2800 // store column index so we don't have to look them up anymore (optimization) 2801 // Here we rely on only a single account-based message type for each MAS. 2802 fi.setEmailImConvoColumns(imEmailCursor); 2803 boolean isValid = imEmailCursor.moveToNext(); 2804 if(V) Log.d(TAG, "Found " + imEmailCursor.getCount() 2805 + " EMAIL/IM conversations. isValid = " + isValid); 2806 synchronized (getImEmailConvoList()) { 2807 int size = Math.max(getImEmailConvoList().size(), imEmailCursor.getCount()); 2808 boolean convoChanged = false; 2809 HashMap<Long,BluetoothMapConvoListingElement> newList = 2810 new HashMap<Long,BluetoothMapConvoListingElement>(size); 2811 while (isValid) { 2812 long id = imEmailCursor.getLong(fi.mConvoColConvoId); 2813 long nextThreadId; 2814 convoElement = getImEmailConvoList().remove(id); 2815 if(convoElement == null) { 2816 // New conversation added 2817 convoElement = new BluetoothMapConvoListingElement(); 2818 convoElement.setConvoId(BluetoothMapUtils.CONVO_ID_TYPE_EMAIL_IM, id); 2819 listChangeDetected = true; 2820 convoElement.setVersionCounter(0); 2821 } 2822 String name = imEmailCursor.getString(fi.mConvoColName); 2823 String summary = imEmailCursor.getString(fi.mConvoColSummary); 2824 long last_activity = imEmailCursor.getLong(fi.mConvoColLastActivity); 2825 boolean read = (imEmailCursor.getInt(fi.mConvoColRead) == 1) ? 2826 true : false; 2827 2828 if(last_activity != convoElement.getLastActivity()) { 2829 convoChanged = true; 2830 convoElement.setLastActivity(last_activity); 2831 } 2832 2833 if(read != convoElement.getReadBool()) { 2834 convoChanged = true; 2835 convoElement.setRead(read, false); 2836 } 2837 2838 if(name != null && !name.equals(convoElement.getName())) { 2839 convoChanged = true; 2840 convoElement.setName(name); 2841 } 2842 2843 if(summary != null && !summary.equals(convoElement.getFullSummary())) { 2844 convoChanged = true; 2845 convoElement.setSummary(summary); 2846 } 2847 /* If the query returned one row for each contact, skip all the dublicates */ 2848 do { 2849 nextThreadId = imEmailCursor.getLong(fi.mConvoColConvoId); 2850 if(V) Log.i(TAG, " threadId = " + id + " newThreadId = " + 2851 nextThreadId); 2852 } while ((nextThreadId == id) && 2853 (isValid = imEmailCursor.moveToNext() == true)); 2854 2855 if(convoChanged) { 2856 listChangeDetected = true; 2857 convoElement.incrementVersionCounter(); 2858 } 2859 newList.put(id, convoElement); 2860 } 2861 // If we still have items on the old list, something was deleted 2862 if(getImEmailConvoList().size() != 0) { 2863 listChangeDetected = true; 2864 } 2865 setImEmailConvoList(newList); 2866 } 2867 } 2868 } finally { 2869 if(imEmailCursor != null) { 2870 imEmailCursor.close(); 2871 } 2872 } 2873 2874 if(listChangeDetected) { 2875 mMasInstance.updateImEmailConvoListVersionCounter(); 2876 } 2877 return listChangeDetected; 2878 } 2879 2880 /** 2881 * Update the convoVersionCounter within the element passed as parameter. 2882 * This function has the side effect to update the ConvoListVersionCounter if needed. 2883 * This function ignores changes to contacts as this shall not change the convoVersionCounter, 2884 * only the convoListVersion counter, which will be updated upon request. 2885 * @param ele Element to update shall not be null. 2886 */ 2887 private void updateSmsMmsConvoVersion(Cursor cursor, BluetoothMapConvoListingElement ele) { 2888 long id = ele.getCpConvoId(); 2889 BluetoothMapConvoListingElement convoElement = getSmsMmsConvoList().get(id); 2890 boolean listChangeDetected = false; 2891 boolean convoChanged = false; 2892 if(convoElement == null) { 2893 // New conversation added 2894 convoElement = new BluetoothMapConvoListingElement(); 2895 getSmsMmsConvoList().put(id, convoElement); 2896 convoElement.setConvoId(BluetoothMapUtils.CONVO_ID_TYPE_SMS_MMS, id); 2897 listChangeDetected = true; 2898 convoElement.setVersionCounter(0); 2899 } 2900 long last_activity = cursor.getLong(MMS_SMS_THREAD_COL_DATE); 2901 boolean read = (cursor.getInt(MMS_SMS_THREAD_COL_READ) == 1) ? 2902 true : false; 2903 2904 if(last_activity != convoElement.getLastActivity()) { 2905 convoChanged = true; 2906 convoElement.setLastActivity(last_activity); 2907 } 2908 2909 if(read != convoElement.getReadBool()) { 2910 convoChanged = true; 2911 convoElement.setRead(read, false); 2912 } 2913 2914 if(convoChanged) { 2915 listChangeDetected = true; 2916 convoElement.incrementVersionCounter(); 2917 } 2918 if(listChangeDetected) { 2919 mMasInstance.updateSmsMmsConvoListVersionCounter(); 2920 } 2921 ele.setVersionCounter(convoElement.getVersionCounter()); 2922 } 2923 2924 /** 2925 * Update the convoVersionCounter within the element passed as parameter. 2926 * This function has the side effect to update the ConvoListVersionCounter if needed. 2927 * This function ignores changes to contacts as this shall not change the convoVersionCounter, 2928 * only the convoListVersion counter, which will be updated upon request. 2929 * @param ele Element to update shall not be null. 2930 */ 2931 private void updateImEmailConvoVersion(Cursor cursor, FilterInfo fi, 2932 BluetoothMapConvoListingElement ele) { 2933 long id = ele.getCpConvoId(); 2934 BluetoothMapConvoListingElement convoElement = getImEmailConvoList().get(id); 2935 boolean listChangeDetected = false; 2936 boolean convoChanged = false; 2937 if(convoElement == null) { 2938 // New conversation added 2939 if(V) Log.d(TAG, "Added new conversation with ID = " + id); 2940 convoElement = new BluetoothMapConvoListingElement(); 2941 convoElement.setConvoId(BluetoothMapUtils.CONVO_ID_TYPE_EMAIL_IM, id); 2942 getImEmailConvoList().put(id, convoElement); 2943 listChangeDetected = true; 2944 convoElement.setVersionCounter(0); 2945 } 2946 String name = cursor.getString(fi.mConvoColName); 2947 long last_activity = cursor.getLong(fi.mConvoColLastActivity); 2948 boolean read = (cursor.getInt(fi.mConvoColRead) == 1) ? 2949 true : false; 2950 2951 if(last_activity != convoElement.getLastActivity()) { 2952 convoChanged = true; 2953 convoElement.setLastActivity(last_activity); 2954 } 2955 2956 if(read != convoElement.getReadBool()) { 2957 convoChanged = true; 2958 convoElement.setRead(read, false); 2959 } 2960 2961 if(name != null && !name.equals(convoElement.getName())) { 2962 convoChanged = true; 2963 convoElement.setName(name); 2964 } 2965 2966 if(convoChanged) { 2967 listChangeDetected = true; 2968 if(V) Log.d(TAG, "conversation with ID = " + id + " changed"); 2969 convoElement.incrementVersionCounter(); 2970 } 2971 if(listChangeDetected) { 2972 mMasInstance.updateImEmailConvoListVersionCounter(); 2973 } 2974 ele.setVersionCounter(convoElement.getVersionCounter()); 2975 } 2976 2977 /** 2978 * @param ele 2979 * @param smsMmsCursor 2980 * @param ap 2981 * @param contacts 2982 */ 2983 private void populateSmsMmsConvoElement(BluetoothMapConvoListingElement ele, 2984 Cursor smsMmsCursor, BluetoothMapAppParams ap, 2985 SmsMmsContacts contacts) { 2986 smsMmsCursor.moveToPosition(ele.getCursorIndex()); 2987 // TODO: If we ever get beyond 31 bit, change to long 2988 int parameterMask = (int) ap.getConvoParameterMask(); // We always set a default value 2989 2990 // TODO: How to determine whether the convo-IDs can be used across message 2991 // types? 2992 ele.setConvoId(BluetoothMapUtils.CONVO_ID_TYPE_SMS_MMS, 2993 smsMmsCursor.getLong(MMS_SMS_THREAD_COL_ID)); 2994 2995 boolean read = (smsMmsCursor.getInt(MMS_SMS_THREAD_COL_READ) == 1) ? 2996 true : false; 2997 if((parameterMask & CONVO_PARAM_MASK_CONVO_READ_STATUS) != 0) { 2998 ele.setRead(read, true); 2999 } else { 3000 ele.setRead(read, false); 3001 } 3002 3003 if((parameterMask & CONVO_PARAM_MASK_CONVO_LAST_ACTIVITY) != 0) { 3004 long timeStamp = smsMmsCursor.getLong(MMS_SMS_THREAD_COL_DATE); 3005 ele.setLastActivity(timeStamp); 3006 } else { 3007 // We need to delete the time stamp, if it was added for multi msg-type 3008 ele.setLastActivity(-1); 3009 } 3010 3011 if((parameterMask & CONVO_PARAM_MASK_CONVO_VERSION_COUNTER) != 0) { 3012 updateSmsMmsConvoVersion(smsMmsCursor, ele); 3013 } 3014 3015 if((parameterMask & CONVO_PARAM_MASK_CONVO_NAME) != 0) { 3016 ele.setName(""); // We never have a thread name for SMS/MMS 3017 } 3018 3019 if((parameterMask & CONVO_PARAM_MASK_CONVO_SUMMARY) != 0) { 3020 String summary = smsMmsCursor.getString(MMS_SMS_THREAD_COL_SNIPPET); 3021 String cs = smsMmsCursor.getString(MMS_SMS_THREAD_COL_SNIPPET_CS); 3022 if(summary != null && cs != null && !cs.equals("UTF-8")) { 3023 try { 3024 // TODO: Not sure this is how to convert to UTF-8 3025 summary = new String(summary.getBytes(cs),"UTF-8"); 3026 } catch (UnsupportedEncodingException e){/*Cannot happen*/} 3027 } 3028 ele.setSummary(summary); 3029 } 3030 3031 if((parameterMask & CONVO_PARAM_MASK_PARTTICIPANTS) != 0) { 3032 if(ap.getFilterRecipient() == null) { 3033 // Add contacts only if not already added 3034 String idsStr = 3035 smsMmsCursor.getString(MMS_SMS_THREAD_COL_RECIPIENT_IDS); 3036 addSmsMmsContacts(ele, contacts, idsStr, null, ap); 3037 } 3038 } 3039 } 3040 3041 /** 3042 * @param ele 3043 * @param tmpCursor 3044 * @param fi 3045 */ 3046 private void populateImEmailConvoElement( BluetoothMapConvoListingElement ele, 3047 Cursor tmpCursor, BluetoothMapAppParams ap, FilterInfo fi) { 3048 tmpCursor.moveToPosition(ele.getCursorIndex()); 3049 // TODO: If we ever get beyond 31 bit, change to long 3050 int parameterMask = (int) ap.getConvoParameterMask(); // We always set a default value 3051 long threadId = tmpCursor.getLong(fi.mConvoColConvoId); 3052 3053 // Mandatory field 3054 ele.setConvoId(BluetoothMapUtils.CONVO_ID_TYPE_EMAIL_IM, threadId); 3055 3056 if((parameterMask & CONVO_PARAM_MASK_CONVO_NAME) != 0) { 3057 ele.setName(tmpCursor.getString(fi.mConvoColName)); 3058 } 3059 3060 boolean reportRead = false; 3061 if((parameterMask & CONVO_PARAM_MASK_CONVO_READ_STATUS) != 0) { 3062 reportRead = true; 3063 } 3064 ele.setRead(((1==tmpCursor.getInt(fi.mConvoColRead))?true:false), reportRead); 3065 3066 long timestamp = tmpCursor.getLong(fi.mConvoColLastActivity); 3067 if((parameterMask & CONVO_PARAM_MASK_CONVO_LAST_ACTIVITY) != 0) { 3068 ele.setLastActivity(timestamp); 3069 } else { 3070 // We need to delete the time stamp, if it was added for multi msg-type 3071 ele.setLastActivity(-1); 3072 } 3073 3074 3075 if((parameterMask & CONVO_PARAM_MASK_CONVO_VERSION_COUNTER) != 0) { 3076 updateImEmailConvoVersion(tmpCursor, fi, ele); 3077 } 3078 if((parameterMask & CONVO_PARAM_MASK_CONVO_SUMMARY) != 0) { 3079 ele.setSummary(tmpCursor.getString(fi.mConvoColSummary)); 3080 } 3081 // TODO: For optimization, we could avoid joining the contact and convo tables 3082 // if we have no filter nor this bit is set. 3083 if((parameterMask & CONVO_PARAM_MASK_PARTTICIPANTS) != 0) { 3084 do { 3085 BluetoothMapConvoContactElement c = new BluetoothMapConvoContactElement(); 3086 if((parameterMask & CONVO_PARAM_MASK_PART_X_BT_UID) != 0) { 3087 c.setBtUid(new SignedLongLong(tmpCursor.getLong(fi.mContactColBtUid),0)); 3088 } 3089 if((parameterMask & CONVO_PARAM_MASK_PART_CHAT_STATE) != 0) { 3090 c.setChatState(tmpCursor.getInt(fi.mContactColChatState)); 3091 } 3092 if((parameterMask & CONVO_PARAM_MASK_PART_PRESENCE) != 0) { 3093 c.setPresenceAvailability(tmpCursor.getInt(fi.mContactColPresenceState)); 3094 } 3095 if((parameterMask & CONVO_PARAM_MASK_PART_PRESENCE_TEXT) != 0) { 3096 c.setPresenceStatus(tmpCursor.getString(fi.mContactColPresenceText)); 3097 } 3098 if((parameterMask & CONVO_PARAM_MASK_PART_PRIORITY) != 0) { 3099 c.setPriority(tmpCursor.getInt(fi.mContactColPriority)); 3100 } 3101 if((parameterMask & CONVO_PARAM_MASK_PART_DISP_NAME) != 0) { 3102 c.setDisplayName(tmpCursor.getString(fi.mContactColNickname)); 3103 } 3104 if((parameterMask & CONVO_PARAM_MASK_PART_UCI) != 0) { 3105 c.setContactId(tmpCursor.getString(fi.mContactColContactUci)); 3106 } 3107 if((parameterMask & CONVO_PARAM_MASK_PART_LAST_ACTIVITY) != 0) { 3108 c.setLastActivity(tmpCursor.getLong(fi.mContactColLastActive)); 3109 } 3110 if((parameterMask & CONVO_PARAM_MASK_PART_NAME) != 0) { 3111 c.setName(tmpCursor.getString(fi.mContactColName)); 3112 } 3113 ele.addContact(c); 3114 } while (tmpCursor.moveToNext() == true 3115 && tmpCursor.getLong(fi.mConvoColConvoId) == threadId); 3116 } 3117 } 3118 3119 /** 3120 * Extract the ConvoList parameters from appParams and build the matching URI with 3121 * query parameters. 3122 * @param ap the appParams from the request 3123 * @param contentUri the URI to append parameters to 3124 * @return the new URI with the appended parameters (if any) 3125 */ 3126 private Uri appendConvoListQueryParameters(BluetoothMapAppParams ap, 3127 Uri contentUri) { 3128 Builder newUri = contentUri.buildUpon(); 3129 String str = ap.getFilterRecipient(); 3130 if(str != null) { 3131 str = str.trim(); 3132 str = str.replace("*", "%"); 3133 newUri.appendQueryParameter(BluetoothMapContract.FILTER_ORIGINATOR_SUBSTRING, str); 3134 } 3135 long time = ap.getFilterLastActivityBegin(); 3136 if(time > 0) { 3137 newUri.appendQueryParameter(BluetoothMapContract.FILTER_PERIOD_BEGIN, 3138 Long.toString(time)); 3139 } 3140 time = ap.getFilterLastActivityEnd(); 3141 if(time > 0) { 3142 newUri.appendQueryParameter(BluetoothMapContract.FILTER_PERIOD_END, 3143 Long.toString(time)); 3144 } 3145 int readStatus = ap.getFilterReadStatus(); 3146 if(readStatus > 0) { 3147 if(readStatus == 1) { 3148 // Conversations with Unread messages only 3149 newUri.appendQueryParameter(BluetoothMapContract.FILTER_READ_STATUS, 3150 "false"); 3151 }else if(readStatus == 2) { 3152 // Conversations with all read messages only 3153 newUri.appendQueryParameter(BluetoothMapContract.FILTER_READ_STATUS, 3154 "true"); 3155 } 3156 // if both are set it will be the same as requesting an empty list, but 3157 // as it makes no sense with such a structure in a bit mask, we treat 3158 // requesting both the same as no filtering. 3159 } 3160 long convoId = -1; 3161 if(ap.getFilterConvoId() != null) { 3162 convoId = ap.getFilterConvoId().getLeastSignificantBits(); 3163 } 3164 if(convoId > 0) { 3165 newUri.appendQueryParameter(BluetoothMapContract.FILTER_THREAD_ID, 3166 Long.toString(convoId)); 3167 } 3168 return newUri.build(); 3169 } 3170 3171 /** 3172 * Procedure if we have a filter: 3173 * - loop through all ids to examine if there is a match (this will build the cache) 3174 * - If there is a match loop again to add all contacts. 3175 * 3176 * Procedure if we don't have a filter 3177 * - Add all contacts 3178 * 3179 * @param convoElement 3180 * @param contacts 3181 * @param idsStr 3182 * @param recipientFilter 3183 * @return 3184 */ 3185 private boolean addSmsMmsContacts( BluetoothMapConvoListingElement convoElement, 3186 SmsMmsContacts contacts, String idsStr, String recipientFilter, 3187 BluetoothMapAppParams ap) { 3188 BluetoothMapConvoContactElement contactElement; 3189 int parameterMask = (int) ap.getConvoParameterMask(); // We always set a default value 3190 boolean foundContact = false; 3191 String[] ids = idsStr.split(" "); 3192 long[] longIds = new long[ids.length]; 3193 if(recipientFilter != null) { 3194 recipientFilter = recipientFilter.trim(); 3195 } 3196 3197 for (int i = 0; i < ids.length; i++) { 3198 long longId; 3199 try { 3200 longId = Long.parseLong(ids[i]); 3201 longIds[i] = longId; 3202 if(recipientFilter == null) { 3203 // If there is not filter, all we need to do is to parse the ids 3204 foundContact = true; 3205 continue; 3206 } 3207 String addr = contacts.getPhoneNumber(mResolver, longId); 3208 if(addr == null) { 3209 // This can only happen if all messages from a contact is deleted while 3210 // performing the query. 3211 continue; 3212 } 3213 MapContact contact = 3214 contacts.getContactNameFromPhone(addr, mResolver, recipientFilter); 3215 if(D) { 3216 Log.d(TAG, " id " + longId + ": " + addr); 3217 if(contact != null) { 3218 Log.d(TAG," contact name: " + contact.getName() + " X-BT-UID: " 3219 + contact.getXBtUid()); 3220 } 3221 } 3222 if(contact == null) { 3223 continue; 3224 } 3225 foundContact = true; 3226 } catch (NumberFormatException ex) { 3227 // skip this id 3228 continue; 3229 } 3230 } 3231 3232 if(foundContact == true) { 3233 foundContact = false; 3234 for (long id : longIds) { 3235 String addr = contacts.getPhoneNumber(mResolver, id); 3236 if(addr == null) { 3237 // This can only happen if all messages from a contact is deleted while 3238 // performing the query. 3239 continue; 3240 } 3241 foundContact = true; 3242 MapContact contact = contacts.getContactNameFromPhone(addr, mResolver); 3243 3244 if(contact == null) { 3245 // We do not have a contact, we need to manually add one 3246 contactElement = new BluetoothMapConvoContactElement(); 3247 if((parameterMask & CONVO_PARAM_MASK_PART_NAME) != 0) { 3248 contactElement.setName(addr); // Use the phone number as name 3249 } 3250 if((parameterMask & CONVO_PARAM_MASK_PART_UCI) != 0) { 3251 contactElement.setContactId(addr); 3252 } 3253 } else { 3254 contactElement = BluetoothMapConvoContactElement 3255 .createFromMapContact(contact, addr); 3256 // Remove the parameters not to be reported 3257 if((parameterMask & CONVO_PARAM_MASK_PART_UCI) == 0) { 3258 contactElement.setContactId(null); 3259 } 3260 if((parameterMask & CONVO_PARAM_MASK_PART_X_BT_UID) == 0) { 3261 contactElement.setBtUid(null); 3262 } 3263 if((parameterMask & CONVO_PARAM_MASK_PART_DISP_NAME) == 0) { 3264 contactElement.setDisplayName(null); 3265 } 3266 } 3267 convoElement.addContact(contactElement); 3268 } 3269 } 3270 return foundContact; 3271 } 3272 3273 /** 3274 * Get the folder name of an SMS message or MMS message. 3275 * @param c the cursor pointing at the message 3276 * @return the folder name. 3277 */ 3278 private String getFolderName(int type, int threadId) { 3279 3280 if(threadId == -1) 3281 return BluetoothMapContract.FOLDER_NAME_DELETED; 3282 3283 switch(type) { 3284 case 1: 3285 return BluetoothMapContract.FOLDER_NAME_INBOX; 3286 case 2: 3287 return BluetoothMapContract.FOLDER_NAME_SENT; 3288 case 3: 3289 return BluetoothMapContract.FOLDER_NAME_DRAFT; 3290 case 4: // Just name outbox, failed and queued "outbox" 3291 case 5: 3292 case 6: 3293 return BluetoothMapContract.FOLDER_NAME_OUTBOX; 3294 } 3295 return ""; 3296 } 3297 3298 public byte[] getMessage(String handle, BluetoothMapAppParams appParams, 3299 BluetoothMapFolderElement folderElement, String version) 3300 throws UnsupportedEncodingException{ 3301 TYPE type = BluetoothMapUtils.getMsgTypeFromHandle(handle); 3302 mMessageVersion = version; 3303 long id = BluetoothMapUtils.getCpHandle(handle); 3304 if(appParams.getFractionRequest() == BluetoothMapAppParams.FRACTION_REQUEST_NEXT) { 3305 throw new IllegalArgumentException("FRACTION_REQUEST_NEXT does not make sence as" + 3306 " we always return the full message."); 3307 } 3308 switch(type) { 3309 case SMS_GSM: 3310 case SMS_CDMA: 3311 return getSmsMessage(id, appParams.getCharset()); 3312 case MMS: 3313 return getMmsMessage(id, appParams); 3314 case EMAIL: 3315 return getEmailMessage(id, appParams, folderElement); 3316 case IM: 3317 return getIMMessage(id, appParams, folderElement); 3318 } 3319 throw new IllegalArgumentException("Invalid message handle."); 3320 } 3321 3322 private String setVCardFromPhoneNumber(BluetoothMapbMessage message, 3323 String phone, boolean incoming) { 3324 String contactId = null, contactName = null; 3325 String[] phoneNumbers = new String[1]; 3326 //Handle possible exception for empty phone address 3327 if (TextUtils.isEmpty(phone)) { 3328 return contactName; 3329 } 3330 // 3331 // Use only actual phone number, because the MCE cannot know which 3332 // number the message is from. 3333 // 3334 phoneNumbers[0] = phone; 3335 String[] emailAddresses = null; 3336 Cursor p; 3337 3338 Uri uri = Uri 3339 .withAppendedPath(PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI, 3340 Uri.encode(phone)); 3341 3342 String[] projection = {Contacts._ID, Contacts.DISPLAY_NAME}; 3343 String selection = Contacts.IN_VISIBLE_GROUP + "=1"; 3344 String orderBy = Contacts._ID + " ASC"; 3345 3346 // Get the contact _ID and name 3347 p = mResolver.query(uri, projection, selection, null, orderBy); 3348 try { 3349 if (p != null && p.moveToFirst()) { 3350 contactId = p.getString(p.getColumnIndex(Contacts._ID)); 3351 contactName = p.getString(p.getColumnIndex(Contacts.DISPLAY_NAME)); 3352 } 3353 } finally { 3354 close(p); 3355 } 3356 // Bail out if we are unable to find a contact, based on the phone number 3357 if (contactId != null) { 3358 Cursor q = null; 3359 // Fetch the contact e-mail addresses 3360 try { 3361 q = mResolver.query(ContactsContract.CommonDataKinds.Email.CONTENT_URI, null, 3362 ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?", 3363 new String[]{contactId}, 3364 null); 3365 if (q != null && q.moveToFirst()) { 3366 int i = 0; 3367 emailAddresses = new String[q.getCount()]; 3368 do { 3369 String emailAddress = q.getString(q.getColumnIndex( 3370 ContactsContract.CommonDataKinds.Email.ADDRESS)); 3371 emailAddresses[i++] = emailAddress; 3372 } while (q != null && q.moveToNext()); 3373 } 3374 } finally { 3375 close(q); 3376 } 3377 } 3378 3379 if (incoming == true) { 3380 if(V) Log.d(TAG, "Adding originator for phone:" + phone); 3381 // Use version 3.0 as we only have a formatted name 3382 message.addOriginator(contactName, contactName, phoneNumbers, emailAddresses,null,null); 3383 } else { 3384 if(V) Log.d(TAG, "Adding recipient for phone:" + phone); 3385 // Use version 3.0 as we only have a formatted name 3386 message.addRecipient(contactName, contactName, phoneNumbers, emailAddresses,null,null); 3387 } 3388 return contactName; 3389 } 3390 3391 public static final int MAP_MESSAGE_CHARSET_NATIVE = 0; 3392 public static final int MAP_MESSAGE_CHARSET_UTF8 = 1; 3393 3394 public byte[] getSmsMessage(long id, int charset) throws UnsupportedEncodingException{ 3395 int type, threadId; 3396 long time = -1; 3397 String msgBody; 3398 BluetoothMapbMessageSms message = new BluetoothMapbMessageSms(); 3399 TelephonyManager tm = (TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE); 3400 3401 Cursor c = mResolver.query(Sms.CONTENT_URI, SMS_PROJECTION, "_ID = " + id, null, null); 3402 if (c == null || !c.moveToFirst()) { 3403 throw new IllegalArgumentException("SMS handle not found"); 3404 } 3405 3406 try{ 3407 if(c != null && c.moveToFirst()) 3408 { 3409 if(V) Log.v(TAG,"c.count: " + c.getCount()); 3410 3411 if (tm.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM) { 3412 message.setType(TYPE.SMS_GSM); 3413 } else if (tm.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) { 3414 message.setType(TYPE.SMS_CDMA); 3415 } 3416 message.setVersionString(mMessageVersion); 3417 String read = c.getString(c.getColumnIndex(Sms.READ)); 3418 if (read.equalsIgnoreCase("1")) 3419 message.setStatus(true); 3420 else 3421 message.setStatus(false); 3422 3423 type = c.getInt(c.getColumnIndex(Sms.TYPE)); 3424 threadId = c.getInt(c.getColumnIndex(Sms.THREAD_ID)); 3425 message.setFolder(getFolderName(type, threadId)); 3426 3427 msgBody = c.getString(c.getColumnIndex(Sms.BODY)); 3428 3429 String phone = c.getString(c.getColumnIndex(Sms.ADDRESS)); 3430 if ((phone == null) && type == Sms.MESSAGE_TYPE_DRAFT) { 3431 //Fetch address for Drafts folder from "canonical_address" table 3432 phone = getCanonicalAddressSms(mResolver, threadId); 3433 } 3434 time = c.getLong(c.getColumnIndex(Sms.DATE)); 3435 if(type == 1) // Inbox message needs to set the vCard as originator 3436 setVCardFromPhoneNumber(message, phone, true); 3437 else // Other messages sets the vCard as the recipient 3438 setVCardFromPhoneNumber(message, phone, false); 3439 3440 if(charset == MAP_MESSAGE_CHARSET_NATIVE) { 3441 if(type == 1) //Inbox 3442 message.setSmsBodyPdus(BluetoothMapSmsPdu.getDeliverPdus(msgBody, 3443 phone, time)); 3444 else 3445 message.setSmsBodyPdus(BluetoothMapSmsPdu.getSubmitPdus(msgBody, phone)); 3446 } else /*if (charset == MAP_MESSAGE_CHARSET_UTF8)*/ { 3447 message.setSmsBody(msgBody); 3448 } 3449 return message.encode(); 3450 } 3451 } finally { 3452 if (c != null) c.close(); 3453 } 3454 3455 return message.encode(); 3456 } 3457 3458 private void extractMmsAddresses(long id, BluetoothMapbMessageMime message) { 3459 final String[] projection = null; 3460 String selection = new String(Mms.Addr.MSG_ID + "=" + id); 3461 String uriStr = new String(Mms.CONTENT_URI + "/" + id + "/addr"); 3462 Uri uriAddress = Uri.parse(uriStr); 3463 String contactName = null; 3464 3465 Cursor c = mResolver.query( uriAddress, projection, selection, null, null); 3466 try { 3467 if (c.moveToFirst()) { 3468 do { 3469 String address = c.getString(c.getColumnIndex(Mms.Addr.ADDRESS)); 3470 if(address.equals(INSERT_ADDRES_TOKEN)) 3471 continue; 3472 Integer type = c.getInt(c.getColumnIndex(Mms.Addr.TYPE)); 3473 switch(type) { 3474 case MMS_FROM: 3475 contactName = setVCardFromPhoneNumber(message, address, true); 3476 message.addFrom(contactName, address); 3477 break; 3478 case MMS_TO: 3479 contactName = setVCardFromPhoneNumber(message, address, false); 3480 message.addTo(contactName, address); 3481 break; 3482 case MMS_CC: 3483 contactName = setVCardFromPhoneNumber(message, address, false); 3484 message.addCc(contactName, address); 3485 break; 3486 case MMS_BCC: 3487 contactName = setVCardFromPhoneNumber(message, address, false); 3488 message.addBcc(contactName, address); 3489 break; 3490 default: 3491 break; 3492 } 3493 } while(c.moveToNext()); 3494 } 3495 } finally { 3496 if (c != null) c.close(); 3497 } 3498 } 3499 3500 3501 /** 3502 * Read out a mime data part and return the data in a byte array. 3503 * @param contentPartUri TODO 3504 * @param partid the content provider id of the Mime Part. 3505 * @return 3506 */ 3507 private byte[] readRawDataPart(Uri contentPartUri, long partid) { 3508 String uriStr = new String(contentPartUri+"/"+ partid); 3509 Uri uriAddress = Uri.parse(uriStr); 3510 InputStream is = null; 3511 ByteArrayOutputStream os = new ByteArrayOutputStream(); 3512 int bufferSize = 8192; 3513 byte[] buffer = new byte[bufferSize]; 3514 byte[] retVal = null; 3515 3516 try { 3517 is = mResolver.openInputStream(uriAddress); 3518 int len = 0; 3519 while ((len = is.read(buffer)) != -1) { 3520 os.write(buffer, 0, len); // We need to specify the len, as it can be != bufferSize 3521 } 3522 retVal = os.toByteArray(); 3523 } catch (IOException e) { 3524 // do nothing for now 3525 Log.w(TAG,"Error reading part data",e); 3526 } finally { 3527 close(os); 3528 close(is); 3529 } 3530 return retVal; 3531 } 3532 3533 /** 3534 * Read out the mms parts and update the bMessage object provided i {@linkplain message} 3535 * @param id the content provider ID of the message 3536 * @param message the bMessage object to add the information to 3537 */ 3538 private void extractMmsParts(long id, BluetoothMapbMessageMime message) 3539 { 3540 /* Handling of filtering out non-text parts for exclude 3541 * attachments is handled within the bMessage object. */ 3542 final String[] projection = null; 3543 String selection = new String(Mms.Part.MSG_ID + "=" + id); 3544 String uriStr = new String(Mms.CONTENT_URI + "/"+ id + "/part"); 3545 Uri uriAddress = Uri.parse(uriStr); 3546 BluetoothMapbMessageMime.MimePart part; 3547 Cursor c = mResolver.query(uriAddress, projection, selection, null, null); 3548 try { 3549 if (c.moveToFirst()) { 3550 do { 3551 Long partId = c.getLong(c.getColumnIndex(BaseColumns._ID)); 3552 String contentType = c.getString(c.getColumnIndex(Mms.Part.CONTENT_TYPE)); 3553 String name = c.getString(c.getColumnIndex(Mms.Part.NAME)); 3554 String charset = c.getString(c.getColumnIndex(Mms.Part.CHARSET)); 3555 String filename = c.getString(c.getColumnIndex(Mms.Part.FILENAME)); 3556 String text = c.getString(c.getColumnIndex(Mms.Part.TEXT)); 3557 Integer fd = c.getInt(c.getColumnIndex(Mms.Part._DATA)); 3558 String cid = c.getString(c.getColumnIndex(Mms.Part.CONTENT_ID)); 3559 String cl = c.getString(c.getColumnIndex(Mms.Part.CONTENT_LOCATION)); 3560 String cdisp = c.getString(c.getColumnIndex(Mms.Part.CONTENT_DISPOSITION)); 3561 3562 if(V) Log.d(TAG, " _id : " + partId + 3563 "\n ct : " + contentType + 3564 "\n partname : " + name + 3565 "\n charset : " + charset + 3566 "\n filename : " + filename + 3567 "\n text : " + text + 3568 "\n fd : " + fd + 3569 "\n cid : " + cid + 3570 "\n cl : " + cl + 3571 "\n cdisp : " + cdisp); 3572 3573 part = message.addMimePart(); 3574 part.mContentType = contentType; 3575 part.mPartName = name; 3576 part.mContentId = cid; 3577 part.mContentLocation = cl; 3578 part.mContentDisposition = cdisp; 3579 3580 try { 3581 if(text != null) { 3582 part.mData = text.getBytes("UTF-8"); 3583 part.mCharsetName = "utf-8"; 3584 } else { 3585 part.mData = 3586 readRawDataPart(Uri.parse(Mms.CONTENT_URI+"/part"), partId); 3587 if(charset != null) { 3588 part.mCharsetName = 3589 CharacterSets.getMimeName(Integer.parseInt(charset)); 3590 } 3591 } 3592 } catch (NumberFormatException e) { 3593 Log.d(TAG,"extractMmsParts",e); 3594 part.mData = null; 3595 part.mCharsetName = null; 3596 } catch (UnsupportedEncodingException e) { 3597 Log.d(TAG,"extractMmsParts",e); 3598 part.mData = null; 3599 part.mCharsetName = null; 3600 } finally { 3601 } 3602 part.mFileName = filename; 3603 } while(c.moveToNext()); 3604 message.updateCharset(); 3605 } 3606 3607 } finally { 3608 if(c != null) c.close(); 3609 } 3610 } 3611 /** 3612 * Read out the mms parts and update the bMessage object provided i {@linkplain message} 3613 * @param id the content provider ID of the message 3614 * @param message the bMessage object to add the information to 3615 */ 3616 private void extractIMParts(long id, BluetoothMapbMessageMime message) 3617 { 3618 /* Handling of filtering out non-text parts for exclude 3619 * attachments is handled within the bMessage object. */ 3620 final String[] projection = null; 3621 String selection = new String(BluetoothMapContract.MessageColumns._ID + "=" + id); 3622 String uriStr = new String(mBaseUri 3623 + BluetoothMapContract.TABLE_MESSAGE + "/"+ id + "/part"); 3624 Uri uriAddress = Uri.parse(uriStr); 3625 BluetoothMapbMessageMime.MimePart part; 3626 Cursor c = mResolver.query(uriAddress, projection, selection, null, null); 3627 try{ 3628 if (c.moveToFirst()) { 3629 do { 3630 Long partId = c.getLong( 3631 c.getColumnIndex(BluetoothMapContract.MessagePartColumns._ID)); 3632 String charset = c.getString( 3633 c.getColumnIndex(BluetoothMapContract.MessagePartColumns.CHARSET)); 3634 String filename = c.getString( 3635 c.getColumnIndex(BluetoothMapContract.MessagePartColumns.FILENAME)); 3636 String text = c.getString( 3637 c.getColumnIndex(BluetoothMapContract.MessagePartColumns.TEXT)); 3638 String body = c.getString( 3639 c.getColumnIndex(BluetoothMapContract.MessagePartColumns.RAW_DATA)); 3640 String cid = c.getString( 3641 c.getColumnIndex(BluetoothMapContract.MessagePartColumns.CONTENT_ID)); 3642 3643 if(V) Log.d(TAG, " _id : " + partId + 3644 "\n charset : " + charset + 3645 "\n filename : " + filename + 3646 "\n text : " + text + 3647 "\n cid : " + cid); 3648 3649 part = message.addMimePart(); 3650 part.mContentId = cid; 3651 try { 3652 if(text.equalsIgnoreCase("yes")) { 3653 part.mData = body.getBytes("UTF-8"); 3654 part.mCharsetName = "utf-8"; 3655 } else { 3656 part.mData = readRawDataPart(Uri.parse(mBaseUri 3657 + BluetoothMapContract.TABLE_MESSAGE_PART) , partId); 3658 if(charset != null) 3659 part.mCharsetName = CharacterSets.getMimeName( 3660 Integer.parseInt(charset)); 3661 } 3662 } catch (NumberFormatException e) { 3663 Log.d(TAG,"extractIMParts",e); 3664 part.mData = null; 3665 part.mCharsetName = null; 3666 } catch (UnsupportedEncodingException e) { 3667 Log.d(TAG,"extractIMParts",e); 3668 part.mData = null; 3669 part.mCharsetName = null; 3670 } finally { 3671 } 3672 part.mFileName = filename; 3673 } while(c.moveToNext()); 3674 } 3675 } finally { 3676 if(c != null) c.close(); 3677 } 3678 3679 message.updateCharset(); 3680 } 3681 3682 /** 3683 * 3684 * @param id the content provider id for the message to fetch. 3685 * @param appParams The application parameter object received from the client. 3686 * @return a byte[] containing the utf-8 encoded bMessage to send to the client. 3687 * @throws UnsupportedEncodingException if UTF-8 is not supported, 3688 * which is guaranteed to be supported on an android device 3689 */ 3690 public byte[] getMmsMessage(long id,BluetoothMapAppParams appParams) 3691 throws UnsupportedEncodingException { 3692 int msgBox, threadId; 3693 if (appParams.getCharset() == MAP_MESSAGE_CHARSET_NATIVE) 3694 throw new IllegalArgumentException("MMS charset native not allowed for MMS" 3695 +" - must be utf-8"); 3696 3697 BluetoothMapbMessageMime message = new BluetoothMapbMessageMime(); 3698 Cursor c = mResolver.query(Mms.CONTENT_URI, MMS_PROJECTION, "_ID = " + id, null, null); 3699 try { 3700 if(c != null && c.moveToFirst()) 3701 { 3702 message.setType(TYPE.MMS); 3703 message.setVersionString(mMessageVersion); 3704 3705 // The MMS info: 3706 String read = c.getString(c.getColumnIndex(Mms.READ)); 3707 if (read.equalsIgnoreCase("1")) 3708 message.setStatus(true); 3709 else 3710 message.setStatus(false); 3711 3712 msgBox = c.getInt(c.getColumnIndex(Mms.MESSAGE_BOX)); 3713 threadId = c.getInt(c.getColumnIndex(Mms.THREAD_ID)); 3714 message.setFolder(getFolderName(msgBox, threadId)); 3715 message.setSubject(c.getString(c.getColumnIndex(Mms.SUBJECT))); 3716 message.setMessageId(c.getString(c.getColumnIndex(Mms.MESSAGE_ID))); 3717 message.setContentType(c.getString(c.getColumnIndex(Mms.CONTENT_TYPE))); 3718 message.setDate(c.getLong(c.getColumnIndex(Mms.DATE)) * 1000L); 3719 message.setTextOnly(c.getInt(c.getColumnIndex(Mms.TEXT_ONLY)) == 0 ? false : true); 3720 message.setIncludeAttachments(appParams.getAttachment() == 0 ? false : true); 3721 // c.getLong(c.getColumnIndex(Mms.DATE_SENT)); - this is never used 3722 // c.getInt(c.getColumnIndex(Mms.STATUS)); - don't know what this is 3723 3724 // The parts 3725 extractMmsParts(id, message); 3726 3727 // The addresses 3728 extractMmsAddresses(id, message); 3729 3730 3731 return message.encode(); 3732 } 3733 } finally { 3734 if (c != null) c.close(); 3735 } 3736 3737 return message.encode(); 3738 } 3739 3740 /** 3741 * 3742 * @param id the content provider id for the message to fetch. 3743 * @param appParams The application parameter object received from the client. 3744 * @return a byte[] containing the utf-8 encoded bMessage to send to the client. 3745 * @throws UnsupportedEncodingException if UTF-8 is not supported, 3746 * which is guaranteed to be supported on an android device 3747 */ 3748 public byte[] getEmailMessage(long id, BluetoothMapAppParams appParams, 3749 BluetoothMapFolderElement currentFolder) throws UnsupportedEncodingException { 3750 // Log print out of application parameters set 3751 if(D && appParams != null) { 3752 Log.d(TAG,"TYPE_MESSAGE (GET): Attachment = " + appParams.getAttachment() + 3753 ", Charset = " + appParams.getCharset() + 3754 ", FractionRequest = " + appParams.getFractionRequest()); 3755 } 3756 3757 // Throw exception if requester NATIVE charset for Email 3758 // Exception is caught by MapObexServer sendGetMessageResp 3759 if (appParams.getCharset() == MAP_MESSAGE_CHARSET_NATIVE) 3760 throw new IllegalArgumentException("EMAIL charset not UTF-8"); 3761 3762 BluetoothMapbMessageEmail message = new BluetoothMapbMessageEmail(); 3763 Uri contentUri = Uri.parse(mBaseUri + BluetoothMapContract.TABLE_MESSAGE); 3764 Cursor c = mResolver.query(contentUri, BluetoothMapContract.BT_MESSAGE_PROJECTION, "_ID = " 3765 + id, null, null); 3766 try { 3767 if(c != null && c.moveToFirst()) 3768 { 3769 BluetoothMapFolderElement folderElement; 3770 FileInputStream is = null; 3771 ParcelFileDescriptor fd = null; 3772 try { 3773 // Handle fraction requests 3774 int fractionRequest = appParams.getFractionRequest(); 3775 if (fractionRequest != BluetoothMapAppParams.INVALID_VALUE_PARAMETER) { 3776 // Fraction requested 3777 if(V) { 3778 String fractionStr = (fractionRequest == 0) ? "FIRST" : "NEXT"; 3779 Log.v(TAG, "getEmailMessage - FractionRequest " + fractionStr 3780 + " - send compete message" ); 3781 } 3782 // Check if message is complete and if not - request message from server 3783 if (c.getString(c.getColumnIndex( 3784 BluetoothMapContract.MessageColumns.RECEPTION_STATE)).equalsIgnoreCase( 3785 BluetoothMapContract.RECEPTION_STATE_COMPLETE) == false) { 3786 // TODO: request message from server 3787 Log.w(TAG, "getEmailMessage - receptionState not COMPLETE - Not Implemented!" ); 3788 } 3789 } 3790 // Set read status: 3791 String read = c.getString( 3792 c.getColumnIndex(BluetoothMapContract.MessageColumns.FLAG_READ)); 3793 if (read != null && read.equalsIgnoreCase("1")) 3794 message.setStatus(true); 3795 else 3796 message.setStatus(false); 3797 3798 // Set message type: 3799 message.setType(TYPE.EMAIL); 3800 message.setVersionString(mMessageVersion); 3801 // Set folder: 3802 long folderId = c.getLong( 3803 c.getColumnIndex(BluetoothMapContract.MessageColumns.FOLDER_ID)); 3804 folderElement = currentFolder.getFolderById(folderId); 3805 message.setCompleteFolder(folderElement.getFullPath()); 3806 3807 // Set recipient: 3808 String nameEmail = c.getString( 3809 c.getColumnIndex(BluetoothMapContract.MessageColumns.TO_LIST)); 3810 Rfc822Token tokens[] = Rfc822Tokenizer.tokenize(nameEmail); 3811 if (tokens.length != 0) { 3812 if(D) Log.d(TAG, "Recipient count= " + tokens.length); 3813 int i = 0; 3814 while (i < tokens.length) { 3815 if(V) Log.d(TAG, "Recipient = " + tokens[i].toString()); 3816 String[] emails = new String[1]; 3817 emails[0] = tokens[i].getAddress(); 3818 String name = tokens[i].getName(); 3819 message.addRecipient(name, name, null, emails, null, null); 3820 i++; 3821 } 3822 } 3823 3824 // Set originator: 3825 nameEmail = c.getString(c.getColumnIndex(BluetoothMapContract.MessageColumns.FROM_LIST)); 3826 tokens = Rfc822Tokenizer.tokenize(nameEmail); 3827 if (tokens.length != 0) { 3828 if(D) Log.d(TAG, "Originator count= " + tokens.length); 3829 int i = 0; 3830 while (i < tokens.length) { 3831 if(V) Log.d(TAG, "Originator = " + tokens[i].toString()); 3832 String[] emails = new String[1]; 3833 emails[0] = tokens[i].getAddress(); 3834 String name = tokens[i].getName(); 3835 message.addOriginator(name, name, null, emails, null, null); 3836 i++; 3837 } 3838 } 3839 } finally { 3840 if(c != null) c.close(); 3841 } 3842 // Find out if we get attachments 3843 String attStr = (appParams.getAttachment() == 0) ? 3844 "/" + BluetoothMapContract.FILE_MSG_NO_ATTACHMENTS : ""; 3845 Uri uri = Uri.parse(contentUri + "/" + id + attStr); 3846 3847 // Get email message body content 3848 int count = 0; 3849 try { 3850 fd = mResolver.openFileDescriptor(uri, "r"); 3851 is = new FileInputStream(fd.getFileDescriptor()); 3852 StringBuilder email = new StringBuilder(""); 3853 byte[] buffer = new byte[1024]; 3854 while((count = is.read(buffer)) != -1) { 3855 // TODO: Handle breaks within a UTF8 character 3856 email.append(new String(buffer,0,count)); 3857 if(V) Log.d(TAG, "Email part = " 3858 + new String(buffer,0,count) 3859 + " count=" + count); 3860 } 3861 // Set email message body: 3862 message.setEmailBody(email.toString()); 3863 } catch (FileNotFoundException e) { 3864 Log.w(TAG, e); 3865 } catch (NullPointerException e) { 3866 Log.w(TAG, e); 3867 } catch (IOException e) { 3868 Log.w(TAG, e); 3869 } finally { 3870 try { 3871 if(is != null) is.close(); 3872 } catch (IOException e) {} 3873 try { 3874 if(fd != null) fd.close(); 3875 } catch (IOException e) {} 3876 } 3877 return message.encode(); 3878 } 3879 } finally { 3880 if (c != null) c.close(); 3881 } 3882 throw new IllegalArgumentException("EMAIL handle not found"); 3883 } 3884 /** 3885 * 3886 * @param id the content provider id for the message to fetch. 3887 * @param appParams The application parameter object received from the client. 3888 * @return a byte[] containing the UTF-8 encoded bMessage to send to the client. 3889 * @throws UnsupportedEncodingException if UTF-8 is not supported, 3890 * which is guaranteed to be supported on an android device 3891 */ 3892 3893 /** 3894 * 3895 * @param id the content provider id for the message to fetch. 3896 * @param appParams The application parameter object received from the client. 3897 * @return a byte[] containing the utf-8 encoded bMessage to send to the client. 3898 * @throws UnsupportedEncodingException if UTF-8 is not supported, 3899 * which is guaranteed to be supported on an android device 3900 */ 3901 public byte[] getIMMessage(long id, 3902 BluetoothMapAppParams appParams, 3903 BluetoothMapFolderElement folderElement) 3904 throws UnsupportedEncodingException { 3905 long threadId, folderId; 3906 3907 if (appParams.getCharset() == MAP_MESSAGE_CHARSET_NATIVE) 3908 throw new IllegalArgumentException( 3909 "IM charset native not allowed for IM - must be utf-8"); 3910 3911 BluetoothMapbMessageMime message = new BluetoothMapbMessageMime(); 3912 Uri contentUri = Uri.parse(mBaseUri + BluetoothMapContract.TABLE_MESSAGE); 3913 Cursor c = mResolver.query(contentUri, 3914 BluetoothMapContract.BT_MESSAGE_PROJECTION, "_ID = " + id, null, null); 3915 Cursor contacts = null; 3916 try { 3917 if(c != null && c.moveToFirst()) { 3918 message.setType(TYPE.IM); 3919 message.setVersionString(mMessageVersion); 3920 3921 // The IM message info: 3922 int read = 3923 c.getInt(c.getColumnIndex(BluetoothMapContract.MessageColumns.FLAG_READ)); 3924 if (read == 1) 3925 message.setStatus(true); 3926 else 3927 message.setStatus(false); 3928 3929 threadId = 3930 c.getInt(c.getColumnIndex(BluetoothMapContract.MessageColumns.THREAD_ID)); 3931 folderId = 3932 c.getLong(c.getColumnIndex(BluetoothMapContract.MessageColumns.FOLDER_ID)); 3933 folderElement = folderElement.getFolderById(folderId); 3934 message.setCompleteFolder(folderElement.getFullPath()); 3935 message.setSubject(c.getString( 3936 c.getColumnIndex(BluetoothMapContract.MessageColumns.SUBJECT))); 3937 message.setMessageId(c.getString( 3938 c.getColumnIndex(BluetoothMapContract.MessageColumns._ID))); 3939 message.setDate(c.getLong( 3940 c.getColumnIndex(BluetoothMapContract.MessageColumns.DATE))); 3941 message.setTextOnly(c.getInt(c.getColumnIndex( 3942 BluetoothMapContract.MessageColumns.ATTACHMENT_SIZE)) != 0 ? false : true); 3943 3944 message.setIncludeAttachments(appParams.getAttachment() == 0 ? false : true); 3945 3946 // c.getLong(c.getColumnIndex(Mms.DATE_SENT)); - this is never used 3947 // c.getInt(c.getColumnIndex(Mms.STATUS)); - don't know what this is 3948 3949 // The parts 3950 3951 //FIXME use the parts when ready - until then use the body column for text-only 3952 // extractIMParts(id, message); 3953 //FIXME next few lines are temporary code 3954 MimePart part = message.addMimePart(); 3955 part.mData = c.getString((c.getColumnIndex( 3956 BluetoothMapContract.MessageColumns.BODY))).getBytes("UTF-8"); 3957 part.mCharsetName = "utf-8"; 3958 part.mContentId = "0"; 3959 part.mContentType = "text/plain"; 3960 message.updateCharset(); 3961 // FIXME end temp code 3962 3963 Uri contactsUri = Uri.parse(mBaseUri + BluetoothMapContract.TABLE_CONVOCONTACT); 3964 contacts = mResolver.query(contactsUri, 3965 BluetoothMapContract.BT_CONTACT_PROJECTION, 3966 BluetoothMapContract.ConvoContactColumns.CONVO_ID 3967 + " = " + threadId, null, null); 3968 // TODO this will not work for group-chats 3969 if(contacts != null && contacts.moveToFirst()){ 3970 String name = contacts.getString(contacts.getColumnIndex( 3971 BluetoothMapContract.ConvoContactColumns.NAME)); 3972 String btUid[] = new String[1]; 3973 btUid[0]= contacts.getString(contacts.getColumnIndex( 3974 BluetoothMapContract.ConvoContactColumns.X_BT_UID)); 3975 String nickname = contacts.getString(contacts.getColumnIndex( 3976 BluetoothMapContract.ConvoContactColumns.NICKNAME)); 3977 String btUci[] = new String[1]; 3978 String btOwnUci[] = new String[1]; 3979 btOwnUci[0] = mAccount.getUciFull(); 3980 btUci[0] = contacts.getString(contacts.getColumnIndex( 3981 BluetoothMapContract.ConvoContactColumns.UCI)); 3982 if(folderId == BluetoothMapContract.FOLDER_ID_SENT 3983 || folderId == BluetoothMapContract.FOLDER_ID_OUTBOX) { 3984 message.addRecipient(nickname,name,null, null, btUid, btUci); 3985 message.addOriginator(null, btOwnUci); 3986 3987 }else { 3988 message.addOriginator(nickname,name,null, null, btUid, btUci); 3989 message.addRecipient(null, btOwnUci); 3990 3991 } 3992 } 3993 return message.encode(); 3994 } 3995 } finally { 3996 if(c != null) c.close(); 3997 if(contacts != null) contacts.close(); 3998 } 3999 4000 throw new IllegalArgumentException("IM handle not found"); 4001 } 4002 4003 public void setRemoteFeatureMask(int featureMask){ 4004 this.mRemoteFeatureMask = featureMask; 4005 if(V) Log.d(TAG, "setRemoteFeatureMask"); 4006 if((this.mRemoteFeatureMask & BluetoothMapUtils.MAP_FEATURE_MESSAGE_LISTING_FORMAT_V11_BIT) 4007 == BluetoothMapUtils.MAP_FEATURE_MESSAGE_LISTING_FORMAT_V11_BIT) { 4008 if(V) Log.d(TAG, "setRemoteFeatureMask MAP_MESSAGE_LISTING_FORMAT_V11"); 4009 this.mMsgListingVersion = BluetoothMapUtils.MAP_MESSAGE_LISTING_FORMAT_V11; 4010 } 4011 } 4012 4013 public int getRemoteFeatureMask(){ 4014 return this.mRemoteFeatureMask; 4015 } 4016 4017 HashMap<Long,BluetoothMapConvoListingElement> getSmsMmsConvoList() { 4018 return mMasInstance.getSmsMmsConvoList(); 4019 } 4020 4021 void setSmsMmsConvoList(HashMap<Long,BluetoothMapConvoListingElement> smsMmsConvoList) { 4022 mMasInstance.setSmsMmsConvoList(smsMmsConvoList); 4023 } 4024 4025 HashMap<Long,BluetoothMapConvoListingElement> getImEmailConvoList() { 4026 return mMasInstance.getImEmailConvoList(); 4027 } 4028 4029 void setImEmailConvoList(HashMap<Long,BluetoothMapConvoListingElement> imEmailConvoList) { 4030 mMasInstance.setImEmailConvoList(imEmailConvoList); 4031 } 4032} 4033