BluetoothMapContent.java revision 5006c8597521a7652eafa89a6fb5483b5cb567b6
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.content.ContentResolver; 18import android.content.ContentValues; 19import android.content.Context; 20import android.database.Cursor; 21import android.net.Uri; 22import android.os.Debug; 23import android.os.ParcelFileDescriptor; 24import android.provider.BaseColumns; 25import com.android.bluetooth.mapapi.BluetoothMapContract; 26import com.android.bluetooth.mapapi.BluetoothMapContract.MessageColumns; 27import android.provider.ContactsContract; 28import android.provider.ContactsContract.Contacts; 29import android.provider.ContactsContract.PhoneLookup; 30import android.provider.Telephony.Mms; 31import android.provider.Telephony.Sms; 32import android.telephony.PhoneNumberUtils; 33import android.telephony.TelephonyManager; 34import android.text.util.Rfc822Token; 35import android.text.util.Rfc822Tokenizer; 36import android.util.Log; 37 38import com.android.bluetooth.map.BluetoothMapSmsPdu.SmsPdu; 39import com.android.bluetooth.map.BluetoothMapUtils.TYPE; 40import com.google.android.mms.pdu.CharacterSets; 41import com.google.android.mms.pdu.PduHeaders; 42import com.android.bluetooth.map.BluetoothMapAppParams; 43 44import java.io.ByteArrayOutputStream; 45import java.io.Closeable; 46import java.io.FileInputStream; 47import java.io.FileNotFoundException; 48import java.io.IOException; 49import java.io.InputStream; 50import java.io.UnsupportedEncodingException; 51import java.text.ParseException; 52import java.text.SimpleDateFormat; 53import java.util.ArrayList; 54import java.util.Arrays; 55import java.util.Date; 56import java.util.List; 57 58public class BluetoothMapContent { 59 private static final String TAG = "BluetoothMapContent"; 60 61 private static final boolean D = BluetoothMapService.DEBUG; 62 private static final boolean V = BluetoothMapService.VERBOSE; 63 64 private static final int MASK_SUBJECT = 0x1; 65 private static final int MASK_DATETIME = 0x2; 66 private static final int MASK_SENDER_NAME = 0x4; 67 private static final int MASK_SENDER_ADDRESSING = 0x8; 68 69 private static final int MASK_RECIPIENT_NAME = 0x10; 70 private static final int MASK_RECIPIENT_ADDRESSING = 0x20; 71 private static final int MASK_TYPE = 0x40; 72 private static final int MASK_SIZE = 0x80; 73 74 private static final int MASK_RECEPTION_STATUS = 0x100; 75 private static final int MASK_TEXT = 0x200; 76 private static final int MASK_ATTACHMENT_SIZE = 0x400; 77 private static final int MASK_PRIORITY = 0x800; 78 79 private static final int MASK_READ = 0x1000; 80 private static final int MASK_SENT = 0x2000; 81 private static final int MASK_PROTECTED = 0x4000; 82 private static final int MASK_REPLYTO_ADDRESSING = 0x8000; 83 84 /* Type of MMS address. From Telephony.java it must be one of PduHeaders.BCC, */ 85 /* PduHeaders.CC, PduHeaders.FROM, PduHeaders.TO. These are from PduHeaders.java */ 86 public static final int MMS_FROM = 0x89; 87 public static final int MMS_TO = 0x97; 88 public static final int MMS_BCC = 0x81; 89 public static final int MMS_CC = 0x82; 90 91 public static final String INSERT_ADDRES_TOKEN = "insert-address-token"; 92 93 private Context mContext; 94 private ContentResolver mResolver; 95 private String mBaseEmailUri = null; 96 97 static final String[] SMS_PROJECTION = new String[] { 98 BaseColumns._ID, 99 Sms.THREAD_ID, 100 Sms.ADDRESS, 101 Sms.BODY, 102 Sms.DATE, 103 Sms.READ, 104 Sms.TYPE, 105 Sms.STATUS, 106 Sms.LOCKED, 107 Sms.ERROR_CODE 108 }; 109 110 static final String[] MMS_PROJECTION = new String[] { 111 BaseColumns._ID, 112 Mms.THREAD_ID, 113 Mms.MESSAGE_ID, 114 Mms.MESSAGE_SIZE, 115 Mms.SUBJECT, 116 Mms.CONTENT_TYPE, 117 Mms.TEXT_ONLY, 118 Mms.DATE, 119 Mms.DATE_SENT, 120 Mms.READ, 121 Mms.MESSAGE_BOX, 122 Mms.STATUS, 123 Mms.PRIORITY 124 }; 125 126 private class FilterInfo { 127 public static final int TYPE_SMS = 0; 128 public static final int TYPE_MMS = 1; 129 public static final int TYPE_EMAIL = 2; 130 131 int mMsgType = TYPE_SMS; 132 int mPhoneType = 0; 133 String mPhoneNum = null; 134 String mPhoneAlphaTag = null; 135 /*column indices used to optimize queries */ 136 public int mEmailColThreadId = -1; 137 public int mEmailColProtected = -1; 138 public int mEmailColFolder = -1; 139 public int mMmsColFolder = -1; 140 public int mSmsColFolder = -1; 141 public int mEmailColRead = -1; 142 public int mSmsColRead = -1; 143 public int mMmsColRead = -1; 144 public int mEmailColPriority = -1; 145 public int mMmsColAttachmentSize = -1; 146 public int mEmailColAttachment = -1; 147 public int mEmailColAttachementSize = -1; 148 public int mMmsColTextOnly = -1; 149 public int mMmsColId = -1; 150 public int mSmsColId = -1; 151 public int mEmailColSize = -1; 152 public int mSmsColSubject = -1; 153 public int mMmsColSize = -1; 154 public int mEmailColToAddress = -1; 155 public int mEmailColCcAddress = -1; 156 public int mEmailColBccAddress = -1; 157 public int mSmsColAddress = -1; 158 public int mSmsColDate = -1; 159 public int mMmsColDate = -1; 160 public int mEmailColDate = -1; 161 public int mMmsColSubject = -1; 162 public int mEmailColSubject = -1; 163 public int mSmsColType = -1; 164 public int mEmailColFromAddress = -1; 165 public int mEmailColId = -1; 166 167 168 public void setEmailColumns(Cursor c) { 169 mEmailColThreadId = c.getColumnIndex(BluetoothMapContract.MessageColumns.THREAD_ID); 170 mEmailColProtected = c.getColumnIndex(BluetoothMapContract.MessageColumns.FLAG_PROTECTED); 171 mEmailColFolder = c.getColumnIndex(BluetoothMapContract.MessageColumns.FOLDER_ID); 172 mEmailColRead = c.getColumnIndex(BluetoothMapContract.MessageColumns.FLAG_READ); 173 mEmailColPriority = c.getColumnIndex(BluetoothMapContract.MessageColumns.FLAG_HIGH_PRIORITY); 174 mEmailColAttachment = c.getColumnIndex(BluetoothMapContract.MessageColumns.FLAG_ATTACHMENT); 175 mEmailColAttachementSize = c.getColumnIndex(BluetoothMapContract.MessageColumns.ATTACHMENT_SIZE); 176 mEmailColSize = c.getColumnIndex(BluetoothMapContract.MessageColumns.MESSAGE_SIZE); 177 mEmailColToAddress = c.getColumnIndex(BluetoothMapContract.MessageColumns.TO_LIST); 178 mEmailColCcAddress = c.getColumnIndex(BluetoothMapContract.MessageColumns.CC_LIST); 179 mEmailColBccAddress = c.getColumnIndex(BluetoothMapContract.MessageColumns.BCC_LIST); 180 mEmailColDate = c.getColumnIndex(BluetoothMapContract.MessageColumns.DATE); 181 mEmailColSubject = c.getColumnIndex(BluetoothMapContract.MessageColumns.SUBJECT); 182 mEmailColFromAddress = c.getColumnIndex(BluetoothMapContract.MessageColumns.FROM_LIST); 183 mEmailColId = c.getColumnIndex(BluetoothMapContract.MessageColumns._ID); 184 } 185 186 public void setSmsColumns(Cursor c) { 187 mSmsColId = c.getColumnIndex(BaseColumns._ID); 188 mSmsColFolder = c.getColumnIndex(Sms.TYPE); 189 mSmsColRead = c.getColumnIndex(Sms.READ); 190 mSmsColSubject = c.getColumnIndex(Sms.BODY); 191 mSmsColAddress = c.getColumnIndex(Sms.ADDRESS); 192 mSmsColDate = c.getColumnIndex(Sms.DATE); 193 mSmsColType = c.getColumnIndex(Sms.TYPE); 194 } 195 196 public void setMmsColumns(Cursor c) { 197 mMmsColId = c.getColumnIndex(BaseColumns._ID); 198 mMmsColFolder = c.getColumnIndex(Mms.MESSAGE_BOX); 199 mMmsColRead = c.getColumnIndex(Mms.READ); 200 mMmsColAttachmentSize = c.getColumnIndex(Mms.MESSAGE_SIZE); 201 mMmsColTextOnly = c.getColumnIndex(Mms.TEXT_ONLY); 202 mMmsColSize = c.getColumnIndex(Mms.MESSAGE_SIZE); 203 mMmsColDate = c.getColumnIndex(Mms.DATE); 204 mMmsColSubject = c.getColumnIndex(Mms.SUBJECT); 205 206 } 207 } 208 209 public BluetoothMapContent(final Context context, String emailBaseUri) { 210 mContext = context; 211 mResolver = mContext.getContentResolver(); 212 if (mResolver == null) { 213 if (D) Log.d(TAG, "getContentResolver failed"); 214 } 215 mBaseEmailUri = emailBaseUri; 216 } 217 218 private static void close(Closeable c) { 219 try { 220 if (c != null) c.close(); 221 } catch (IOException e) { 222 } 223 } 224 225 private void setProtected(BluetoothMapMessageListingElement e, Cursor c, 226 FilterInfo fi, BluetoothMapAppParams ap) { 227 if ((ap.getParameterMask() & MASK_PROTECTED) != 0) { 228 String protect = "no"; 229 if (fi.mMsgType == FilterInfo.TYPE_EMAIL) { 230 int flagProtected = c.getInt(fi.mEmailColProtected); 231 if (flagProtected == 1) { 232 protect = "yes"; 233 } 234 } 235 if (V) Log.d(TAG, "setProtected: " + protect + "\n"); 236 e.setProtect(protect); 237 } 238 } 239 240 /** 241 * Email only 242 */ 243 private void setThreadId(BluetoothMapMessageListingElement e, Cursor c, 244 FilterInfo fi, BluetoothMapAppParams ap) { 245 if (fi.mMsgType == FilterInfo.TYPE_EMAIL) { 246 long threadId = c.getLong(fi.mEmailColThreadId); 247 e.setThreadId(threadId); 248 if (V) Log.d(TAG, "setThreadId: " + threadId + "\n"); 249 } 250 } 251 252 private void setSent(BluetoothMapMessageListingElement e, Cursor c, 253 FilterInfo fi, BluetoothMapAppParams ap) { 254 if ((ap.getParameterMask() & MASK_SENT) != 0) { 255 int msgType = 0; 256 if (fi.mMsgType == FilterInfo.TYPE_SMS) { 257 msgType = c.getInt(fi.mSmsColFolder); 258 } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { 259 msgType = c.getInt(fi.mMmsColFolder); 260 } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL) { 261 msgType = c.getInt(fi.mEmailColFolder); 262 } 263 String sent = null; 264 if (msgType == 2) { 265 sent = "yes"; 266 } else { 267 sent = "no"; 268 } 269 if (V) Log.d(TAG, "setSent: " + sent); 270 e.setSent(sent); 271 } 272 } 273 274 private void setRead(BluetoothMapMessageListingElement e, Cursor c, 275 FilterInfo fi, BluetoothMapAppParams ap) { 276 int read = 0; 277 if (fi.mMsgType == FilterInfo.TYPE_SMS) { 278 read = c.getInt(fi.mSmsColRead); 279 } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { 280 read = c.getInt(fi.mMmsColRead); 281 } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL) { 282 read = c.getInt(fi.mEmailColRead); 283 } 284 String setread = null; 285 286 if (V) Log.d(TAG, "setRead: " + setread); 287 e.setRead((read==1?true:false), ((ap.getParameterMask() & MASK_READ) != 0)); 288 } 289 290 private void setPriority(BluetoothMapMessageListingElement e, Cursor c, 291 FilterInfo fi, BluetoothMapAppParams ap) { 292 if ((ap.getParameterMask() & MASK_PRIORITY) != 0) { 293 String priority = "no"; 294 if (fi.mMsgType == FilterInfo.TYPE_EMAIL) { 295 int highPriority = c.getInt(fi.mEmailColPriority); 296 if (highPriority == 1) { 297 priority = "yes"; 298 } 299 } 300 int pri = 0; 301 if (fi.mMsgType == FilterInfo.TYPE_MMS) { 302 pri = c.getInt(c.getColumnIndex(Mms.PRIORITY)); 303 } 304 if (pri == PduHeaders.PRIORITY_HIGH) { 305 priority = "yes"; 306 } 307 if (V) Log.d(TAG, "setPriority: " + priority); 308 e.setPriority(priority); 309 } 310 } 311 312 /** 313 * For SMS we set the attachment size to 0, as all data will be text data, hence 314 * attachments for SMS is not possible. 315 * For MMS all data is actually attachments, hence we do set the attachment size to 316 * the total message size. To provide a more accurate attachment size, one could 317 * extract the length (in bytes) of the text parts. 318 */ 319 private void setAttachmentSize(BluetoothMapMessageListingElement e, Cursor c, 320 FilterInfo fi, BluetoothMapAppParams ap) { 321 if ((ap.getParameterMask() & MASK_ATTACHMENT_SIZE) != 0) { 322 int size = 0; 323 if (fi.mMsgType == FilterInfo.TYPE_MMS) { 324 if(c.getInt(fi.mMmsColTextOnly) == 0) { 325 size = c.getInt(fi.mMmsColAttachmentSize); 326 if(size <= 0) { 327 // We know there are attachments, since it is not TextOnly 328 // Hence the size in the database must be wrong. 329 // Set size to 1 to indicate to the client, that attachments are present 330 if (D) Log.d(TAG, "Error in message database, size reported as: " + size 331 + " Changing size to 1"); 332 size = 1; 333 } 334 } 335 } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL) { 336 int attachment = c.getInt(fi.mEmailColAttachment); 337 size = c.getInt(fi.mEmailColAttachementSize); 338 if(attachment == 1 && size == 0) { 339 if (D) Log.d(TAG, "Error in message database, attachment size reported as: " + size 340 + " Changing size to 1"); 341 size = 1; /* Ensure we indicate we have attachments in the size, if the 342 message has attachments, in case the e-mail client do not 343 report a size */ 344 } 345 } 346 if (V) Log.d(TAG, "setAttachmentSize: " + size); 347 e.setAttachmentSize(size); 348 } 349 } 350 351 private void setText(BluetoothMapMessageListingElement e, Cursor c, 352 FilterInfo fi, BluetoothMapAppParams ap) { 353 if ((ap.getParameterMask() & MASK_TEXT) != 0) { 354 String hasText = ""; 355 if (fi.mMsgType == FilterInfo.TYPE_SMS) { 356 hasText = "yes"; 357 } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { 358 int textOnly = c.getInt(fi.mMmsColTextOnly); 359 if (textOnly == 1) { 360 hasText = "yes"; 361 } else { 362 long id = c.getLong(fi.mMmsColId); 363 String text = getTextPartsMms(id); 364 if (text != null && text.length() > 0) { 365 hasText = "yes"; 366 } else { 367 hasText = "no"; 368 } 369 } 370 } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL) { 371 hasText = "yes"; 372 } 373 if (V) Log.d(TAG, "setText: " + hasText); 374 e.setText(hasText); 375 } 376 } 377 378 private void setReceptionStatus(BluetoothMapMessageListingElement e, Cursor c, 379 FilterInfo fi, BluetoothMapAppParams ap) { 380 if ((ap.getParameterMask() & MASK_RECEPTION_STATUS) != 0) { 381 String status = "complete"; 382 if (V) Log.d(TAG, "setReceptionStatus: " + status); 383 e.setReceptionStatus(status); 384 } 385 } 386 387 private void setSize(BluetoothMapMessageListingElement e, Cursor c, 388 FilterInfo fi, BluetoothMapAppParams ap) { 389 if ((ap.getParameterMask() & MASK_SIZE) != 0) { 390 int size = 0; 391 if (fi.mMsgType == FilterInfo.TYPE_SMS) { 392 String subject = c.getString(fi.mSmsColSubject); 393 size = subject.length(); 394 } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { 395 size = c.getInt(fi.mMmsColSize); 396 } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL) { 397 size = c.getInt(fi.mEmailColSize); 398 } 399 if(size <= 0) { 400 // A message cannot have size 0 401 // Hence the size in the database must be wrong. 402 // Set size to 1 to indicate to the client, that the message has content. 403 if (D) Log.d(TAG, "Error in message database, size reported as: " + size 404 + " Changing size to 1"); 405 size = 1; 406 } 407 if (V) Log.d(TAG, "setSize: " + size); 408 e.setSize(size); 409 } 410 } 411 412 private void setType(BluetoothMapMessageListingElement e, Cursor c, 413 FilterInfo fi, BluetoothMapAppParams ap) { 414 if ((ap.getParameterMask() & MASK_TYPE) != 0) { 415 TYPE type = null; 416 if (fi.mMsgType == FilterInfo.TYPE_SMS) { 417 if (fi.mPhoneType == TelephonyManager.PHONE_TYPE_GSM) { 418 type = TYPE.SMS_GSM; 419 } else if (fi.mPhoneType == TelephonyManager.PHONE_TYPE_CDMA) { 420 type = TYPE.SMS_CDMA; 421 } 422 } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { 423 type = TYPE.MMS; 424 } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL) { 425 type = TYPE.EMAIL; 426 } 427 if (V) Log.d(TAG, "setType: " + type); 428 e.setType(type); 429 } 430 } 431 432 private String setRecipientAddressingEmail(BluetoothMapMessageListingElement e, Cursor c, FilterInfo fi) { 433 String toAddress, ccAddress, bccAddress; 434 toAddress = c.getString(fi.mEmailColToAddress); 435 ccAddress = c.getString(fi.mEmailColCcAddress); 436 bccAddress = c.getString(fi.mEmailColBccAddress); 437 438 String address = ""; 439 if (toAddress != null) { 440 address += toAddress; 441 if (ccAddress != null) { 442 address += ","; 443 } 444 } 445 if (ccAddress != null) { 446 address += ccAddress; 447 if (bccAddress != null) { 448 address += ","; 449 } 450 } 451 if (bccAddress != null) { 452 address += bccAddress; 453 } 454 return address; 455 } 456 457 private void setRecipientAddressing(BluetoothMapMessageListingElement e, Cursor c, 458 FilterInfo fi, BluetoothMapAppParams ap) { 459 if ((ap.getParameterMask() & MASK_RECIPIENT_ADDRESSING) != 0) { 460 String address = null; 461 if (fi.mMsgType == FilterInfo.TYPE_SMS) { 462 int msgType = c.getInt(fi.mSmsColType); 463 if (msgType == 1) { 464 address = fi.mPhoneNum; 465 } else { 466 address = c.getString(c.getColumnIndex(Sms.ADDRESS)); 467 } 468 } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { 469 long id = c.getLong(c.getColumnIndex(BaseColumns._ID)); 470 address = getAddressMms(mResolver, id, MMS_TO); 471 } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL) { 472 /* Might be another way to handle addresses */ 473 address = setRecipientAddressingEmail(e, c,fi); 474 } 475 if (V) Log.v(TAG, "setRecipientAddressing: " + address); 476 if(address == null) 477 address = ""; 478 e.setRecipientAddressing(address); 479 } 480 } 481 482 private void setRecipientName(BluetoothMapMessageListingElement e, Cursor c, 483 FilterInfo fi, BluetoothMapAppParams ap) { 484 if ((ap.getParameterMask() & MASK_RECIPIENT_NAME) != 0) { 485 String name = null; 486 if (fi.mMsgType == FilterInfo.TYPE_SMS) { 487 int msgType = c.getInt(fi.mSmsColType); 488 if (msgType != 1) { 489 String phone = c.getString(fi.mSmsColAddress); 490 if (phone != null && !phone.isEmpty()) 491 name = getContactNameFromPhone(phone); 492 } else { 493 name = fi.mPhoneAlphaTag; 494 } 495 } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { 496 long id = c.getLong(fi.mMmsColId); 497 String phone; 498 if(e.getRecipientAddressing() != null){ 499 phone = getAddressMms(mResolver, id, MMS_TO); 500 } else { 501 phone = e.getRecipientAddressing(); 502 } 503 if (phone != null && !phone.isEmpty()) 504 name = getContactNameFromPhone(phone); 505 } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL) { 506 /* Might be another way to handle address and names */ 507 name = setRecipientAddressingEmail(e,c,fi); 508 } 509 if (V) Log.v(TAG, "setRecipientName: " + name); 510 if(name == null) 511 name = ""; 512 e.setRecipientName(name); 513 } 514 } 515 516 private void setSenderAddressing(BluetoothMapMessageListingElement e, Cursor c, 517 FilterInfo fi, BluetoothMapAppParams ap) { 518 if ((ap.getParameterMask() & MASK_SENDER_ADDRESSING) != 0) { 519 String address = null; 520 String tempAddress; 521 if (fi.mMsgType == FilterInfo.TYPE_SMS) { 522 int msgType = c.getInt(fi.mSmsColType); 523 if (msgType == 1) { // INBOX 524 tempAddress = c.getString(fi.mSmsColAddress); 525 } else { 526 tempAddress = fi.mPhoneNum; 527 } 528 if(tempAddress == null) { 529 /* This can only happen on devices with no SIM - 530 hence will typically not have any SMS messages. */ 531 } else { 532 address = PhoneNumberUtils.extractNetworkPortion(tempAddress); 533 /* extractNetworkPortion can return N if the number is a service "number" = a string 534 * with the a name in (i.e. "Some-Tele-company" would return N because of the N in compaNy) 535 * Hence we need to check if the number is actually a string with alpha chars. 536 * */ 537 Boolean alpha = PhoneNumberUtils.stripSeparators(tempAddress).matches("[0-9]*[a-zA-Z]+[0-9]*"); 538 539 if(address == null || address.length() < 2 || alpha) { 540 address = tempAddress; // if the number is a service acsii text just use it 541 } 542 } 543 } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { 544 long id = c.getLong(fi.mMmsColId); 545 tempAddress = getAddressMms(mResolver, id, MMS_FROM); 546 address = PhoneNumberUtils.extractNetworkPortion(tempAddress); 547 if(address == null || address.length() < 1){ 548 address = tempAddress; // if the number is a service acsii text just use it 549 } 550 } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL) { 551 address = c.getString(fi.mEmailColFromAddress); 552 } 553 if (V) Log.v(TAG, "setSenderAddressing: " + address); 554 if(address == null) 555 address = ""; 556 e.setSenderAddressing(address); 557 } 558 } 559 560 private void setSenderName(BluetoothMapMessageListingElement e, Cursor c, 561 FilterInfo fi, BluetoothMapAppParams ap) { 562 if ((ap.getParameterMask() & MASK_SENDER_NAME) != 0) { 563 String name = null; 564 if (fi.mMsgType == FilterInfo.TYPE_SMS) { 565 int msgType = c.getInt(c.getColumnIndex(Sms.TYPE)); 566 if (msgType == 1) { 567 String phone = c.getString(fi.mSmsColAddress); 568 if (phone != null && !phone.isEmpty()) 569 name = getContactNameFromPhone(phone); 570 } else { 571 name = fi.mPhoneAlphaTag; 572 } 573 } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { 574 long id = c.getLong(fi.mMmsColId); 575 String phone; 576 if(e.getSenderAddressing() != null){ 577 phone = getAddressMms(mResolver, id, MMS_FROM); 578 } else { 579 phone = e.getSenderAddressing(); 580 } 581 if (phone != null && !phone.isEmpty() ) 582 name = getContactNameFromPhone(phone); 583 } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL) { 584 name = c.getString(fi.mEmailColFromAddress); 585 } 586 if (V) Log.v(TAG, "setSenderName: " + name); 587 if(name == null) 588 name = ""; 589 e.setSenderName(name); 590 } 591 } 592 593 private void setDateTime(BluetoothMapMessageListingElement e, Cursor c, 594 FilterInfo fi, BluetoothMapAppParams ap) { 595 if ((ap.getParameterMask() & MASK_DATETIME) != 0) { 596 long date = 0; 597 if (fi.mMsgType == FilterInfo.TYPE_SMS) { 598 date = c.getLong(fi.mSmsColDate); 599 } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { 600 /* Use Mms.DATE for all messages. Although contract class states */ 601 /* Mms.DATE_SENT are for outgoing messages. But that is not working. */ 602 date = c.getLong(fi.mMmsColDate) * 1000L; 603 604 /* int msgBox = c.getInt(c.getColumnIndex(Mms.MESSAGE_BOX)); */ 605 /* if (msgBox == Mms.MESSAGE_BOX_INBOX) { */ 606 /* date = c.getLong(c.getColumnIndex(Mms.DATE)) * 1000L; */ 607 /* } else { */ 608 /* date = c.getLong(c.getColumnIndex(Mms.DATE_SENT)) * 1000L; */ 609 /* } */ 610 } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL) { 611 date = c.getLong(fi.mEmailColDate); 612 } 613 e.setDateTime(date); 614 } 615 } 616 617 private String getTextPartsMms(long id) { 618 String text = ""; 619 String selection = new String("mid=" + id); 620 String uriStr = new String(Mms.CONTENT_URI + "/" + id + "/part"); 621 Uri uriAddress = Uri.parse(uriStr); 622 // TODO: maybe use a projection with only "ct" and "text" 623 624 Cursor c = mResolver.query(uriAddress, null, selection, null, null); 625 try { 626 while(c != null && c.moveToNext()) { 627 String ct = c.getString(c.getColumnIndex("ct")); 628 if (ct.equals("text/plain")) { 629 String part = c.getString(c.getColumnIndex("text")); 630 if(part != null) { 631 text += part; 632 } 633 } 634 } 635 } finally { 636 close(c); 637 } 638 return text; 639 } 640 641 private void setSubject(BluetoothMapMessageListingElement e, Cursor c, 642 FilterInfo fi, BluetoothMapAppParams ap) { 643 String subject = ""; 644 int subLength = ap.getSubjectLength(); 645 if(subLength == BluetoothMapAppParams.INVALID_VALUE_PARAMETER) 646 subLength = 256; 647 648 if ((ap.getParameterMask() & MASK_SUBJECT) != 0) { 649 if (fi.mMsgType == FilterInfo.TYPE_SMS) { 650 subject = c.getString(fi.mSmsColSubject); 651 } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { 652 subject = c.getString(fi.mMmsColSubject); 653 if (subject == null || subject.length() == 0) { 654 /* Get subject from mms text body parts - if any exists */ 655 long id = c.getLong(fi.mMmsColId); 656 subject = getTextPartsMms(id); 657 } 658 } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL) { 659 subject = c.getString(fi.mEmailColSubject); 660 } 661 if (subject != null && subject.length() > subLength) { 662 subject = subject.substring(0, subLength); 663 } 664 if (V) Log.d(TAG, "setSubject: " + subject); 665 e.setSubject(subject); 666 } 667 } 668 669 private void setHandle(BluetoothMapMessageListingElement e, Cursor c, 670 FilterInfo fi, BluetoothMapAppParams ap) { 671 long handle = -1; 672 if (fi.mMsgType == FilterInfo.TYPE_SMS) { 673 handle = c.getLong(fi.mSmsColId); 674 } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { 675 handle = c.getLong(fi.mMmsColId); 676 } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL) { 677 handle = c.getLong(fi.mEmailColId); 678 } 679 if (V) Log.d(TAG, "setHandle: " + handle ); 680 e.setHandle(handle); 681 } 682 683 private BluetoothMapMessageListingElement element(Cursor c, FilterInfo fi, 684 BluetoothMapAppParams ap) { 685 BluetoothMapMessageListingElement e = new BluetoothMapMessageListingElement(); 686 setHandle(e, c, fi, ap); 687 setDateTime(e, c, fi, ap); 688 setType(e, c, fi, ap); 689 setRead(e, c, fi, ap); 690 // we set number and name for sender/recipient later 691 // they require lookup on contacts so no need to 692 // do it for all elements unless they are to be used. 693 e.setCursorIndex(c.getPosition()); 694 return e; 695 } 696 697 private String getContactNameFromPhone(String phone) { 698 String name = null; 699 700 Uri uri = Uri.withAppendedPath(PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI, 701 Uri.encode(phone)); 702 703 String[] projection = {Contacts._ID, Contacts.DISPLAY_NAME}; 704 String selection = Contacts.IN_VISIBLE_GROUP + "=1"; 705 String orderBy = Contacts.DISPLAY_NAME + " ASC"; 706 707 Cursor c = mResolver.query(uri, projection, selection, null, orderBy); 708 try { 709 if (c != null && c.moveToFirst()) { 710 name = c.getString(c.getColumnIndex(Contacts.DISPLAY_NAME)); 711 }; 712 } finally { 713 close(c); 714 } 715 return name; 716 } 717 718 static public String getAddressMms(ContentResolver r, long id, int type) { 719 String selection = new String("msg_id=" + id + " AND type=" + type); 720 String uriStr = new String(Mms.CONTENT_URI + "/" + id + "/addr"); 721 Uri uriAddress = Uri.parse(uriStr); 722 String addr = null; 723 724 Cursor c = r.query(uriAddress, null, selection, null, null); 725 try { 726 if (c != null && c.moveToFirst()) { 727 addr = c.getString(c.getColumnIndex(Mms.Addr.ADDRESS)); 728 if (addr.equals(INSERT_ADDRES_TOKEN)) addr = ""; 729 } 730 } finally { 731 close(c); 732 } 733 734 return addr; 735 } 736 737 /** 738 * Matching functions for originator and recipient for MMS 739 * @return true if found a match 740 */ 741 private boolean matchRecipientMms(Cursor c, FilterInfo fi, String recip) { 742 boolean res; 743 long id = c.getLong(c.getColumnIndex(BaseColumns._ID)); 744 String phone = getAddressMms(mResolver, id, MMS_TO); 745 if (phone != null && phone.length() > 0) { 746 if (phone.matches(recip)) { 747 if (V) Log.v(TAG, "matchRecipientMms: match recipient phone = " + phone); 748 res = true; 749 } else { 750 String name = getContactNameFromPhone(phone); 751 if (name != null && name.length() > 0 && name.matches(recip)) { 752 if (V) Log.v(TAG, "matchRecipientMms: match recipient name = " + name); 753 res = true; 754 } else { 755 res = false; 756 } 757 } 758 } else { 759 res = false; 760 } 761 return res; 762 } 763 764 private boolean matchRecipientSms(Cursor c, FilterInfo fi, String recip) { 765 boolean res; 766 int msgType = c.getInt(c.getColumnIndex(Sms.TYPE)); 767 if (msgType == 1) { 768 String phone = fi.mPhoneNum; 769 String name = fi.mPhoneAlphaTag; 770 if (phone != null && phone.length() > 0 && phone.matches(recip)) { 771 if (V) Log.v(TAG, "matchRecipientSms: match recipient phone = " + phone); 772 res = true; 773 } else if (name != null && name.length() > 0 && name.matches(recip)) { 774 if (V) Log.v(TAG, "matchRecipientSms: match recipient name = " + name); 775 res = true; 776 } else { 777 res = false; 778 } 779 } else { 780 String phone = c.getString(c.getColumnIndex(Sms.ADDRESS)); 781 if (phone != null && phone.length() > 0) { 782 if (phone.matches(recip)) { 783 if (V) Log.v(TAG, "matchRecipientSms: match recipient phone = " + phone); 784 res = true; 785 } else { 786 String name = getContactNameFromPhone(phone); 787 if (name != null && name.length() > 0 && name.matches(recip)) { 788 if (V) Log.v(TAG, "matchRecipientSms: match recipient name = " + name); 789 res = true; 790 } else { 791 res = false; 792 } 793 } 794 } else { 795 res = false; 796 } 797 } 798 return res; 799 } 800 801 private boolean matchRecipient(Cursor c, FilterInfo fi, BluetoothMapAppParams ap) { 802 boolean res; 803 String recip = ap.getFilterRecipient(); 804 if (recip != null && recip.length() > 0) { 805 recip = recip.replace("*", ".*"); 806 recip = ".*" + recip + ".*"; 807 if (fi.mMsgType == FilterInfo.TYPE_SMS) { 808 res = matchRecipientSms(c, fi, recip); 809 } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { 810 res = matchRecipientMms(c, fi, recip); 811 } else { 812 if (D) Log.d(TAG, "matchRecipient: Unknown msg type: " + fi.mMsgType); 813 res = false; 814 } 815 } else { 816 res = true; 817 } 818 return res; 819 } 820 821 private boolean matchOriginatorMms(Cursor c, FilterInfo fi, String orig) { 822 boolean res; 823 long id = c.getLong(c.getColumnIndex(BaseColumns._ID)); 824 String phone = getAddressMms(mResolver, id, MMS_FROM); 825 if (phone != null && phone.length() > 0) { 826 if (phone.matches(orig)) { 827 if (V) Log.v(TAG, "matchOriginatorMms: match originator phone = " + phone); 828 res = true; 829 } else { 830 String name = getContactNameFromPhone(phone); 831 if (name != null && name.length() > 0 && name.matches(orig)) { 832 if (V) Log.v(TAG, "matchOriginatorMms: match originator name = " + name); 833 res = true; 834 } else { 835 res = false; 836 } 837 } 838 } else { 839 res = false; 840 } 841 return res; 842 } 843 844 private boolean matchOriginatorSms(Cursor c, FilterInfo fi, String orig) { 845 boolean res; 846 int msgType = c.getInt(c.getColumnIndex(Sms.TYPE)); 847 if (msgType == 1) { 848 String phone = c.getString(c.getColumnIndex(Sms.ADDRESS)); 849 if (phone !=null && phone.length() > 0) { 850 if (phone.matches(orig)) { 851 if (V) Log.v(TAG, "matchOriginatorSms: match originator phone = " + phone); 852 res = true; 853 } else { 854 String name = getContactNameFromPhone(phone); 855 if (name != null && name.length() > 0 && name.matches(orig)) { 856 if (V) Log.v(TAG, "matchOriginatorSms: match originator name = " + name); 857 res = true; 858 } else { 859 res = false; 860 } 861 } 862 } else { 863 res = false; 864 } 865 } else { 866 String phone = fi.mPhoneNum; 867 String name = fi.mPhoneAlphaTag; 868 if (phone != null && phone.length() > 0 && phone.matches(orig)) { 869 if (V) Log.v(TAG, "matchOriginatorSms: match originator phone = " + phone); 870 res = true; 871 } else if (name != null && name.length() > 0 && name.matches(orig)) { 872 if (V) Log.v(TAG, "matchOriginatorSms: match originator name = " + name); 873 res = true; 874 } else { 875 res = false; 876 } 877 } 878 return res; 879 } 880 881 private boolean matchOriginator(Cursor c, FilterInfo fi, BluetoothMapAppParams ap) { 882 boolean res; 883 String orig = ap.getFilterOriginator(); 884 if (orig != null && orig.length() > 0) { 885 orig = orig.replace("*", ".*"); 886 orig = ".*" + orig + ".*"; 887 if (fi.mMsgType == FilterInfo.TYPE_SMS) { 888 res = matchOriginatorSms(c, fi, orig); 889 } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { 890 res = matchOriginatorMms(c, fi, orig); 891 } else { 892 if(D) Log.d(TAG, "matchOriginator: Unknown msg type: " + fi.mMsgType); 893 res = false; 894 } 895 } else { 896 res = true; 897 } 898 return res; 899 } 900 901 private boolean matchAddresses(Cursor c, FilterInfo fi, BluetoothMapAppParams ap) { 902 if (matchOriginator(c, fi, ap) && matchRecipient(c, fi, ap)) { 903 return true; 904 } else { 905 return false; 906 } 907 } 908 909 /* 910 * Where filter functions 911 * */ 912 private String setWhereFilterFolderTypeSms(String folder) { 913 String where = ""; 914 if (BluetoothMapContract.FOLDER_NAME_INBOX.equalsIgnoreCase(folder)) { 915 where = Sms.TYPE + " = 1 AND " + Sms.THREAD_ID + " <> -1"; 916 } else if (BluetoothMapContract.FOLDER_NAME_OUTBOX.equalsIgnoreCase(folder)) { 917 where = "(" + Sms.TYPE + " = 4 OR " + Sms.TYPE + " = 5 OR " 918 + Sms.TYPE + " = 6) AND " + Sms.THREAD_ID + " <> -1"; 919 } else if (BluetoothMapContract.FOLDER_NAME_SENT.equalsIgnoreCase(folder)) { 920 where = Sms.TYPE + " = 2 AND " + Sms.THREAD_ID + " <> -1"; 921 } else if (BluetoothMapContract.FOLDER_NAME_DRAFT.equalsIgnoreCase(folder)) { 922 where = Sms.TYPE + " = 3 AND " + Sms.THREAD_ID + " <> -1"; 923 } else if (BluetoothMapContract.FOLDER_NAME_DELETED.equalsIgnoreCase(folder)) { 924 where = Sms.THREAD_ID + " = -1"; 925 } 926 927 return where; 928 } 929 930 private String setWhereFilterFolderTypeMms(String folder) { 931 String where = ""; 932 if (BluetoothMapContract.FOLDER_NAME_INBOX.equalsIgnoreCase(folder)) { 933 where = Mms.MESSAGE_BOX + " = 1 AND " + Mms.THREAD_ID + " <> -1"; 934 } else if (BluetoothMapContract.FOLDER_NAME_OUTBOX.equalsIgnoreCase(folder)) { 935 where = Mms.MESSAGE_BOX + " = 4 AND " + Mms.THREAD_ID + " <> -1"; 936 } else if (BluetoothMapContract.FOLDER_NAME_SENT.equalsIgnoreCase(folder)) { 937 where = Mms.MESSAGE_BOX + " = 2 AND " + Mms.THREAD_ID + " <> -1"; 938 } else if (BluetoothMapContract.FOLDER_NAME_DRAFT.equalsIgnoreCase(folder)) { 939 where = Mms.MESSAGE_BOX + " = 3 AND " + Mms.THREAD_ID + " <> -1"; 940 } else if (BluetoothMapContract.FOLDER_NAME_DELETED.equalsIgnoreCase(folder)) { 941 where = Mms.THREAD_ID + " = -1"; 942 } 943 944 return where; 945 } 946 947 private String setWhereFilterFolderTypeEmail(long folderId) { 948 String where = ""; 949 if (folderId >= 0) { 950 where = BluetoothMapContract.MessageColumns.FOLDER_ID + " = " + folderId; 951 } else { 952 Log.e(TAG, "setWhereFilterFolderTypeEmail: not valid!" ); 953 throw new IllegalArgumentException("Invalid folder ID"); 954 } 955 return where; 956 } 957 958 private String setWhereFilterFolderType(BluetoothMapFolderElement folderElement, FilterInfo fi) { 959 String where = ""; 960 if (fi.mMsgType == FilterInfo.TYPE_SMS) { 961 where = setWhereFilterFolderTypeSms(folderElement.getName()); 962 } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { 963 where = setWhereFilterFolderTypeMms(folderElement.getName()); 964 } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL) { 965 where = setWhereFilterFolderTypeEmail(folderElement.getEmailFolderId()); 966 } 967 return where; 968 } 969 970 private String setWhereFilterReadStatus(BluetoothMapAppParams ap, FilterInfo fi) { 971 String where = ""; 972 if (ap.getFilterReadStatus() != -1) { 973 if (fi.mMsgType == FilterInfo.TYPE_SMS) { 974 if ((ap.getFilterReadStatus() & 0x01) != 0) { 975 where = " AND " + Sms.READ + "= 0"; 976 } 977 978 if ((ap.getFilterReadStatus() & 0x02) != 0) { 979 where = " AND " + Sms.READ + "= 1"; 980 } 981 } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { 982 if ((ap.getFilterReadStatus() & 0x01) != 0) { 983 where = " AND " + Mms.READ + "= 0"; 984 } 985 986 if ((ap.getFilterReadStatus() & 0x02) != 0) { 987 where = " AND " + Mms.READ + "= 1"; 988 } 989 } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL) { 990 if ((ap.getFilterReadStatus() & 0x01) != 0) { 991 where = " AND " + BluetoothMapContract.MessageColumns.FLAG_READ + "= 0"; 992 } 993 994 if ((ap.getFilterReadStatus() & 0x02) != 0) { 995 where = " AND " + BluetoothMapContract.MessageColumns.FLAG_READ + "= 1"; 996 } 997 } 998 } 999 return where; 1000 } 1001 1002 private String setWhereFilterPeriod(BluetoothMapAppParams ap, FilterInfo fi) { 1003 String where = ""; 1004 if ((ap.getFilterPeriodBegin() != -1)) { 1005 if (fi.mMsgType == FilterInfo.TYPE_SMS) { 1006 where = " AND " + Sms.DATE + " >= " + ap.getFilterPeriodBegin(); 1007 } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { 1008 where = " AND " + Mms.DATE + " >= " + (ap.getFilterPeriodBegin() / 1000L); 1009 } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL) { 1010 where = " AND " + BluetoothMapContract.MessageColumns.DATE + " >= " + (ap.getFilterPeriodBegin()); 1011 } 1012 } 1013 1014 if ((ap.getFilterPeriodEnd() != -1)) { 1015 if (fi.mMsgType == FilterInfo.TYPE_SMS) { 1016 where += " AND " + Sms.DATE + " < " + ap.getFilterPeriodEnd(); 1017 } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { 1018 where += " AND " + Mms.DATE + " < " + (ap.getFilterPeriodEnd() / 1000L); 1019 } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL) { 1020 where += " AND " + BluetoothMapContract.MessageColumns.DATE + " < " + (ap.getFilterPeriodEnd()); 1021 } 1022 } 1023 1024 1025 return where; 1026 } 1027 1028 private String setWhereFilterOriginatorEmail(BluetoothMapAppParams ap) { 1029 String where = ""; 1030 String orig = ap.getFilterOriginator(); 1031 1032 /* Be aware of wild cards in the beginning of string, may not be valid? */ 1033 if (orig != null && orig.length() > 0) { 1034 orig = orig.replace("*", "%"); 1035 where = " AND " + BluetoothMapContract.MessageColumns.FROM_LIST + " LIKE '%" + orig + "%'"; 1036 } 1037 return where; 1038 } 1039 private String setWhereFilterPriority(BluetoothMapAppParams ap, FilterInfo fi) { 1040 String where = ""; 1041 int pri = ap.getFilterPriority(); 1042 /*only MMS have priority info */ 1043 if(fi.mMsgType == FilterInfo.TYPE_MMS) 1044 { 1045 if(pri == 0x0002) 1046 { 1047 where += " AND " + Mms.PRIORITY + "<=" + 1048 Integer.toString(PduHeaders.PRIORITY_NORMAL); 1049 }else if(pri == 0x0001) { 1050 where += " AND " + Mms.PRIORITY + "=" + 1051 Integer.toString(PduHeaders.PRIORITY_HIGH); 1052 } 1053 } 1054 return where; 1055 } 1056 1057 private String setWhereFilterRecipientEmail(BluetoothMapAppParams ap) { 1058 String where = ""; 1059 String recip = ap.getFilterRecipient(); 1060 1061 /* Be aware of wild cards in the beginning of string, may not be valid? */ 1062 if (recip != null && recip.length() > 0) { 1063 recip = recip.replace("*", "%"); 1064 where = " AND (" 1065 + BluetoothMapContract.MessageColumns.TO_LIST + " LIKE '%" + recip + "%' OR " 1066 + BluetoothMapContract.MessageColumns.CC_LIST + " LIKE '%" + recip + "%' OR " 1067 + BluetoothMapContract.MessageColumns.BCC_LIST + " LIKE '%" + recip + "%' )"; 1068 } 1069 return where; 1070 } 1071 1072 private String setWhereFilter(BluetoothMapFolderElement folderElement, 1073 FilterInfo fi, BluetoothMapAppParams ap) { 1074 String where = ""; 1075 1076 where += setWhereFilterFolderType(folderElement, fi); 1077 if(!where.isEmpty()) { 1078 where += setWhereFilterReadStatus(ap, fi); 1079 where += setWhereFilterPeriod(ap, fi); 1080 where += setWhereFilterPriority(ap,fi); 1081 1082 if (fi.mMsgType == FilterInfo.TYPE_EMAIL) { 1083 where += setWhereFilterOriginatorEmail(ap); 1084 where += setWhereFilterRecipientEmail(ap); 1085 } 1086 } 1087 1088 1089 return where; 1090 } 1091 1092 /** 1093 * Determine from application parameter if sms should be included. 1094 * The filter mask is set for message types not selected 1095 * @param fi 1096 * @param ap 1097 * @return boolean true if sms is selected, false if not 1098 */ 1099 private boolean smsSelected(FilterInfo fi, BluetoothMapAppParams ap) { 1100 int msgType = ap.getFilterMessageType(); 1101 int phoneType = fi.mPhoneType; 1102 1103 if (D) Log.d(TAG, "smsSelected msgType: " + msgType); 1104 1105 if (msgType == -1) 1106 return true; 1107 1108 if ((msgType & 0x03) == 0) 1109 return true; 1110 1111 if (((msgType & 0x01) == 0) && (phoneType == TelephonyManager.PHONE_TYPE_GSM)) 1112 return true; 1113 1114 if (((msgType & 0x02) == 0) && (phoneType == TelephonyManager.PHONE_TYPE_CDMA)) 1115 return true; 1116 1117 return false; 1118 } 1119 1120 /** 1121 * Determine from application parameter if mms should be included. 1122 * The filter mask is set for message types not selected 1123 * @param fi 1124 * @param ap 1125 * @return boolean true if sms is selected, false if not 1126 */ 1127 private boolean mmsSelected(FilterInfo fi, BluetoothMapAppParams ap) { 1128 int msgType = ap.getFilterMessageType(); 1129 1130 if (D) Log.d(TAG, "mmsSelected msgType: " + msgType); 1131 1132 if (msgType == -1) 1133 return true; 1134 1135 if ((msgType & 0x08) == 0) 1136 return true; 1137 1138 return false; 1139 } 1140 1141 /** 1142 * Determine from application parameter if email should be included. 1143 * The filter mask is set for message types not selected 1144 * @param fi 1145 * @param ap 1146 * @return boolean true if sms is selected, false if not 1147 */ 1148 private boolean emailSelected(FilterInfo fi, BluetoothMapAppParams ap) { 1149 int msgType = ap.getFilterMessageType(); 1150 1151 if (D) Log.d(TAG, "emailSelected msgType: " + msgType); 1152 1153 if (msgType == -1) 1154 return true; 1155 1156 if ((msgType & 0x04) == 0) 1157 return true; 1158 1159 return false; 1160 } 1161 1162 private void setFilterInfo(FilterInfo fi) { 1163 TelephonyManager tm = (TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE); 1164 if (tm != null) { 1165 fi.mPhoneType = tm.getPhoneType(); 1166 fi.mPhoneNum = tm.getLine1Number(); 1167 fi.mPhoneAlphaTag = tm.getLine1AlphaTag(); 1168 if (D) Log.d(TAG, "phone type = " + fi.mPhoneType + 1169 " phone num = " + fi.mPhoneNum + 1170 " phone alpha tag = " + fi.mPhoneAlphaTag); 1171 } 1172 } 1173 1174 /** 1175 * Get a listing of message in folder after applying filter. 1176 * @param folder Must contain a valid folder string != null 1177 * @param ap Parameters specifying message content and filters 1178 * @return Listing object containing requested messages 1179 */ 1180 public BluetoothMapMessageListing msgListing(BluetoothMapFolderElement folderElement, 1181 BluetoothMapAppParams ap) { 1182 if (D) Log.d(TAG, "msgListing: folderName = " + folderElement.getName() 1183 + " folderId = " + folderElement.getEmailFolderId() 1184 + " messageType = " + ap.getFilterMessageType() ); 1185 BluetoothMapMessageListing bmList = new BluetoothMapMessageListing(); 1186 1187 1188 /* We overwrite the parameter mask here if it is 0 or not present, as this 1189 * should cause all parameters to be included in the message list. */ 1190 if(ap.getParameterMask() == BluetoothMapAppParams.INVALID_VALUE_PARAMETER || 1191 ap.getParameterMask() == 0) { 1192 ap.setParameterMask(BluetoothMapAppParams.PARAMETER_MASK_ALL_ENABLED); 1193 if (V) Log.v(TAG, "msgListing(): appParameterMask is zero or not present, " + 1194 "changing to: " + ap.getParameterMask()); 1195 } 1196 1197 /* Cache some info used throughout filtering */ 1198 FilterInfo fi = new FilterInfo(); 1199 setFilterInfo(fi); 1200 Cursor smsCursor = null; 1201 Cursor mmsCursor = null; 1202 Cursor emailCursor = null; 1203 1204 try { 1205 String limit = ""; 1206 int countNum = ap.getMaxListCount(); 1207 int offsetNum = ap.getStartOffset(); 1208 if(ap.getMaxListCount()>0){ 1209 limit=" LIMIT "+ (ap.getMaxListCount()+ap.getStartOffset()); 1210 } 1211 1212 if (smsSelected(fi, ap) && folderElement.hasSmsMmsContent()) { 1213 if(ap.getFilterMessageType() == (BluetoothMapAppParams.FILTER_NO_EMAIL| 1214 BluetoothMapAppParams.FILTER_NO_MMS| 1215 BluetoothMapAppParams.FILTER_NO_SMS_GSM)|| 1216 ap.getFilterMessageType() == (BluetoothMapAppParams.FILTER_NO_EMAIL| 1217 BluetoothMapAppParams.FILTER_NO_MMS| 1218 BluetoothMapAppParams.FILTER_NO_SMS_CDMA)){ 1219 //set real limit and offset if only this type is used (only if offset/limit is used 1220 limit = " LIMIT " + ap.getMaxListCount()+" OFFSET "+ ap.getStartOffset(); 1221 if(D) Log.d(TAG, "SMS Limit => "+limit); 1222 offsetNum = 0; 1223 } 1224 fi.mMsgType = FilterInfo.TYPE_SMS; 1225 if(ap.getFilterPriority() != 1){ /*SMS cannot have high priority*/ 1226 String where = setWhereFilter(folderElement, fi, ap); 1227 if(!where.isEmpty()) { 1228 if (D) Log.d(TAG, "msgType: " + fi.mMsgType); 1229 smsCursor = mResolver.query(Sms.CONTENT_URI, 1230 SMS_PROJECTION, where, null, Sms.DATE + " DESC" + limit); 1231 if (smsCursor != null) { 1232 BluetoothMapMessageListingElement e = null; 1233 // store column index so we dont have to look them up anymore (optimization) 1234 if(D) Log.d(TAG, "Found " + smsCursor.getCount() + " sms messages."); 1235 fi.setSmsColumns(smsCursor); 1236 while (smsCursor.moveToNext()) { 1237 if (matchAddresses(smsCursor, fi, ap)) { 1238 e = element(smsCursor, fi, ap); 1239 bmList.add(e); 1240 } 1241 } 1242 } 1243 } 1244 } 1245 } 1246 1247 if (mmsSelected(fi, ap) && folderElement.hasSmsMmsContent()) { 1248 if(ap.getFilterMessageType() == (BluetoothMapAppParams.FILTER_NO_EMAIL| 1249 BluetoothMapAppParams.FILTER_NO_SMS_CDMA| 1250 BluetoothMapAppParams.FILTER_NO_SMS_GSM)){ 1251 //set real limit and offset if only this type is used (only if offset/limit is used 1252 limit = " LIMIT " + ap.getMaxListCount()+" OFFSET "+ ap.getStartOffset(); 1253 if(D) Log.d(TAG, "MMS Limit => "+limit); 1254 offsetNum = 0; 1255 } 1256 fi.mMsgType = FilterInfo.TYPE_MMS; 1257 String where = setWhereFilter(folderElement, fi, ap); 1258 if(!where.isEmpty()) { 1259 if (D) Log.d(TAG, "msgType: " + fi.mMsgType); 1260 mmsCursor = mResolver.query(Mms.CONTENT_URI, 1261 MMS_PROJECTION, where, null, Mms.DATE + " DESC" + limit); 1262 if (mmsCursor != null) { 1263 BluetoothMapMessageListingElement e = null; 1264 // store column index so we dont have to look them up anymore (optimization) 1265 fi.setMmsColumns(mmsCursor); 1266 int cnt = 0; 1267 if(D) Log.d(TAG, "Found " + mmsCursor.getCount() + " mms messages."); 1268 while (mmsCursor.moveToNext()) { 1269 if (matchAddresses(mmsCursor, fi, ap)) { 1270 e = element(mmsCursor, fi, ap); 1271 bmList.add(e); 1272 } 1273 } 1274 } 1275 } 1276 } 1277 1278 if (emailSelected(fi, ap) && folderElement.getEmailFolderId() != -1) { 1279 if(ap.getFilterMessageType() == (BluetoothMapAppParams.FILTER_NO_MMS| 1280 BluetoothMapAppParams.FILTER_NO_SMS_CDMA| 1281 BluetoothMapAppParams.FILTER_NO_SMS_GSM)){ 1282 //set real limit and offset if only this type is used (only if offset/limit is used 1283 limit = " LIMIT " + ap.getMaxListCount()+" OFFSET "+ ap.getStartOffset(); 1284 if(D) Log.d(TAG, "Email Limit => "+limit); 1285 offsetNum = 0; 1286 } 1287 fi.mMsgType = FilterInfo.TYPE_EMAIL; 1288 String where = setWhereFilter(folderElement, fi, ap); 1289 1290 if(!where.isEmpty()) { 1291 if (D) Log.d(TAG, "msgType: " + fi.mMsgType); 1292 Uri contentUri = Uri.parse(mBaseEmailUri + BluetoothMapContract.TABLE_MESSAGE); 1293 emailCursor = mResolver.query(contentUri, BluetoothMapContract.BT_MESSAGE_PROJECTION, 1294 where, null, BluetoothMapContract.MessageColumns.DATE + " DESC" + limit); 1295 if (emailCursor != null) { 1296 BluetoothMapMessageListingElement e = null; 1297 // store column index so we dont have to look them up anymore (optimization) 1298 fi.setEmailColumns(emailCursor); 1299 int cnt = 0; 1300 while (emailCursor.moveToNext()) { 1301 if(D) Log.d(TAG, "Found " + emailCursor.getCount() + " email messages."); 1302 e = element(emailCursor, fi, ap); 1303 bmList.add(e); 1304 } 1305 // emailCursor.close(); 1306 } 1307 } 1308 } 1309 1310 /* Enable this if post sorting and segmenting needed */ 1311 bmList.sort(); 1312 bmList.segment(ap.getMaxListCount(), offsetNum); 1313 List<BluetoothMapMessageListingElement> list = bmList.getList(); 1314 int listSize = list.size(); 1315 Cursor tmpCursor = null; 1316 for (int x=0; x<listSize; x++){ 1317 BluetoothMapMessageListingElement ele = list.get(x); 1318 if ((ele.getType().equals(TYPE.SMS_GSM)||ele.getType().equals(TYPE.SMS_CDMA)) && smsCursor != null){ 1319 tmpCursor = smsCursor; 1320 fi.mMsgType = FilterInfo.TYPE_SMS; 1321 } else if (ele.getType().equals(TYPE.MMS) && mmsCursor != null){ 1322 tmpCursor = mmsCursor; 1323 fi.mMsgType = FilterInfo.TYPE_MMS; 1324 } else if (ele.getType().equals(TYPE.EMAIL) && emailCursor != null){ 1325 tmpCursor = emailCursor; 1326 fi.mMsgType = FilterInfo.TYPE_EMAIL; 1327 } 1328 1329 if (tmpCursor != null && tmpCursor.moveToPosition(ele.getCursorIndex())) { 1330 setSenderAddressing(ele, tmpCursor, fi, ap); 1331 setSenderName(ele, tmpCursor, fi, ap); 1332 setRecipientAddressing(ele, tmpCursor, fi, ap); 1333 setRecipientName(ele, tmpCursor, fi, ap); 1334 setSubject(ele, tmpCursor, fi, ap); 1335 setSize(ele, tmpCursor, fi, ap); 1336 setReceptionStatus(ele, tmpCursor, fi, ap); 1337 setText(ele, tmpCursor, fi, ap); 1338 setAttachmentSize(ele, tmpCursor, fi, ap); 1339 setPriority(ele, tmpCursor, fi, ap); 1340 setSent(ele, tmpCursor, fi, ap); 1341 setProtected(ele, tmpCursor, fi, ap); 1342 setThreadId(ele, tmpCursor, fi, ap); 1343 } 1344 } 1345 } finally { 1346 close(emailCursor); 1347 close(smsCursor); 1348 close(mmsCursor); 1349 } 1350 1351 if (D) Log.d(TAG, "messagelisting end"); 1352 return bmList; 1353 } 1354 1355 /** 1356 * Get the size of the message listing 1357 * @param folder Must contain a valid folder string != null 1358 * @param ap Parameters specifying message content and filters 1359 * @return Integer equal to message listing size 1360 */ 1361 public int msgListingSize(BluetoothMapFolderElement folderElement, 1362 BluetoothMapAppParams ap) { 1363 if (D) Log.d(TAG, "msgListingSize: folder = " + folderElement.getName()); 1364 int cnt = 0; 1365 1366 /* Cache some info used throughout filtering */ 1367 FilterInfo fi = new FilterInfo(); 1368 setFilterInfo(fi); 1369 1370 if (smsSelected(fi, ap) && folderElement.hasSmsMmsContent()) { 1371 fi.mMsgType = FilterInfo.TYPE_SMS; 1372 String where = setWhereFilter(folderElement, fi, ap); 1373 Cursor c = mResolver.query(Sms.CONTENT_URI, 1374 SMS_PROJECTION, where, null, Sms.DATE + " DESC"); 1375 1376 if (c != null) cnt = c.getCount(); 1377 close(c); 1378 } 1379 1380 if (mmsSelected(fi, ap) && folderElement.hasSmsMmsContent()) { 1381 fi.mMsgType = FilterInfo.TYPE_MMS; 1382 String where = setWhereFilter(folderElement, fi, ap); 1383 Cursor c = mResolver.query(Mms.CONTENT_URI, 1384 MMS_PROJECTION, where, null, Mms.DATE + " DESC"); 1385 if (c != null) cnt += c.getCount(); 1386 close(c); 1387 } 1388 1389 if (emailSelected(fi, ap) && folderElement.getEmailFolderId() != -1) { 1390 fi.mMsgType = FilterInfo.TYPE_EMAIL; 1391 String where = setWhereFilter(folderElement, fi, ap); 1392 if (!where.isEmpty()) { 1393 Uri contentUri = Uri.parse(mBaseEmailUri + BluetoothMapContract.TABLE_MESSAGE); 1394 Cursor c = mResolver.query(contentUri, BluetoothMapContract.BT_MESSAGE_PROJECTION, 1395 where, null, BluetoothMapContract.MessageColumns.DATE + " DESC"); 1396 if (c != null) cnt += c.getCount(); 1397 close(c); 1398 } 1399 } 1400 1401 if (D) Log.d(TAG, "msgListingSize: size = " + cnt); 1402 return cnt; 1403 } 1404 1405 /** 1406 * Return true if there are unread messages in the requested list of messages 1407 * @param folder folder where the message listing should come from 1408 * @param ap application parameter object 1409 * @return true if unread messages are in the list, else false 1410 */ 1411 public boolean msgListingHasUnread(BluetoothMapFolderElement folderElement, 1412 BluetoothMapAppParams ap) { 1413 if (D) Log.d(TAG, "msgListingHasUnread: folder = " + folderElement.getName()); 1414 int cnt = 0; 1415 1416 /* Cache some info used throughout filtering */ 1417 FilterInfo fi = new FilterInfo(); 1418 setFilterInfo(fi); 1419 1420 if (smsSelected(fi, ap) && folderElement.hasSmsMmsContent()) { 1421 fi.mMsgType = FilterInfo.TYPE_SMS; 1422 String where = setWhereFilterFolderType(folderElement, fi); 1423 where += " AND " + Sms.READ + "=0 "; 1424 where += setWhereFilterPeriod(ap, fi); 1425 Cursor c = mResolver.query(Sms.CONTENT_URI, 1426 SMS_PROJECTION, where, null, Sms.DATE + " DESC"); 1427 1428 if (c != null) cnt += c.getCount(); 1429 close(c); 1430 } 1431 1432 if (mmsSelected(fi, ap) && folderElement.hasSmsMmsContent()) { 1433 fi.mMsgType = FilterInfo.TYPE_MMS; 1434 String where = setWhereFilterFolderType(folderElement, fi); 1435 where += " AND " + Mms.READ + "=0 "; 1436 where += setWhereFilterPeriod(ap, fi); 1437 Cursor c = mResolver.query(Mms.CONTENT_URI, 1438 MMS_PROJECTION, where, null, Sms.DATE + " DESC"); 1439 1440 if (c != null) cnt += c.getCount(); 1441 close(c); 1442 } 1443 1444 1445 if (emailSelected(fi, ap) && folderElement.getEmailFolderId() != -1) { 1446 fi.mMsgType = FilterInfo.TYPE_EMAIL; 1447 String where = setWhereFilterFolderType(folderElement, fi); 1448 if(!where.isEmpty()) { 1449 where += " AND " + BluetoothMapContract.MessageColumns.FLAG_READ + "=0 "; 1450 where += setWhereFilterPeriod(ap, fi); 1451 Uri contentUri = Uri.parse(mBaseEmailUri + BluetoothMapContract.TABLE_MESSAGE); 1452 Cursor c = mResolver.query(contentUri, BluetoothMapContract.BT_MESSAGE_PROJECTION, 1453 where, null, BluetoothMapContract.MessageColumns.DATE + " DESC"); 1454 if (c != null) cnt += c.getCount(); 1455 close(c); 1456 } 1457 } 1458 1459 if (D) Log.d(TAG, "msgListingHasUnread: numUnread = " + cnt); 1460 return (cnt>0)?true:false; 1461 } 1462 1463 /** 1464 * Get the folder name of an SMS message or MMS message. 1465 * @param c the cursor pointing at the message 1466 * @return the folder name. 1467 */ 1468 private String getFolderName(int type, int threadId) { 1469 1470 if(threadId == -1) 1471 return BluetoothMapContract.FOLDER_NAME_DELETED; 1472 1473 switch(type) { 1474 case 1: 1475 return BluetoothMapContract.FOLDER_NAME_INBOX; 1476 case 2: 1477 return BluetoothMapContract.FOLDER_NAME_SENT; 1478 case 3: 1479 return BluetoothMapContract.FOLDER_NAME_DRAFT; 1480 case 4: // Just name outbox, failed and queued "outbox" 1481 case 5: 1482 case 6: 1483 return BluetoothMapContract.FOLDER_NAME_OUTBOX; 1484 } 1485 return ""; 1486 } 1487 1488 public byte[] getMessage(String handle, BluetoothMapAppParams appParams, 1489 BluetoothMapFolderElement folderElement) throws UnsupportedEncodingException{ 1490 TYPE type = BluetoothMapUtils.getMsgTypeFromHandle(handle); 1491 long id = BluetoothMapUtils.getCpHandle(handle); 1492 if(appParams.getFractionRequest() == BluetoothMapAppParams.FRACTION_REQUEST_NEXT) { 1493 throw new IllegalArgumentException("FRACTION_REQUEST_NEXT does not make sence as" + 1494 " we always return the full message."); 1495 } 1496 switch(type) { 1497 case SMS_GSM: 1498 case SMS_CDMA: 1499 return getSmsMessage(id, appParams.getCharset()); 1500 case MMS: 1501 return getMmsMessage(id, appParams); 1502 case EMAIL: 1503 return getEmailMessage(id, appParams, folderElement); 1504 } 1505 throw new IllegalArgumentException("Invalid message handle."); 1506 } 1507 1508 private String setVCardFromPhoneNumber(BluetoothMapbMessage message, String phone, boolean incoming) { 1509 String contactId = null, contactName = null; 1510 String[] phoneNumbers = null; 1511 String[] emailAddresses = null; 1512 1513 Uri uri = Uri 1514 .withAppendedPath(PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI, 1515 Uri.encode(phone)); 1516 1517 String[] projection = {Contacts._ID, Contacts.DISPLAY_NAME}; 1518 String selection = Contacts.IN_VISIBLE_GROUP + "=1"; 1519 String orderBy = Contacts._ID + " ASC"; 1520 1521 // Get the contact _ID and name 1522 Cursor p = mResolver.query(uri, projection, selection, null, orderBy); 1523 1524 try { 1525 if (p != null && p.moveToFirst()) { 1526 contactId = p.getString(p.getColumnIndex(Contacts._ID)); 1527 contactName = p.getString(p.getColumnIndex(Contacts.DISPLAY_NAME)); 1528 } 1529 1530 // Bail out if we are unable to find a contact, based on the phone number 1531 if(contactId == null) { 1532 phoneNumbers = new String[1]; 1533 phoneNumbers[0] = phone; 1534 } else { 1535 // use only actual phone number 1536 phoneNumbers = new String[1]; 1537 phoneNumbers[0] = phone; 1538 1539 // Fetch contact e-mail addresses 1540 close (p); 1541 // TODO: Fetch enterprise email 1542 p = mResolver.query(ContactsContract.CommonDataKinds.Email.CONTENT_URI, null, 1543 ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?", 1544 new String[]{contactId}, 1545 null); 1546 if (p != null) { 1547 int i = 0; 1548 emailAddresses = new String[p.getCount()]; 1549 while (p != null && p.moveToNext()) { 1550 String emailAddress = p.getString( 1551 p.getColumnIndex(ContactsContract.CommonDataKinds.Email.ADDRESS)); 1552 emailAddresses[i++] = emailAddress; 1553 } 1554 } 1555 } 1556 } finally { 1557 close(p); 1558 } 1559 1560 if (incoming == true) { 1561 if(V) Log.d(TAG, "Adding originator for phone:" + phone); 1562 message.addOriginator(contactName, contactName, phoneNumbers, emailAddresses); // Use version 3.0 as we only have a formatted name 1563 } else { 1564 if(V) Log.d(TAG, "Adding recipient for phone:" + phone); 1565 message.addRecipient(contactName, contactName, phoneNumbers, emailAddresses); // Use version 3.0 as we only have a formatted name 1566 } 1567 return contactName; 1568 } 1569 1570 public static final int MAP_MESSAGE_CHARSET_NATIVE = 0; 1571 public static final int MAP_MESSAGE_CHARSET_UTF8 = 1; 1572 1573 public byte[] getSmsMessage(long id, int charset) throws UnsupportedEncodingException{ 1574 int type, threadId; 1575 long time = -1; 1576 String msgBody; 1577 BluetoothMapbMessageSms message = new BluetoothMapbMessageSms(); 1578 TelephonyManager tm = (TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE); 1579 1580 Cursor c = mResolver.query(Sms.CONTENT_URI, SMS_PROJECTION, "_ID = " + id, null, null); 1581 if (c == null || !c.moveToFirst()) { 1582 throw new IllegalArgumentException("SMS handle not found"); 1583 } 1584 1585 try { 1586 if(V) Log.v(TAG,"c.count: " + c.getCount()); 1587 1588 if (tm.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM) { 1589 message.setType(TYPE.SMS_GSM); 1590 } else if (tm.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) { 1591 message.setType(TYPE.SMS_CDMA); 1592 } 1593 1594 String read = c.getString(c.getColumnIndex(Sms.READ)); 1595 if (read.equalsIgnoreCase("1")) 1596 message.setStatus(true); 1597 else 1598 message.setStatus(false); 1599 1600 type = c.getInt(c.getColumnIndex(Sms.TYPE)); 1601 threadId = c.getInt(c.getColumnIndex(Sms.THREAD_ID)); 1602 message.setFolder(getFolderName(type, threadId)); 1603 1604 msgBody = c.getString(c.getColumnIndex(Sms.BODY)); 1605 1606 String phone = c.getString(c.getColumnIndex(Sms.ADDRESS)); 1607 1608 time = c.getLong(c.getColumnIndex(Sms.DATE)); 1609 if(type == 1) // Inbox message needs to set the vCard as originator 1610 setVCardFromPhoneNumber(message, phone, true); 1611 else // Other messages sets the vCard as the recipient 1612 setVCardFromPhoneNumber(message, phone, false); 1613 1614 if(charset == MAP_MESSAGE_CHARSET_NATIVE) { 1615 if(type == 1) //Inbox 1616 message.setSmsBodyPdus(BluetoothMapSmsPdu.getDeliverPdus(msgBody, phone, time)); 1617 else 1618 message.setSmsBodyPdus(BluetoothMapSmsPdu.getSubmitPdus(msgBody, phone)); 1619 } else /*if (charset == MAP_MESSAGE_CHARSET_UTF8)*/ { 1620 message.setSmsBody(msgBody); 1621 } 1622 } finally { 1623 close(c); 1624 } 1625 1626 return message.encode(); 1627 } 1628 1629 private void extractMmsAddresses(long id, BluetoothMapbMessageMms message) { 1630 final String[] projection = null; 1631 String selection = new String(Mms.Addr.MSG_ID + "=" + id); 1632 String uriStr = new String(Mms.CONTENT_URI + "/" + id + "/addr"); 1633 Uri uriAddress = Uri.parse(uriStr); 1634 String contactName = null; 1635 1636 Cursor c = mResolver.query( uriAddress, projection, selection, null, null); 1637 try { 1638 while (c != null && c.moveToNext()) { 1639 String address = c.getString(c.getColumnIndex(Mms.Addr.ADDRESS)); 1640 if(address.equals(INSERT_ADDRES_TOKEN)) 1641 continue; 1642 Integer type = c.getInt(c.getColumnIndex(Mms.Addr.TYPE)); 1643 switch(type) { 1644 case MMS_FROM: 1645 contactName = setVCardFromPhoneNumber(message, address, true); 1646 message.addFrom(contactName, address); 1647 break; 1648 case MMS_TO: 1649 contactName = setVCardFromPhoneNumber(message, address, false); 1650 message.addTo(contactName, address); 1651 break; 1652 case MMS_CC: 1653 contactName = setVCardFromPhoneNumber(message, address, false); 1654 message.addCc(contactName, address); 1655 break; 1656 case MMS_BCC: 1657 contactName = setVCardFromPhoneNumber(message, address, false); 1658 message.addBcc(contactName, address); 1659 break; 1660 default: 1661 break; 1662 } 1663 } 1664 } finally { 1665 close (c); 1666 } 1667 } 1668 1669 /** 1670 * Read out a mms data part and return the data in a byte array. 1671 * @param partid the content provider id of the mms. 1672 * @return 1673 */ 1674 private byte[] readMmsDataPart(long partid) { 1675 String uriStr = new String(Mms.CONTENT_URI + "/part/" + partid); 1676 Uri uriAddress = Uri.parse(uriStr); 1677 InputStream is = null; 1678 ByteArrayOutputStream os = new ByteArrayOutputStream(); 1679 int bufferSize = 8192; 1680 byte[] buffer = new byte[bufferSize]; 1681 byte[] retVal = null; 1682 1683 try { 1684 is = mResolver.openInputStream(uriAddress); 1685 int len = 0; 1686 while ((len = is.read(buffer)) != -1) { 1687 os.write(buffer, 0, len); // We need to specify the len, as it can be != bufferSize 1688 } 1689 retVal = os.toByteArray(); 1690 } catch (IOException e) { 1691 // do nothing for now 1692 Log.w(TAG,"Error reading part data",e); 1693 } finally { 1694 close(os); 1695 close(is); 1696 } 1697 return retVal; 1698 } 1699 1700 /** 1701 * Read out the mms parts and update the bMessage object provided i {@linkplain message} 1702 * @param id the content provider ID of the message 1703 * @param message the bMessage object to add the information to 1704 */ 1705 private void extractMmsParts(long id, BluetoothMapbMessageMms message) 1706 { 1707 /* Handling of filtering out non-text parts for exclude 1708 * attachments is handled within the bMessage object. */ 1709 final String[] projection = null; 1710 String selection = new String(Mms.Part.MSG_ID + "=" + id); 1711 String uriStr = new String(Mms.CONTENT_URI + "/"+ id + "/part"); 1712 Uri uriAddress = Uri.parse(uriStr); 1713 BluetoothMapbMessageMms.MimePart part; 1714 Cursor c = mResolver.query(uriAddress, projection, selection, null, null); 1715 1716 try { 1717 while(c != null && c.moveToNext()) { 1718 Long partId = c.getLong(c.getColumnIndex(BaseColumns._ID)); 1719 String contentType = c.getString(c.getColumnIndex(Mms.Part.CONTENT_TYPE)); 1720 String name = c.getString(c.getColumnIndex(Mms.Part.NAME)); 1721 String charset = c.getString(c.getColumnIndex(Mms.Part.CHARSET)); 1722 String filename = c.getString(c.getColumnIndex(Mms.Part.FILENAME)); 1723 String text = c.getString(c.getColumnIndex(Mms.Part.TEXT)); 1724 Integer fd = c.getInt(c.getColumnIndex(Mms.Part._DATA)); 1725 String cid = c.getString(c.getColumnIndex(Mms.Part.CONTENT_ID)); 1726 String cl = c.getString(c.getColumnIndex(Mms.Part.CONTENT_LOCATION)); 1727 String cdisp = c.getString(c.getColumnIndex(Mms.Part.CONTENT_DISPOSITION)); 1728 1729 if(V) Log.d(TAG, " _id : " + partId + 1730 "\n ct : " + contentType + 1731 "\n partname : " + name + 1732 "\n charset : " + charset + 1733 "\n filename : " + filename + 1734 "\n text : " + text + 1735 "\n fd : " + fd + 1736 "\n cid : " + cid + 1737 "\n cl : " + cl + 1738 "\n cdisp : " + cdisp); 1739 1740 part = message.addMimePart(); 1741 part.mContentType = contentType; 1742 part.mPartName = name; 1743 part.mContentId = cid; 1744 part.mContentLocation = cl; 1745 part.mContentDisposition = cdisp; 1746 1747 try { 1748 if(text != null) { 1749 part.mData = text.getBytes("UTF-8"); 1750 part.mCharsetName = "utf-8"; 1751 } else { 1752 part.mData = readMmsDataPart(partId); 1753 if(charset != null) 1754 part.mCharsetName = CharacterSets.getMimeName(Integer.parseInt(charset)); 1755 } 1756 } catch (NumberFormatException e) { 1757 Log.d(TAG,"extractMmsParts",e); 1758 part.mData = null; 1759 part.mCharsetName = null; 1760 } catch (UnsupportedEncodingException e) { 1761 Log.d(TAG,"extractMmsParts",e); 1762 part.mData = null; 1763 part.mCharsetName = null; 1764 } 1765 part.mFileName = filename; 1766 } 1767 } finally { 1768 close(c); 1769 } 1770 1771 message.updateCharset(); 1772 } 1773 1774 /** 1775 * 1776 * @param id the content provider id for the message to fetch. 1777 * @param appParams The application parameter object received from the client. 1778 * @return a byte[] containing the utf-8 encoded bMessage to send to the client. 1779 * @throws UnsupportedEncodingException if UTF-8 is not supported, 1780 * which is guaranteed to be supported on an android device 1781 */ 1782 public byte[] getMmsMessage(long id, BluetoothMapAppParams appParams) throws UnsupportedEncodingException { 1783 int msgBox, threadId; 1784 if (appParams.getCharset() == MAP_MESSAGE_CHARSET_NATIVE) 1785 throw new IllegalArgumentException("MMS charset native not allowed for MMS - must be utf-8"); 1786 1787 BluetoothMapbMessageMms message = new BluetoothMapbMessageMms(); 1788 Cursor c = mResolver.query(Mms.CONTENT_URI, MMS_PROJECTION, "_ID = " + id, null, null); 1789 if (c == null || !c.moveToFirst()) { 1790 throw new IllegalArgumentException("MMS handle not found"); 1791 } 1792 1793 try { 1794 message.setType(TYPE.MMS); 1795 1796 // The MMS info: 1797 String read = c.getString(c.getColumnIndex(Mms.READ)); 1798 if (read.equalsIgnoreCase("1")) 1799 message.setStatus(true); 1800 else 1801 message.setStatus(false); 1802 1803 msgBox = c.getInt(c.getColumnIndex(Mms.MESSAGE_BOX)); 1804 threadId = c.getInt(c.getColumnIndex(Mms.THREAD_ID)); 1805 message.setFolder(getFolderName(msgBox, threadId)); 1806 message.setSubject(c.getString(c.getColumnIndex(Mms.SUBJECT))); 1807 message.setMessageId(c.getString(c.getColumnIndex(Mms.MESSAGE_ID))); 1808 message.setContentType(c.getString(c.getColumnIndex(Mms.CONTENT_TYPE))); 1809 message.setDate(c.getLong(c.getColumnIndex(Mms.DATE)) * 1000L); 1810 message.setTextOnly(c.getInt(c.getColumnIndex(Mms.TEXT_ONLY)) == 0 ? false : true); 1811 message.setIncludeAttachments(appParams.getAttachment() == 0 ? false : true); 1812 1813 extractMmsParts(id, message); 1814 extractMmsAddresses(id, message); 1815 } finally { 1816 close(c); 1817 } 1818 1819 return message.encode(); 1820 } 1821 1822 /** 1823 * 1824 * @param id the content provider id for the message to fetch. 1825 * @param appParams The application parameter object received from the client. 1826 * @return a byte[] containing the utf-8 encoded bMessage to send to the client. 1827 * @throws UnsupportedEncodingException if UTF-8 is not supported, 1828 * which is guaranteed to be supported on an android device 1829 */ 1830 public byte[] getEmailMessage(long id, BluetoothMapAppParams appParams, 1831 BluetoothMapFolderElement currentFolder) throws UnsupportedEncodingException { 1832 // Log print out of application parameters set 1833 if(D && appParams != null) { 1834 Log.d(TAG,"TYPE_MESSAGE (GET): Attachment = " + appParams.getAttachment() + 1835 ", Charset = " + appParams.getCharset() + 1836 ", FractionRequest = " + appParams.getFractionRequest()); 1837 } 1838 1839 // Throw exception if requester NATIVE charset for Email 1840 // Exception is caught by MapObexServer sendGetMessageResp 1841 if (appParams.getCharset() == MAP_MESSAGE_CHARSET_NATIVE) 1842 throw new IllegalArgumentException("EMAIL charset not UTF-8"); 1843 1844 BluetoothMapbMessageEmail message = new BluetoothMapbMessageEmail(); 1845 Uri contentUri = Uri.parse(mBaseEmailUri + BluetoothMapContract.TABLE_MESSAGE); 1846 Cursor c = mResolver.query(contentUri, BluetoothMapContract.BT_MESSAGE_PROJECTION, "_ID = " + id, null, null); 1847 if (c != null && c.moveToFirst()) { 1848 throw new IllegalArgumentException("EMAIL handle not found"); 1849 } 1850 1851 try { 1852 BluetoothMapFolderElement folderElement; 1853 1854 // Handle fraction requests 1855 int fractionRequest = appParams.getFractionRequest(); 1856 if (fractionRequest != BluetoothMapAppParams.INVALID_VALUE_PARAMETER) { 1857 // Fraction requested 1858 if(V) { 1859 String fractionStr = (fractionRequest == 0) ? "FIRST" : "NEXT"; 1860 Log.v(TAG, "getEmailMessage - FractionRequest " + fractionStr 1861 + " - send compete message" ); 1862 } 1863 // Check if message is complete and if not - request message from server 1864 if (c.getString(c.getColumnIndex( 1865 BluetoothMapContract.MessageColumns.RECEPTION_STATE)).equalsIgnoreCase( 1866 BluetoothMapContract.RECEPTION_STATE_COMPLETE) == false) { 1867 // TODO: request message from server 1868 Log.w(TAG, "getEmailMessage - receptionState not COMPLETE - Not Implemented!" ); 1869 } 1870 } 1871 // Set read status: 1872 String read = c.getString(c.getColumnIndex(BluetoothMapContract.MessageColumns.FLAG_READ)); 1873 if (read != null && read.equalsIgnoreCase("1")) 1874 message.setStatus(true); 1875 else 1876 message.setStatus(false); 1877 1878 // Set message type: 1879 message.setType(TYPE.EMAIL); 1880 1881 // Set folder: 1882 long folderId = c.getLong(c.getColumnIndex(BluetoothMapContract.MessageColumns.FOLDER_ID)); 1883 folderElement = currentFolder.getEmailFolderById(folderId); 1884 message.setCompleteFolder(folderElement.getFullPath()); 1885 1886 // Set recipient: 1887 String nameEmail = c.getString(c.getColumnIndex(BluetoothMapContract.MessageColumns.TO_LIST)); 1888 Rfc822Token tokens[] = Rfc822Tokenizer.tokenize(nameEmail); 1889 if (tokens.length != 0) { 1890 if(D) Log.d(TAG, "Recipient count= " + tokens.length); 1891 int i = 0; 1892 while (i < tokens.length) { 1893 if(V) Log.d(TAG, "Recipient = " + tokens[i].toString()); 1894 String[] emails = new String[1]; 1895 emails[0] = tokens[i].getAddress(); 1896 String name = tokens[i].getName(); 1897 message.addRecipient(name, name, null, emails); 1898 i++; 1899 } 1900 } 1901 1902 // Set originator: 1903 nameEmail = c.getString(c.getColumnIndex(BluetoothMapContract.MessageColumns.FROM_LIST)); 1904 tokens = Rfc822Tokenizer.tokenize(nameEmail); 1905 if (tokens.length != 0) { 1906 if(D) Log.d(TAG, "Originator count= " + tokens.length); 1907 int i = 0; 1908 while (i < tokens.length) { 1909 if(V) Log.d(TAG, "Originator = " + tokens[i].toString()); 1910 String[] emails = new String[1]; 1911 emails[0] = tokens[i].getAddress(); 1912 String name = tokens[i].getName(); 1913 message.addOriginator(name, name, null, emails); 1914 i++; 1915 } 1916 } 1917 1918 // Find out if we get attachments 1919 String attStr = (appParams.getAttachment() == 0) ? "/" + BluetoothMapContract.FILE_MSG_NO_ATTACHMENTS : ""; 1920 Uri uri = Uri.parse(contentUri + "/" + id + attStr); 1921 1922 // Get email message body content 1923 int count = 0; 1924 FileInputStream is = null; 1925 ParcelFileDescriptor fd = null; 1926 1927 try { 1928 fd = mResolver.openFileDescriptor(uri, "r"); 1929 is = new FileInputStream(fd.getFileDescriptor()); 1930 StringBuilder email = new StringBuilder(""); 1931 byte[] buffer = new byte[1024]; 1932 while((count = is.read(buffer)) != -1) { 1933 // TODO: Handle breaks within a UTF8 character 1934 email.append(new String(buffer,0,count)); 1935 if(V) Log.d(TAG, "Email part = " + new String(buffer,0,count) + " count=" + count); 1936 } 1937 // Set email message body: 1938 message.setEmailBody(email.toString()); 1939 } catch (FileNotFoundException e) { 1940 Log.w(TAG, e); 1941 } catch (NullPointerException e) { 1942 Log.w(TAG, e); 1943 } catch (IOException e) { 1944 Log.w(TAG, e); 1945 } finally { 1946 close(is); 1947 close(fd); 1948 } 1949 } finally { 1950 close(c); 1951 } 1952 1953 return message.encode(); 1954 } 1955} 1956