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