1/* 2* Copyright (C) 2013 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 java.io.ByteArrayOutputStream; 18import java.io.IOException; 19import java.io.InputStream; 20import java.io.UnsupportedEncodingException; 21import java.text.ParseException; 22 23import org.apache.http.util.ByteArrayBuffer; 24 25import android.content.ContentResolver; 26import android.content.ContentValues; 27import android.content.Context; 28import android.database.Cursor; 29import android.net.Uri; 30import android.provider.BaseColumns; 31import android.provider.ContactsContract; 32import android.provider.ContactsContract.Contacts; 33import android.provider.ContactsContract.PhoneLookup; 34import android.provider.Telephony.Mms; 35import android.provider.Telephony.Sms; 36import android.telephony.TelephonyManager; 37import android.util.Log; 38 39import com.android.bluetooth.map.BluetoothMapUtils.TYPE; 40import com.google.android.mms.pdu.CharacterSets; 41 42public class BluetoothMapContent { 43 private static final String TAG = "BluetoothMapContent"; 44 45 private static final boolean D = false; 46 private static final boolean V = false; 47 48 private static final int MASK_SUBJECT = 0x1; 49 private static final int MASK_DATETIME = 0x2; 50 private static final int MASK_SENDER_NAME = 0x4; 51 private static final int MASK_SENDER_ADDRESSING = 0x8; 52 53 private static final int MASK_RECIPIENT_NAME = 0x10; 54 private static final int MASK_RECIPIENT_ADDRESSING = 0x20; 55 private static final int MASK_TYPE = 0x40; 56 private static final int MASK_SIZE = 0x80; 57 58 private static final int MASK_RECEPTION_STATUS = 0x100; 59 private static final int MASK_TEXT = 0x200; 60 private static final int MASK_ATTACHMENT_SIZE = 0x400; 61 private static final int MASK_PRIORITY = 0x800; 62 63 private static final int MASK_READ = 0x1000; 64 private static final int MASK_SENT = 0x2000; 65 private static final int MASK_PROTECTED = 0x4000; 66 private static final int MASK_REPLYTO_ADDRESSING = 0x8000; 67 68 /* Type of MMS address. From Telephony.java it must be one of PduHeaders.BCC, */ 69 /* PduHeaders.CC, PduHeaders.FROM, PduHeaders.TO. These are from PduHeaders.java */ 70 public static final int MMS_FROM = 0x89; 71 public static final int MMS_TO = 0x97; 72 public static final int MMS_BCC = 0x81; 73 public static final int MMS_CC = 0x82; 74 75 private Context mContext; 76 private ContentResolver mResolver; 77 78 static final String[] SMS_PROJECTION = new String[] { 79 BaseColumns._ID, 80 Sms.THREAD_ID, 81 Sms.ADDRESS, 82 Sms.BODY, 83 Sms.DATE, 84 Sms.READ, 85 Sms.TYPE, 86 Sms.STATUS, 87 Sms.LOCKED, 88 Sms.ERROR_CODE, 89 }; 90 91 static final String[] MMS_PROJECTION = new String[] { 92 BaseColumns._ID, 93 Mms.THREAD_ID, 94 Mms.MESSAGE_ID, 95 Mms.MESSAGE_SIZE, 96 Mms.SUBJECT, 97 Mms.CONTENT_TYPE, 98 Mms.TEXT_ONLY, 99 Mms.DATE, 100 Mms.DATE_SENT, 101 Mms.READ, 102 Mms.MESSAGE_BOX, 103 Mms.STATUS, 104 }; 105 106 private class FilterInfo { 107 public static final int TYPE_SMS = 0; 108 public static final int TYPE_MMS = 1; 109 110 int msgType = TYPE_SMS; 111 int phoneType = 0; 112 String phoneNum = null; 113 String phoneAlphaTag = null; 114 } 115 116 public BluetoothMapContent(final Context context) { 117 mContext = context; 118 mResolver = mContext.getContentResolver(); 119 if (mResolver == null) { 120 Log.d(TAG, "getContentResolver failed"); 121 } 122 } 123 124 private void addSmsEntry() { 125 if (D) Log.d(TAG, "*** Adding dummy sms ***"); 126 127 ContentValues mVal = new ContentValues(); 128 mVal.put(Sms.ADDRESS, "1234"); 129 mVal.put(Sms.BODY, "Hello!!!"); 130 mVal.put(Sms.DATE, System.currentTimeMillis()); 131 mVal.put(Sms.READ, "0"); 132 133 Uri mUri = mResolver.insert(Sms.CONTENT_URI, mVal); 134 } 135 136 private BluetoothMapAppParams buildAppParams() { 137 BluetoothMapAppParams ap = new BluetoothMapAppParams(); 138 try { 139 int paramMask = (MASK_SUBJECT 140 | MASK_DATETIME 141 | MASK_SENDER_NAME 142 | MASK_SENDER_ADDRESSING 143 | MASK_RECIPIENT_NAME 144 | MASK_RECIPIENT_ADDRESSING 145 | MASK_TYPE 146 | MASK_SIZE 147 | MASK_RECEPTION_STATUS 148 | MASK_TEXT 149 | MASK_ATTACHMENT_SIZE 150 | MASK_PRIORITY 151 | MASK_READ 152 | MASK_SENT 153 | MASK_PROTECTED 154 ); 155 ap.setMaxListCount(5); 156 ap.setStartOffset(0); 157 ap.setFilterMessageType(0); 158 ap.setFilterPeriodBegin("20130101T000000"); 159 ap.setFilterPeriodEnd("20131230T000000"); 160 ap.setFilterReadStatus(0); 161 ap.setParameterMask(paramMask); 162 ap.setSubjectLength(10); 163 /* ap.setFilterOriginator("Sms*"); */ 164 /* ap.setFilterRecipient("41*"); */ 165 } catch (ParseException e) { 166 return null; 167 } 168 return ap; 169 } 170 171 private void printSms(Cursor c) { 172 String body = c.getString(c.getColumnIndex(Sms.BODY)); 173 if (D) Log.d(TAG, "printSms " + BaseColumns._ID + ": " + c.getLong(c.getColumnIndex(BaseColumns._ID)) + 174 " " + Sms.THREAD_ID + " : " + c.getLong(c.getColumnIndex(Sms.THREAD_ID)) + 175 " " + Sms.ADDRESS + " : " + c.getString(c.getColumnIndex(Sms.ADDRESS)) + 176 " " + Sms.BODY + " : " + body.substring(0, Math.min(body.length(), 8)) + 177 " " + Sms.DATE + " : " + c.getLong(c.getColumnIndex(Sms.DATE)) + 178 " " + Sms.TYPE + " : " + c.getInt(c.getColumnIndex(Sms.TYPE))); 179 } 180 181 private void printMms(Cursor c) { 182 if (D) Log.d(TAG, "printMms " + BaseColumns._ID + ": " + c.getLong(c.getColumnIndex(BaseColumns._ID)) + 183 "\n " + Mms.THREAD_ID + " : " + c.getLong(c.getColumnIndex(Mms.THREAD_ID)) + 184 "\n " + Mms.MESSAGE_ID + " : " + c.getString(c.getColumnIndex(Mms.MESSAGE_ID)) + 185 "\n " + Mms.SUBJECT + " : " + c.getString(c.getColumnIndex(Mms.SUBJECT)) + 186 "\n " + Mms.CONTENT_TYPE + " : " + c.getString(c.getColumnIndex(Mms.CONTENT_TYPE)) + 187 "\n " + Mms.TEXT_ONLY + " : " + c.getInt(c.getColumnIndex(Mms.TEXT_ONLY)) + 188 "\n " + Mms.DATE + " : " + c.getLong(c.getColumnIndex(Mms.DATE)) + 189 "\n " + Mms.DATE_SENT + " : " + c.getLong(c.getColumnIndex(Mms.DATE_SENT)) + 190 "\n " + Mms.READ + " : " + c.getInt(c.getColumnIndex(Mms.READ)) + 191 "\n " + Mms.MESSAGE_BOX + " : " + c.getInt(c.getColumnIndex(Mms.MESSAGE_BOX)) + 192 "\n " + Mms.STATUS + " : " + c.getInt(c.getColumnIndex(Mms.STATUS))); 193 } 194 195 private void printMmsAddr(long id) { 196 final String[] projection = null; 197 String selection = new String("msg_id=" + id); 198 String uriStr = String.format("content://mms/%d/addr", id); 199 Uri uriAddress = Uri.parse(uriStr); 200 Cursor c = mResolver.query( 201 uriAddress, 202 projection, 203 selection, 204 null, null); 205 206 if (c.moveToFirst()) { 207 do { 208 String add = c.getString(c.getColumnIndex("address")); 209 Integer type = c.getInt(c.getColumnIndex("type")); 210 if (type == MMS_TO) { 211 if (D) Log.d(TAG, " recipient: " + add + " (type: " + type + ")"); 212 } else if (type == MMS_FROM) { 213 if (D) Log.d(TAG, " originator: " + add + " (type: " + type + ")"); 214 } else { 215 if (D) Log.d(TAG, " address other: " + add + " (type: " + type + ")"); 216 } 217 218 } while(c.moveToNext()); 219 } 220 } 221 222 private void printMmsPartImage(long partid) { 223 String uriStr = String.format("content://mms/part/%d", partid); 224 Uri uriAddress = Uri.parse(uriStr); 225 int ch; 226 StringBuffer sb = new StringBuffer(""); 227 InputStream is = null; 228 229 try { 230 is = mResolver.openInputStream(uriAddress); 231 232 while ((ch = is.read()) != -1) { 233 sb.append((char)ch); 234 } 235 if (D) Log.d(TAG, sb.toString()); 236 237 } catch (IOException e) { 238 // do nothing for now 239 e.printStackTrace(); 240 } 241 } 242 243 private void printMmsParts(long id) { 244 final String[] projection = null; 245 String selection = new String("mid=" + id); 246 String uriStr = String.format("content://mms/%d/part", id); 247 Uri uriAddress = Uri.parse(uriStr); 248 Cursor c = mResolver.query( 249 uriAddress, 250 projection, 251 selection, 252 null, null); 253 254 if (D) Log.d(TAG, " parts:"); 255 if (c.moveToFirst()) { 256 do { 257 Long partid = c.getLong(c.getColumnIndex(BaseColumns._ID)); 258 String ct = c.getString(c.getColumnIndex("ct")); 259 String name = c.getString(c.getColumnIndex("name")); 260 String charset = c.getString(c.getColumnIndex("chset")); 261 String filename = c.getString(c.getColumnIndex("fn")); 262 String text = c.getString(c.getColumnIndex("text")); 263 Integer fd = c.getInt(c.getColumnIndex("_data")); 264 String cid = c.getString(c.getColumnIndex("cid")); 265 String cl = c.getString(c.getColumnIndex("cl")); 266 String cdisp = c.getString(c.getColumnIndex("cd")); 267 268 if (D) Log.d(TAG, " _id : " + partid + 269 "\n ct : " + ct + 270 "\n partname : " + name + 271 "\n charset : " + charset + 272 "\n filename : " + filename + 273 "\n text : " + text + 274 "\n fd : " + fd + 275 "\n cid : " + cid + 276 "\n cl : " + cl + 277 "\n cdisp : " + cdisp); 278 279 /* if (ct.equals("image/jpeg")) { */ 280 /* printMmsPartImage(partid); */ 281 /* } */ 282 } while(c.moveToNext()); 283 } 284 } 285 286 public void dumpMmsTable() { 287 if (D) Log.d(TAG, "**** Dump of mms table ****"); 288 Cursor c = mResolver.query(Mms.CONTENT_URI, 289 MMS_PROJECTION, null, null, "_id DESC"); 290 if (c != null) { 291 if (D) Log.d(TAG, "c.getCount() = " + c.getCount()); 292 c.moveToPosition(-1); 293 while (c.moveToNext()) { 294 printMms(c); 295 long id = c.getLong(c.getColumnIndex(BaseColumns._ID)); 296 printMmsAddr(id); 297 printMmsParts(id); 298 } 299 } else { 300 Log.d(TAG, "query failed"); 301 c.close(); 302 } 303 } 304 305 public void dumpSmsTable() { 306 addSmsEntry(); 307 if (D) Log.d(TAG, "**** Dump of sms table ****"); 308 Cursor c = mResolver.query(Sms.CONTENT_URI, 309 SMS_PROJECTION, null, null, "_id DESC"); 310 if (c != null) { 311 if (D) Log.d(TAG, "c.getCount() = " + c.getCount()); 312 c.moveToPosition(-1); 313 while (c.moveToNext()) { 314 printSms(c); 315 } 316 } else { 317 Log.d(TAG, "query failed"); 318 c.close(); 319 } 320 321 } 322 323 public void dumpMessages() { 324 dumpSmsTable(); 325 dumpMmsTable(); 326 327 BluetoothMapAppParams ap = buildAppParams(); 328 if (D) Log.d(TAG, "message listing size = " + msgListingSize("inbox", ap)); 329 BluetoothMapMessageListing mList = msgListing("inbox", ap); 330 try { 331 mList.encode(); 332 } catch (UnsupportedEncodingException ex) { 333 /* do nothing */ 334 } 335 mList = msgListing("sent", ap); 336 try { 337 mList.encode(); 338 } catch (UnsupportedEncodingException ex) { 339 /* do nothing */ 340 } 341 } 342 343 private void setProtected(BluetoothMapMessageListingElement e, Cursor c, 344 FilterInfo fi, BluetoothMapAppParams ap) { 345 if ((ap.getParameterMask() & MASK_PROTECTED) != 0) { 346 String protect = "no"; 347 if (D) Log.d(TAG, "setProtected: " + protect); 348 e.setProtect(protect); 349 } 350 } 351 352 private void setSent(BluetoothMapMessageListingElement e, Cursor c, 353 FilterInfo fi, BluetoothMapAppParams ap) { 354 if ((ap.getParameterMask() & MASK_SENT) != 0) { 355 int msgType = 0; 356 if (fi.msgType == FilterInfo.TYPE_SMS) { 357 msgType = c.getInt(c.getColumnIndex(Sms.TYPE)); 358 } else if (fi.msgType == FilterInfo.TYPE_MMS) { 359 msgType = c.getInt(c.getColumnIndex(Mms.MESSAGE_BOX)); 360 } 361 String sent = null; 362 if (msgType == 2) { 363 sent = "yes"; 364 } else { 365 sent = "no"; 366 } 367 if (D) Log.d(TAG, "setSent: " + sent); 368 e.setSent(sent); 369 } 370 } 371 372 private void setRead(BluetoothMapMessageListingElement e, Cursor c, 373 FilterInfo fi, BluetoothMapAppParams ap) { 374 int read = 0; 375 if (fi.msgType == FilterInfo.TYPE_SMS) { 376 read = c.getInt(c.getColumnIndex(Sms.READ)); 377 } else if (fi.msgType == FilterInfo.TYPE_MMS) { 378 read = c.getInt(c.getColumnIndex(Mms.READ)); 379 } 380 String setread = null; 381 if (read == 1) { 382 setread = "yes"; 383 } else { 384 setread = "no"; 385 } 386 if (D) Log.d(TAG, "setRead: " + setread); 387 e.setRead(setread, ((ap.getParameterMask() & MASK_READ) != 0)); 388 } 389 390 private void setPriority(BluetoothMapMessageListingElement e, Cursor c, 391 FilterInfo fi, BluetoothMapAppParams ap) { 392 if ((ap.getParameterMask() & MASK_PRIORITY) != 0) { 393 String priority = "no"; 394 if (D) Log.d(TAG, "setPriority: " + priority); 395 e.setPriority(priority); 396 } 397 } 398 399 /** 400 * For SMS we set the attachment size to 0, as all data will be text data, hence 401 * attachments for SMS is not possible. 402 * For MMS all data is actually attachments, hence we do set the attachment size to 403 * the total message size. To provide a more accurate attachment size, one could 404 * extract the length (in bytes) of the text parts. 405 */ 406 private void setAttachmentSize(BluetoothMapMessageListingElement e, Cursor c, 407 FilterInfo fi, BluetoothMapAppParams ap) { 408 if ((ap.getParameterMask() & MASK_ATTACHMENT_SIZE) != 0) { 409 int size = 0; 410 if (fi.msgType == FilterInfo.TYPE_MMS) { 411 size = c.getInt(c.getColumnIndex(Mms.MESSAGE_SIZE)); 412 } 413 if (D) Log.d(TAG, "setAttachmentSize: " + size); 414 e.setAttachmentSize(size); 415 } 416 } 417 418 private void setText(BluetoothMapMessageListingElement e, Cursor c, 419 FilterInfo fi, BluetoothMapAppParams ap) { 420 if ((ap.getParameterMask() & MASK_TEXT) != 0) { 421 String hasText = ""; 422 if (fi.msgType == FilterInfo.TYPE_SMS) { 423 hasText = "yes"; 424 } else if (fi.msgType == FilterInfo.TYPE_MMS) { 425 int textOnly = c.getInt(c.getColumnIndex(Mms.TEXT_ONLY)); 426 if (textOnly == 1) { 427 hasText = "yes"; 428 } else { 429 long id = c.getLong(c.getColumnIndex(BaseColumns._ID)); 430 String text = getTextPartsMms(id); 431 if (text != null && text.length() > 0) { 432 hasText = "yes"; 433 } else { 434 hasText = "no"; 435 } 436 } 437 } 438 if (D) Log.d(TAG, "setText: " + hasText); 439 e.setText(hasText); 440 } 441 } 442 443 private void setReceptionStatus(BluetoothMapMessageListingElement e, Cursor c, 444 FilterInfo fi, BluetoothMapAppParams ap) { 445 if ((ap.getParameterMask() & MASK_RECEPTION_STATUS) != 0) { 446 String status = "complete"; 447 if (D) Log.d(TAG, "setReceptionStatus: " + status); 448 e.setReceptionStatus(status); 449 } 450 } 451 452 private void setSize(BluetoothMapMessageListingElement e, Cursor c, 453 FilterInfo fi, BluetoothMapAppParams ap) { 454 if ((ap.getParameterMask() & MASK_SIZE) != 0) { 455 int size = 0; 456 if (fi.msgType == FilterInfo.TYPE_SMS) { 457 String subject = c.getString(c.getColumnIndex(Sms.BODY)); 458 size = subject.length(); 459 } else if (fi.msgType == FilterInfo.TYPE_MMS) { 460 size = c.getInt(c.getColumnIndex(Mms.MESSAGE_SIZE)); 461 } 462 if (D) Log.d(TAG, "setSize: " + size); 463 e.setSize(size); 464 } 465 } 466 467 private void setType(BluetoothMapMessageListingElement e, Cursor c, 468 FilterInfo fi, BluetoothMapAppParams ap) { 469 if ((ap.getParameterMask() & MASK_TYPE) != 0) { 470 TYPE type = null; 471 if (fi.msgType == FilterInfo.TYPE_SMS) { 472 if (fi.phoneType == TelephonyManager.PHONE_TYPE_GSM) { 473 type = TYPE.SMS_GSM; 474 } else if (fi.phoneType == TelephonyManager.PHONE_TYPE_CDMA) { 475 type = TYPE.SMS_CDMA; 476 } 477 } else if (fi.msgType == FilterInfo.TYPE_MMS) { 478 type = TYPE.MMS; 479 } 480 if (D) Log.d(TAG, "setType: " + type); 481 e.setType(type); 482 } 483 } 484 485 private void setRecipientAddressing(BluetoothMapMessageListingElement e, Cursor c, 486 FilterInfo fi, BluetoothMapAppParams ap) { 487 if ((ap.getParameterMask() & MASK_RECIPIENT_ADDRESSING) != 0) { 488 String address = null; 489 if (fi.msgType == FilterInfo.TYPE_SMS) { 490 int msgType = c.getInt(c.getColumnIndex(Sms.TYPE)); 491 if (msgType == 1) { 492 address = fi.phoneNum; 493 } else { 494 address = c.getString(c.getColumnIndex(Sms.ADDRESS)); 495 } 496 } else if (fi.msgType == FilterInfo.TYPE_MMS) { 497 long id = c.getLong(c.getColumnIndex(BaseColumns._ID)); 498 address = getAddressMms(mResolver, id, MMS_TO); 499 } 500 if (D) Log.d(TAG, "setRecipientAddressing: " + address); 501 e.setRecipientAddressing(address); 502 } 503 } 504 505 private void setRecipientName(BluetoothMapMessageListingElement e, Cursor c, 506 FilterInfo fi, BluetoothMapAppParams ap) { 507 if ((ap.getParameterMask() & MASK_RECIPIENT_NAME) != 0) { 508 String name = null; 509 if (fi.msgType == FilterInfo.TYPE_SMS) { 510 int msgType = c.getInt(c.getColumnIndex(Sms.TYPE)); 511 if (msgType != 1) { 512 String phone = c.getString(c.getColumnIndex(Sms.ADDRESS)); 513 name = getContactNameFromPhone(phone); 514 } else { 515 name = fi.phoneAlphaTag; 516 } 517 } else if (fi.msgType == FilterInfo.TYPE_MMS) { 518 long id = c.getLong(c.getColumnIndex(BaseColumns._ID)); 519 String phone = getAddressMms(mResolver, id, MMS_TO); 520 name = getContactNameFromPhone(phone); 521 } 522 if (D) Log.d(TAG, "setRecipientName: " + name); 523 e.setRecipientName(name); 524 } 525 } 526 527 private void setSenderAddressing(BluetoothMapMessageListingElement e, Cursor c, 528 FilterInfo fi, BluetoothMapAppParams ap) { 529 if ((ap.getParameterMask() & MASK_SENDER_ADDRESSING) != 0) { 530 String address = null; 531 if (fi.msgType == FilterInfo.TYPE_SMS) { 532 int msgType = c.getInt(c.getColumnIndex(Sms.TYPE)); 533 if (msgType == 1) { 534 address = c.getString(c.getColumnIndex(Sms.ADDRESS)); 535 } else { 536 address = fi.phoneNum; 537 } 538 } else if (fi.msgType == FilterInfo.TYPE_MMS) { 539 long id = c.getLong(c.getColumnIndex(BaseColumns._ID)); 540 address = getAddressMms(mResolver, id, MMS_FROM); 541 } 542 if (D) Log.d(TAG, "setSenderAddressing: " + address); 543 e.setSenderAddressing(address); 544 } 545 } 546 547 private void setSenderName(BluetoothMapMessageListingElement e, Cursor c, 548 FilterInfo fi, BluetoothMapAppParams ap) { 549 if ((ap.getParameterMask() & MASK_SENDER_NAME) != 0) { 550 String name = null; 551 if (fi.msgType == FilterInfo.TYPE_SMS) { 552 int msgType = c.getInt(c.getColumnIndex(Sms.TYPE)); 553 if (msgType == 1) { 554 String phone = c.getString(c.getColumnIndex(Sms.ADDRESS)); 555 name = getContactNameFromPhone(phone); 556 } else { 557 name = fi.phoneAlphaTag; 558 } 559 } else if (fi.msgType == FilterInfo.TYPE_MMS) { 560 long id = c.getLong(c.getColumnIndex(BaseColumns._ID)); 561 String phone = getAddressMms(mResolver, id, MMS_FROM); 562 name = getContactNameFromPhone(phone); 563 } 564 if (D) Log.d(TAG, "setSenderName: " + name); 565 e.setSenderName(name); 566 } 567 } 568 569 private void setDateTime(BluetoothMapMessageListingElement e, Cursor c, 570 FilterInfo fi, BluetoothMapAppParams ap) { 571 long date = 0; 572 573 if (fi.msgType == FilterInfo.TYPE_SMS) { 574 date = c.getLong(c.getColumnIndex(Sms.DATE)); 575 } else if (fi.msgType == FilterInfo.TYPE_MMS) { 576 /* Use Mms.DATE for all messages. Although contract class states */ 577 /* Mms.DATE_SENT are for outgoing messages. But that is not working. */ 578 date = c.getLong(c.getColumnIndex(Mms.DATE)) * 1000L; 579 580 /* int msgBox = c.getInt(c.getColumnIndex(Mms.MESSAGE_BOX)); */ 581 /* if (msgBox == Mms.MESSAGE_BOX_INBOX) { */ 582 /* date = c.getLong(c.getColumnIndex(Mms.DATE)) * 1000L; */ 583 /* } else { */ 584 /* date = c.getLong(c.getColumnIndex(Mms.DATE_SENT)) * 1000L; */ 585 /* } */ 586 } 587 e.setDateTime(date); 588 } 589 590 private String getTextPartsMms(long id) { 591 String text = ""; 592 String selection = new String("mid=" + id); 593 String uriStr = String.format("content://mms/%d/part", id); 594 Uri uriAddress = Uri.parse(uriStr); 595 Cursor c = mResolver.query(uriAddress, null, selection, 596 null, null); 597 598 if (c != null && c.moveToFirst()) { 599 do { 600 String ct = c.getString(c.getColumnIndex("ct")); 601 if (ct.equals("text/plain")) { 602 text += c.getString(c.getColumnIndex("text")); 603 } 604 } while(c.moveToNext()); 605 } 606 if (c != null) { 607 c.close(); 608 } 609 return text; 610 } 611 612 private void setSubject(BluetoothMapMessageListingElement e, Cursor c, 613 FilterInfo fi, BluetoothMapAppParams ap) { 614 String subject = ""; 615 int subLength = ap.getSubjectLength(); 616 if(subLength == BluetoothMapAppParams.INVALID_VALUE_PARAMETER) 617 subLength = 256; 618 619 if ((ap.getParameterMask() & MASK_SUBJECT) != 0) { 620 if (fi.msgType == FilterInfo.TYPE_SMS) { 621 subject = c.getString(c.getColumnIndex(Sms.BODY)); 622 } else if (fi.msgType == FilterInfo.TYPE_MMS) { 623 subject = c.getString(c.getColumnIndex(Mms.SUBJECT)); 624 if (subject == null || subject.length() == 0) { 625 /* Get subject from mms text body parts - if any exists */ 626 long id = c.getLong(c.getColumnIndex(BaseColumns._ID)); 627 subject = getTextPartsMms(id); 628 } 629 } 630 if (subject != null) { 631 subject = subject.substring(0, Math.min(subject.length(), 632 subLength)); 633 } 634 if (D) Log.d(TAG, "setSubject: " + subject); 635 e.setSubject(subject); 636 } 637 } 638 639 private void setHandle(BluetoothMapMessageListingElement e, Cursor c, 640 FilterInfo fi, BluetoothMapAppParams ap) { 641 long handle = c.getLong(c.getColumnIndex(BaseColumns._ID)); 642 TYPE type = null; 643 if (fi.msgType == FilterInfo.TYPE_SMS) { 644 if (fi.phoneType == TelephonyManager.PHONE_TYPE_GSM) { 645 type = TYPE.SMS_GSM; 646 } else if (fi.phoneType == TelephonyManager.PHONE_TYPE_CDMA) { 647 type = TYPE.SMS_CDMA; 648 } 649 } else if (fi.msgType == FilterInfo.TYPE_MMS) { 650 type = TYPE.MMS; 651 } 652 if (D) Log.d(TAG, "setHandle: " + handle + " - Type: " + type.name()); 653 e.setHandle(handle, type); 654 } 655 656 private BluetoothMapMessageListingElement element(Cursor c, FilterInfo fi, 657 BluetoothMapAppParams ap) { 658 BluetoothMapMessageListingElement e = new BluetoothMapMessageListingElement(); 659 660 setHandle(e, c, fi, ap); 661 setSubject(e, c, fi, ap); 662 setDateTime(e, c, fi, ap); 663 setSenderName(e, c, fi, ap); 664 setSenderAddressing(e, c, fi, ap); 665 setRecipientName(e, c, fi, ap); 666 setRecipientAddressing(e, c, fi, ap); 667 setType(e, c, fi, ap); 668 setSize(e, c, fi, ap); 669 setReceptionStatus(e, c, fi, ap); 670 setText(e, c, fi, ap); 671 setAttachmentSize(e, c, fi, ap); 672 setPriority(e, c, fi, ap); 673 setRead(e, c, fi, ap); 674 setSent(e, c, fi, ap); 675 setProtected(e, c, fi, ap); 676 return e; 677 } 678 679 private String getContactNameFromPhone(String phone) { 680 String name = null; 681 682 Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, 683 Uri.encode(phone)); 684 685 String[] projection = {Contacts._ID, Contacts.DISPLAY_NAME}; 686 String selection = Contacts.IN_VISIBLE_GROUP + "=1"; 687 String orderBy = Contacts.DISPLAY_NAME + " ASC"; 688 689 Cursor c = mResolver.query(uri, projection, selection, null, orderBy); 690 691 if (c != null && c.getCount() >= 1) { 692 c.moveToFirst(); 693 name = c.getString(c.getColumnIndex(Contacts.DISPLAY_NAME)); 694 } 695 696 c.close(); 697 return name; 698 } 699 700 static public String getAddressMms(ContentResolver r, long id, int type) { 701 String selection = new String("msg_id=" + id + " AND type=" + type); 702 String uriStr = String.format("content://mms/%d/addr", id); 703 Uri uriAddress = Uri.parse(uriStr); 704 String addr = null; 705 Cursor c = r.query(uriAddress, null, selection, null, null); 706 707 if (c != null && c.moveToFirst()) { 708 addr = c.getString(c.getColumnIndex("address")); 709 } 710 711 if (c != null) { 712 c.close(); 713 } 714 return addr; 715 } 716 717 private boolean matchRecipientMms(Cursor c, FilterInfo fi, String recip) { 718 boolean res; 719 long id = c.getLong(c.getColumnIndex(BaseColumns._ID)); 720 String phone = getAddressMms(mResolver, id, MMS_TO); 721 if (phone != null && phone.length() > 0) { 722 if (phone.matches(recip)) { 723 if (D) Log.d(TAG, "match recipient phone = " + phone); 724 res = true; 725 } else { 726 String name = getContactNameFromPhone(phone); 727 if (name != null && name.length() > 0 && name.matches(recip)) { 728 if (D) Log.d(TAG, "match recipient name = " + name); 729 res = true; 730 } else { 731 res = false; 732 } 733 } 734 } else { 735 res = false; 736 } 737 return res; 738 } 739 740 private boolean matchRecipientSms(Cursor c, FilterInfo fi, String recip) { 741 boolean res; 742 int msgType = c.getInt(c.getColumnIndex(Sms.TYPE)); 743 if (msgType == 1) { 744 String phone = fi.phoneNum; 745 String name = fi.phoneAlphaTag; 746 if (phone != null && phone.length() > 0 && phone.matches(recip)) { 747 if (D) Log.d(TAG, "match recipient phone = " + phone); 748 res = true; 749 } else if (name != null && name.length() > 0 && name.matches(recip)) { 750 if (D) Log.d(TAG, "match recipient name = " + name); 751 res = true; 752 } else { 753 res = false; 754 } 755 } 756 else { 757 String phone = c.getString(c.getColumnIndex(Sms.ADDRESS)); 758 if (phone != null && phone.length() > 0) { 759 if (phone.matches(recip)) { 760 if (D) Log.d(TAG, "match recipient phone = " + phone); 761 res = true; 762 } else { 763 String name = getContactNameFromPhone(phone); 764 if (name != null && name.length() > 0 && name.matches(recip)) { 765 if (D) Log.d(TAG, "match recipient name = " + name); 766 res = true; 767 } else { 768 res = false; 769 } 770 } 771 } else { 772 res = false; 773 } 774 } 775 return res; 776 } 777 778 private boolean matchRecipient(Cursor c, FilterInfo fi, BluetoothMapAppParams ap) { 779 boolean res; 780 String recip = ap.getFilterRecipient(); 781 if (recip != null && recip.length() > 0) { 782 recip = recip.replace("*", ".*"); 783 recip = ".*" + recip + ".*"; 784 if (fi.msgType == FilterInfo.TYPE_SMS) { 785 res = matchRecipientSms(c, fi, recip); 786 } else if (fi.msgType == FilterInfo.TYPE_MMS) { 787 res = matchRecipientMms(c, fi, recip); 788 } else { 789 if (D) Log.d(TAG, "Unknown msg type: " + fi.msgType); 790 res = false; 791 } 792 } else { 793 res = true; 794 } 795 return res; 796 } 797 798 private boolean matchOriginatorMms(Cursor c, FilterInfo fi, String orig) { 799 boolean res; 800 long id = c.getLong(c.getColumnIndex(BaseColumns._ID)); 801 String phone = getAddressMms(mResolver, id, MMS_FROM); 802 if (phone != null && phone.length() > 0) { 803 if (phone.matches(orig)) { 804 if (D) Log.d(TAG, "match originator phone = " + phone); 805 res = true; 806 } else { 807 String name = getContactNameFromPhone(phone); 808 if (name != null && name.length() > 0 && name.matches(orig)) { 809 if (D) Log.d(TAG, "match originator name = " + name); 810 res = true; 811 } else { 812 res = false; 813 } 814 } 815 } else { 816 res = false; 817 } 818 return res; 819 } 820 821 private boolean matchOriginatorSms(Cursor c, FilterInfo fi, String orig) { 822 boolean res; 823 int msgType = c.getInt(c.getColumnIndex(Sms.TYPE)); 824 if (msgType == 1) { 825 String phone = c.getString(c.getColumnIndex(Sms.ADDRESS)); 826 if (phone !=null && phone.length() > 0) { 827 if (phone.matches(orig)) { 828 if (D) Log.d(TAG, "match originator phone = " + phone); 829 res = true; 830 } else { 831 String name = getContactNameFromPhone(phone); 832 if (name != null && name.length() > 0 && name.matches(orig)) { 833 if (D) Log.d(TAG, "match originator name = " + name); 834 res = true; 835 } else { 836 res = false; 837 } 838 } 839 } else { 840 res = false; 841 } 842 } 843 else { 844 String phone = fi.phoneNum; 845 String name = fi.phoneAlphaTag; 846 if (phone != null && phone.length() > 0 && phone.matches(orig)) { 847 if (D) Log.d(TAG, "match originator phone = " + phone); 848 res = true; 849 } else if (name != null && name.length() > 0 && name.matches(orig)) { 850 if (D) Log.d(TAG, "match originator name = " + name); 851 res = true; 852 } else { 853 res = false; 854 } 855 } 856 return res; 857 } 858 859 private boolean matchOriginator(Cursor c, FilterInfo fi, BluetoothMapAppParams ap) { 860 boolean res; 861 String orig = ap.getFilterOriginator(); 862 if (orig != null && orig.length() > 0) { 863 orig = orig.replace("*", ".*"); 864 orig = ".*" + orig + ".*"; 865 if (fi.msgType == FilterInfo.TYPE_SMS) { 866 res = matchOriginatorSms(c, fi, orig); 867 } else if (fi.msgType == FilterInfo.TYPE_MMS) { 868 res = matchOriginatorMms(c, fi, orig); 869 } else { 870 Log.d(TAG, "Unknown msg type: " + fi.msgType); 871 res = false; 872 } 873 } else { 874 res = true; 875 } 876 return res; 877 } 878 879 private boolean matchAddresses(Cursor c, FilterInfo fi, BluetoothMapAppParams ap) { 880 if (matchOriginator(c, fi, ap) && matchRecipient(c, fi, ap)) { 881 return true; 882 } else { 883 return false; 884 } 885 } 886 887 private String setWhereFilterFolderTypeSms(String folder) { 888 String where = ""; 889 if ("inbox".equalsIgnoreCase(folder)) { 890 where = "type = 1 AND thread_id <> -1"; 891 } 892 else if ("outbox".equalsIgnoreCase(folder)) { 893 where = "(type = 4 OR type = 5 OR type = 6) AND thread_id <> -1"; 894 } 895 else if ("sent".equalsIgnoreCase(folder)) { 896 where = "type = 2 AND thread_id <> -1"; 897 } 898 else if ("draft".equalsIgnoreCase(folder)) { 899 where = "type = 3 AND thread_id <> -1"; 900 } 901 else if ("deleted".equalsIgnoreCase(folder)) { 902 where = "thread_id = -1"; 903 } 904 905 return where; 906 } 907 908 private String setWhereFilterFolderTypeMms(String folder) { 909 String where = ""; 910 if ("inbox".equalsIgnoreCase(folder)) { 911 where = "msg_box = 1 AND thread_id <> -1"; 912 } 913 else if ("outbox".equalsIgnoreCase(folder)) { 914 where = "msg_box = 4 AND thread_id <> -1"; 915 } 916 else if ("sent".equalsIgnoreCase(folder)) { 917 where = "msg_box = 2 AND thread_id <> -1"; 918 } 919 else if ("draft".equalsIgnoreCase(folder)) { 920 where = "msg_box = 3 AND thread_id <> -1"; 921 } 922 else if ("deleted".equalsIgnoreCase(folder)) { 923 where = "thread_id = -1"; 924 } 925 926 return where; 927 } 928 929 private String setWhereFilterFolderType(String folder, FilterInfo fi) { 930 String where = ""; 931 if (fi.msgType == FilterInfo.TYPE_SMS) { 932 where = setWhereFilterFolderTypeSms(folder); 933 } else if (fi.msgType == FilterInfo.TYPE_MMS) { 934 where = setWhereFilterFolderTypeMms(folder); 935 } 936 937 return where; 938 } 939 940 private String setWhereFilterReadStatus(BluetoothMapAppParams ap) { 941 String where = ""; 942 if (ap.getFilterReadStatus() != -1) { 943 if ((ap.getFilterReadStatus() & 0x01) != 0) { 944 where = " AND read=0 "; 945 } 946 947 if ((ap.getFilterReadStatus() & 0x02) != 0) { 948 where = " AND read=1 "; 949 } 950 } 951 952 return where; 953 } 954 955 private String setWhereFilterPeriod(BluetoothMapAppParams ap, FilterInfo fi) { 956 String where = ""; 957 if ((ap.getFilterPeriodBegin() != -1)) { 958 if (fi.msgType == FilterInfo.TYPE_SMS) { 959 where = " AND date >= " + ap.getFilterPeriodBegin(); 960 } else if (fi.msgType == FilterInfo.TYPE_MMS) { 961 where = " AND date >= " + (ap.getFilterPeriodBegin() / 1000L); 962 } 963 } 964 965 if ((ap.getFilterPeriodEnd() != -1)) { 966 if (fi.msgType == FilterInfo.TYPE_SMS) { 967 where += " AND date < " + ap.getFilterPeriodEnd(); 968 } else if (fi.msgType == FilterInfo.TYPE_MMS) { 969 where += " AND date < " + (ap.getFilterPeriodEnd() / 1000L); 970 } 971 } 972 973 return where; 974 } 975 976 private String setWhereFilterPhones(String str) { 977 String where = ""; 978 str = str.replace("*", "%"); 979 980 Cursor c = mResolver.query(ContactsContract.Contacts.CONTENT_URI, null, 981 ContactsContract.Contacts.DISPLAY_NAME + " like ?", 982 new String[]{str}, 983 ContactsContract.Contacts.DISPLAY_NAME + " ASC"); 984 985 while (c != null && c.moveToNext()) { 986 String contactId = c.getString(c.getColumnIndex(ContactsContract.Contacts._ID)); 987 988 Cursor p = mResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, 989 ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?", 990 new String[]{contactId}, 991 null); 992 993 while (p != null && p.moveToNext()) { 994 String number = p.getString( 995 p.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)); 996 997 where += " address = " + "'" + number + "'"; 998 if (!p.isLast()) { 999 where += " OR "; 1000 } 1001 } 1002 if (!c.isLast()) { 1003 where += " OR "; 1004 } 1005 p.close(); 1006 } 1007 c.close(); 1008 1009 if (str != null && str.length() > 0) { 1010 if (where.length() > 0) { 1011 where += " OR "; 1012 } 1013 where += " address like " + "'" + str + "'"; 1014 } 1015 1016 return where; 1017 } 1018 1019 private String setWhereFilterOriginator(BluetoothMapAppParams ap, 1020 FilterInfo fi) { 1021 String where = ""; 1022 String orig = ap.getFilterOriginator(); 1023 1024 if (orig != null && orig.length() > 0) { 1025 String phones = setWhereFilterPhones(orig); 1026 1027 if (phones.length() > 0) { 1028 where = " AND ((type <> 1) OR ( " + phones + " ))"; 1029 } else { 1030 where = " AND (type <> 1)"; 1031 } 1032 1033 orig = orig.replace("*", ".*"); 1034 orig = ".*" + orig + ".*"; 1035 1036 boolean localPhoneMatchOrig = false; 1037 if (fi.phoneNum != null && fi.phoneNum.length() > 0 1038 && fi.phoneNum.matches(orig)) { 1039 localPhoneMatchOrig = true; 1040 } 1041 1042 if (fi.phoneAlphaTag != null && fi.phoneAlphaTag.length() > 0 1043 && fi.phoneAlphaTag.matches(orig)) { 1044 localPhoneMatchOrig = true; 1045 } 1046 1047 if (!localPhoneMatchOrig) { 1048 where += " AND (type = 1)"; 1049 } 1050 } 1051 1052 return where; 1053 } 1054 1055 private String setWhereFilterRecipient(BluetoothMapAppParams ap, 1056 FilterInfo fi) { 1057 String where = ""; 1058 String recip = ap.getFilterRecipient(); 1059 1060 if (recip != null && recip.length() > 0) { 1061 String phones = setWhereFilterPhones(recip); 1062 1063 if (phones.length() > 0) { 1064 where = " AND ((type = 1) OR ( " + phones + " ))"; 1065 } else { 1066 where = " AND (type = 1)"; 1067 } 1068 1069 recip = recip.replace("*", ".*"); 1070 recip = ".*" + recip + ".*"; 1071 1072 boolean localPhoneMatchOrig = false; 1073 if (fi.phoneNum != null && fi.phoneNum.length() > 0 1074 && fi.phoneNum.matches(recip)) { 1075 localPhoneMatchOrig = true; 1076 } 1077 1078 if (fi.phoneAlphaTag != null && fi.phoneAlphaTag.length() > 0 1079 && fi.phoneAlphaTag.matches(recip)) { 1080 localPhoneMatchOrig = true; 1081 } 1082 1083 if (!localPhoneMatchOrig) { 1084 where += " AND (type <> 1)"; 1085 } 1086 } 1087 1088 return where; 1089 } 1090 1091 private String setWhereFilter(String folder, FilterInfo fi, BluetoothMapAppParams ap) { 1092 String where = ""; 1093 1094 where += setWhereFilterFolderType(folder, fi); 1095 where += setWhereFilterReadStatus(ap); 1096 where += setWhereFilterPeriod(ap, fi); 1097 /* where += setWhereFilterOriginator(ap, fi); */ 1098 /* where += setWhereFilterRecipient(ap, fi); */ 1099 1100 if (D) Log.d(TAG, "where: " + where); 1101 1102 return where; 1103 } 1104 1105 private boolean smsSelected(FilterInfo fi, BluetoothMapAppParams ap) { 1106 int msgType = ap.getFilterMessageType(); 1107 int phoneType = fi.phoneType; 1108 1109 if (msgType == -1) 1110 return true; 1111 if ((msgType & 0x03) == 0) 1112 return true; 1113 1114 if (((msgType & 0x01) == 0) && (phoneType == TelephonyManager.PHONE_TYPE_GSM)) 1115 return true; 1116 1117 if (((msgType & 0x02) == 0) && (phoneType == TelephonyManager.PHONE_TYPE_CDMA)) 1118 return true; 1119 1120 return false; 1121 } 1122 1123 private boolean mmsSelected(FilterInfo fi, BluetoothMapAppParams ap) { 1124 int msgType = ap.getFilterMessageType(); 1125 1126 if (msgType == -1) 1127 return true; 1128 1129 if ((msgType & 0x08) == 0) 1130 return true; 1131 1132 return false; 1133 } 1134 1135 private void setFilterInfo(FilterInfo fi) { 1136 TelephonyManager tm = (TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE); 1137 if (tm != null) { 1138 fi.phoneType = tm.getPhoneType(); 1139 fi.phoneNum = tm.getLine1Number(); 1140 fi.phoneAlphaTag = tm.getLine1AlphaTag(); 1141 if (D) Log.d(TAG, "phone type = " + fi.phoneType + 1142 " phone num = " + fi.phoneNum + 1143 " phone alpha tag = " + fi.phoneAlphaTag); 1144 } 1145 } 1146 1147 public BluetoothMapMessageListing msgListing(String folder, BluetoothMapAppParams ap) { 1148 Log.d(TAG, "msgListing: folder = " + folder); 1149 BluetoothMapMessageListing bmList = new BluetoothMapMessageListing(); 1150 BluetoothMapMessageListingElement e = null; 1151 1152 /* Cache some info used throughout filtering */ 1153 FilterInfo fi = new FilterInfo(); 1154 setFilterInfo(fi); 1155 1156 if (smsSelected(fi, ap)) { 1157 fi.msgType = FilterInfo.TYPE_SMS; 1158 1159 String where = setWhereFilter(folder, fi, ap); 1160 1161 Cursor c = mResolver.query(Sms.CONTENT_URI, 1162 SMS_PROJECTION, where, null, "date DESC"); 1163 1164 if (c != null) { 1165 while (c.moveToNext()) { 1166 if (matchAddresses(c, fi, ap)) { 1167 printSms(c); 1168 e = element(c, fi, ap); 1169 bmList.add(e); 1170 } 1171 } 1172 c.close(); 1173 } 1174 } 1175 1176 if (mmsSelected(fi, ap)) { 1177 fi.msgType = FilterInfo.TYPE_MMS; 1178 1179 String where = setWhereFilter(folder, fi, ap); 1180 1181 Cursor c = mResolver.query(Mms.CONTENT_URI, 1182 MMS_PROJECTION, where, null, "date DESC"); 1183 1184 if (c != null) { 1185 int cnt = 0; 1186 while (c.moveToNext()) { 1187 if (matchAddresses(c, fi, ap)) { 1188 printMms(c); 1189 e = element(c, fi, ap); 1190 bmList.add(e); 1191 } 1192 } 1193 c.close(); 1194 } 1195 } 1196 1197 /* Enable this if post sorting and segmenting needed */ 1198 bmList.sort(); 1199 bmList.segment(ap.getMaxListCount(), ap.getStartOffset()); 1200 1201 return bmList; 1202 } 1203 1204 public int msgListingSize(String folder, BluetoothMapAppParams ap) { 1205 if (D) Log.d(TAG, "msgListingSize: folder = " + folder); 1206 int cnt = 0; 1207 1208 /* Cache some info used throughout filtering */ 1209 FilterInfo fi = new FilterInfo(); 1210 setFilterInfo(fi); 1211 1212 if (smsSelected(fi, ap)) { 1213 fi.msgType = FilterInfo.TYPE_SMS; 1214 String where = setWhereFilter(folder, fi, ap); 1215 Cursor c = mResolver.query(Sms.CONTENT_URI, 1216 SMS_PROJECTION, where, null, "date DESC"); 1217 1218 if (c != null) { 1219 cnt = c.getCount(); 1220 c.close(); 1221 } 1222 } 1223 1224 if (mmsSelected(fi, ap)) { 1225 fi.msgType = FilterInfo.TYPE_MMS; 1226 String where = setWhereFilter(folder, fi, ap); 1227 Cursor c = mResolver.query(Mms.CONTENT_URI, 1228 MMS_PROJECTION, where, null, "date DESC"); 1229 1230 if (c != null) { 1231 cnt += c.getCount(); 1232 c.close(); 1233 } 1234 } 1235 1236 if (D) Log.d(TAG, "msgListingSize: size = " + cnt); 1237 return cnt; 1238 } 1239 /** 1240 * Return true if there are unread messages in the requested list of messages 1241 * @param folder folder where the message listing should come from 1242 * @param ap application parameter object 1243 * @return true if unread messages are in the list, else false 1244 */ 1245 public boolean msgListingHasUnread(String folder, BluetoothMapAppParams ap) { 1246 if (D) Log.d(TAG, "msgListingHasUnread: folder = " + folder); 1247 int cnt = 0; 1248 1249 /* Cache some info used throughout filtering */ 1250 FilterInfo fi = new FilterInfo(); 1251 setFilterInfo(fi); 1252 1253 if (smsSelected(fi, ap)) { 1254 fi.msgType = FilterInfo.TYPE_SMS; 1255 String where = setWhereFilterFolderType(folder, fi); 1256 where += " AND read=0 "; 1257 where += setWhereFilterPeriod(ap, fi); 1258 Cursor c = mResolver.query(Sms.CONTENT_URI, 1259 SMS_PROJECTION, where, null, "date DESC"); 1260 1261 if (c != null) { 1262 cnt = c.getCount(); 1263 c.close(); 1264 } 1265 } 1266 1267 if (mmsSelected(fi, ap)) { 1268 fi.msgType = FilterInfo.TYPE_MMS; 1269 String where = setWhereFilterFolderType(folder, fi); 1270 where += " AND read=0 "; 1271 where += setWhereFilterPeriod(ap, fi); 1272 Cursor c = mResolver.query(Mms.CONTENT_URI, 1273 MMS_PROJECTION, where, null, "date DESC"); 1274 1275 if (c != null) { 1276 cnt += c.getCount(); 1277 c.close(); 1278 } 1279 } 1280 1281 if (D) Log.d(TAG, "msgListingHasUnread: numUnread = " + cnt); 1282 return (cnt>0)?true:false; 1283 } 1284 1285 /** 1286 * Get the folder name of an SMS message or MMS message. 1287 * @param c the cursor pointing at the message 1288 * @return the folder name. 1289 */ 1290 private String getFolderName(int type, int threadId) { 1291 1292 if(threadId == -1) 1293 return "deleted"; 1294 1295 switch(type) { 1296 case 1: 1297 return "inbox"; 1298 case 2: 1299 return "sent"; 1300 case 3: 1301 return "draft"; 1302 case 4: // Just name outbox, failed and queued "outbox" 1303 case 5: 1304 case 6: 1305 return "outbox"; 1306 } 1307 return ""; 1308 } 1309 1310 public byte[] getMessage(String handle, BluetoothMapAppParams appParams) throws UnsupportedEncodingException{ 1311 TYPE type = BluetoothMapUtils.getMsgTypeFromHandle(handle); 1312 long id = BluetoothMapUtils.getCpHandle(handle); 1313 switch(type) { 1314 case SMS_GSM: 1315 case SMS_CDMA: 1316 return getSmsMessage(id, appParams.getCharset()); 1317 case MMS: 1318 return getMmsMessage(id, appParams); 1319 case EMAIL: 1320 throw new IllegalArgumentException("Email not implemented - invalid message handle."); 1321 } 1322 throw new IllegalArgumentException("Invalid message handle."); 1323 } 1324 1325 private void setVCardFromPhoneNumber(BluetoothMapbMessage message, String phone, boolean incoming) { 1326 String contactId = null, contactName = null; 1327 String[] phoneNumbers = null; 1328 String[] emailAddresses = null; 1329 Cursor p; 1330 1331 Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, 1332 Uri.encode(phone)); 1333 1334 String[] projection = {Contacts._ID, Contacts.DISPLAY_NAME}; 1335 String selection = Contacts.IN_VISIBLE_GROUP + "=1"; 1336 String orderBy = Contacts._ID + " ASC"; 1337 1338 // Get the contact _ID and name 1339 p = mResolver.query(uri, projection, selection, null, orderBy); 1340 if (p != null && p.getCount() >= 1) { 1341 p.moveToFirst(); 1342 contactId = p.getString(p.getColumnIndex(Contacts._ID)); 1343 contactName = p.getString(p.getColumnIndex(Contacts.DISPLAY_NAME)); 1344 } 1345 p.close(); 1346 1347 // Bail out if we are unable to find a contact, based on the phone number 1348 if(contactId == null) { 1349 phoneNumbers = new String[1]; 1350 phoneNumbers[0] = phone; 1351 } 1352 else { 1353 // Fetch all contact phone numbers 1354 p = mResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, 1355 ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?", 1356 new String[]{contactId}, 1357 null); 1358 if(p != null) { 1359 int i = 0; 1360 phoneNumbers = new String[p.getCount()]; 1361 while (p != null && p.moveToNext()) { 1362 String number = p.getString( 1363 p.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)); 1364 phoneNumbers[i++] = number; 1365 } 1366 p.close(); 1367 } 1368 1369 // Fetch contact e-mail addresses 1370 p = mResolver.query(ContactsContract.CommonDataKinds.Email.CONTENT_URI, null, 1371 ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?", 1372 new String[]{contactId}, 1373 null); 1374 if(p != null) { 1375 int i = 0; 1376 emailAddresses = new String[p.getCount()]; 1377 while (p != null && p.moveToNext()) { 1378 String emailAddress = p.getString( 1379 p.getColumnIndex(ContactsContract.CommonDataKinds.Email.ADDRESS)); 1380 emailAddresses[i++] = emailAddress; 1381 } 1382 p.close(); 1383 } 1384 } 1385 if(incoming == true) 1386 message.addOriginator(contactName, contactName, phoneNumbers, emailAddresses); // Use version 3.0 as we only have a formatted name 1387 else 1388 message.addRecipient(contactName, contactName, phoneNumbers, emailAddresses); // Use version 3.0 as we only have a formatted name 1389 } 1390 1391 public static final int MAP_MESSAGE_CHARSET_NATIVE = 0; 1392 public static final int MAP_MESSAGE_CHARSET_UTF8 = 1; 1393 1394 public byte[] getSmsMessage(long id, int charset) throws UnsupportedEncodingException{ 1395 int type, threadId; 1396 long time = -1; 1397 String msgBody; 1398 BluetoothMapbMessageSms message = new BluetoothMapbMessageSms(); 1399 TelephonyManager tm = (TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE); 1400 Cursor c = mResolver.query(Sms.CONTENT_URI, SMS_PROJECTION, "_ID = " + id, null, null); 1401 1402 if(c != null && c.moveToFirst()) 1403 { 1404 1405 if(V) Log.d(TAG,"c.count: " + c.getCount()); 1406 1407 if (tm.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM) { 1408 message.setType(TYPE.SMS_GSM); 1409 } else if (tm.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) { 1410 message.setType(TYPE.SMS_CDMA); 1411 } 1412 1413 String read = c.getString(c.getColumnIndex(Sms.READ)); 1414 if (read.equalsIgnoreCase("1")) 1415 message.setStatus(true); 1416 else 1417 message.setStatus(false); 1418 1419 type = c.getInt(c.getColumnIndex(Sms.TYPE)); 1420 threadId = c.getInt(c.getColumnIndex(Sms.THREAD_ID)); 1421 message.setFolder(getFolderName(type, threadId)); 1422 1423 msgBody = c.getString(c.getColumnIndex(Sms.BODY)); 1424 1425 String phone = c.getString(c.getColumnIndex(Sms.ADDRESS)); 1426 1427 time = c.getLong(c.getColumnIndex(Sms.DATE)); 1428 if(type == 1) // Inbox message needs to set the vCard as originator 1429 setVCardFromPhoneNumber(message, phone, true); 1430 else // Other messages sets the vCard as the recipient 1431 setVCardFromPhoneNumber(message, phone, false); 1432 1433 if(charset == MAP_MESSAGE_CHARSET_NATIVE) { 1434 if(type == 1) //Inbox 1435 message.setSmsBodyPdus(BluetoothMapSmsPdu.getDeliverPdus(msgBody, phone, time)); 1436 else 1437 message.setSmsBodyPdus(BluetoothMapSmsPdu.getSubmitPdus(msgBody, phone)); 1438 } else /*if (charset == MAP_MESSAGE_CHARSET_UTF8)*/ { 1439 message.setSmsBody(msgBody); 1440 } 1441 1442 c.close(); 1443 1444 return message.encode(); 1445 } 1446 throw new IllegalArgumentException("SMS handle not found"); 1447 } 1448 1449 private void extractMmsAddresses(long id, BluetoothMapbMessageMmsEmail message) { 1450 final String[] projection = null; 1451 String selection = new String("msg_id=" + id); 1452 String uriStr = String.format("content://mms/%d/addr", id); 1453 Uri uriAddress = Uri.parse(uriStr); 1454 Cursor c = mResolver.query( 1455 uriAddress, 1456 projection, 1457 selection, 1458 null, null); 1459 /* TODO: Change the setVCard...() to return the vCard, and use the name in message.addXxx() */ 1460 if (c.moveToFirst()) { 1461 do { 1462 String address = c.getString(c.getColumnIndex("address")); 1463 Integer type = c.getInt(c.getColumnIndex("type")); 1464 switch(type) { 1465 case MMS_FROM: 1466 setVCardFromPhoneNumber(message, address, true); 1467 message.addFrom(null, address); 1468 break; 1469 case MMS_TO: 1470 setVCardFromPhoneNumber(message, address, false); 1471 message.addTo(null, address); 1472 break; 1473 case MMS_CC: 1474 setVCardFromPhoneNumber(message, address, false); 1475 message.addCc(null, address); 1476 break; 1477 case MMS_BCC: 1478 setVCardFromPhoneNumber(message, address, false); 1479 message.addBcc(null, address); 1480 default: 1481 break; 1482 } 1483 } while(c.moveToNext()); 1484 } 1485 } 1486 1487 /** 1488 * Read out a mms data part and return the data in a byte array. 1489 * @param partid the content provider id of the mms. 1490 * @return 1491 */ 1492 private byte[] readMmsDataPart(long partid) { 1493 String uriStr = String.format("content://mms/part/%d", partid); 1494 Uri uriAddress = Uri.parse(uriStr); 1495 InputStream is = null; 1496 ByteArrayOutputStream os = new ByteArrayOutputStream(); 1497 int bufferSize = 8192; 1498 byte[] buffer = new byte[bufferSize]; 1499 byte[] retVal = null; 1500 1501 try { 1502 is = mResolver.openInputStream(uriAddress); 1503 int len = 0; 1504 while ((len = is.read(buffer)) != -1) { 1505 os.write(buffer, 0, len); // We need to specify the len, as it can be != bufferSize 1506 } 1507 retVal = os.toByteArray(); 1508 } catch (IOException e) { 1509 // do nothing for now 1510 Log.w(TAG,"Error reading part data",e); 1511 } finally { 1512 try { 1513 os.close(); 1514 is.close(); 1515 } catch (IOException e) { 1516 } 1517 } 1518 return retVal; 1519 } 1520 1521 /** 1522 * Read out the mms parts and update the bMessage object provided i {@linkplain message} 1523 * @param id the content provider ID of the message 1524 * @param message the bMessage object to add the information to 1525 */ 1526 private void extractMmsParts(long id, BluetoothMapbMessageMmsEmail message) 1527 { 1528 /* TODO: If the attachment appParam is set to "no", only add the text parts. 1529 * (content type contains "text" - case insensitive) */ 1530 final String[] projection = null; 1531 String selection = new String("mid=" + id); 1532 String uriStr = String.format("content://mms/%d/part", id); 1533 Uri uriAddress = Uri.parse(uriStr); 1534 BluetoothMapbMessageMmsEmail.MimePart part; 1535 Cursor c = mResolver.query( 1536 uriAddress, 1537 projection, 1538 selection, 1539 null, null); 1540 1541 if (c.moveToFirst()) { 1542 do { 1543 Long partId = c.getLong(c.getColumnIndex(BaseColumns._ID)); 1544 String contentType = c.getString(c.getColumnIndex("ct")); 1545 String name = c.getString(c.getColumnIndex("name")); 1546 String charset = c.getString(c.getColumnIndex("chset")); 1547 String filename = c.getString(c.getColumnIndex("fn")); 1548 String text = c.getString(c.getColumnIndex("text")); 1549 Integer fd = c.getInt(c.getColumnIndex("_data")); 1550 String cid = c.getString(c.getColumnIndex("cid")); 1551 String cl = c.getString(c.getColumnIndex("cl")); 1552 String cdisp = c.getString(c.getColumnIndex("cd")); 1553 1554 if(D) Log.d(TAG, " _id : " + partId + 1555 "\n ct : " + contentType + 1556 "\n partname : " + name + 1557 "\n charset : " + charset + 1558 "\n filename : " + filename + 1559 "\n text : " + text + 1560 "\n fd : " + fd + 1561 "\n cid : " + cid + 1562 "\n cl : " + cl + 1563 "\n cdisp : " + cdisp); 1564 1565 part = message.addMimePart(); 1566 part.contentType = contentType; 1567 part.partName = name; 1568 part.contentId = cid; 1569 part.contentLocation = cl; 1570 part.contentDisposition = cdisp; 1571 1572 try { 1573 if(text != null) { 1574 part.data = text.getBytes("UTF-8"); 1575 part.charsetName = "utf-8"; 1576 } 1577 else { 1578 part.data = readMmsDataPart(partId); 1579 if(charset != null) 1580 part.charsetName = CharacterSets.getMimeName(Integer.parseInt(charset)); 1581 } 1582 } catch (NumberFormatException e) { 1583 Log.d(TAG,"extractMmsParts",e); 1584 part.data = null; 1585 part.charsetName = null; 1586 } catch (UnsupportedEncodingException e) { 1587 Log.d(TAG,"extractMmsParts",e); 1588 part.data = null; 1589 part.charsetName = null; 1590 } finally { 1591 } 1592 part.fileName = filename; 1593 } while(c.moveToNext()); 1594 } 1595 message.updateCharset(); 1596 } 1597 1598 /** 1599 * 1600 * @param id the content provider id for the message to fetch. 1601 * @param appParams The application parameter object received from the client. 1602 * @return a byte[] containing the utf-8 encoded bMessage to send to the client. 1603 * @throws UnsupportedEncodingException if UTF-8 is not supported, 1604 * which is guaranteed to be supported on an android device 1605 */ 1606 public byte[] getMmsMessage(long id, BluetoothMapAppParams appParams) throws UnsupportedEncodingException { 1607 int msgBox, threadId; 1608 BluetoothMapbMessageMmsEmail message = new BluetoothMapbMessageMmsEmail(); 1609 Cursor c = mResolver.query(Mms.CONTENT_URI, MMS_PROJECTION, "_ID = " + id, null, null); 1610 if(c != null && c.moveToFirst()) 1611 { 1612 message.setType(TYPE.MMS); 1613 1614 // The MMS info: 1615 String read = c.getString(c.getColumnIndex(Mms.READ)); 1616 if (read.equalsIgnoreCase("1")) 1617 message.setStatus(true); 1618 else 1619 message.setStatus(false); 1620 1621 msgBox = c.getInt(c.getColumnIndex(Mms.MESSAGE_BOX)); 1622 threadId = c.getInt(c.getColumnIndex(Mms.THREAD_ID)); 1623 message.setFolder(getFolderName(msgBox, threadId)); 1624 1625 message.setSubject(c.getString(c.getColumnIndex(Mms.SUBJECT))); 1626 message.setMessageId(c.getString(c.getColumnIndex(Mms.MESSAGE_ID))); 1627 message.setContentType(c.getString(c.getColumnIndex(Mms.CONTENT_TYPE))); 1628 message.setDate(c.getLong(c.getColumnIndex(Mms.DATE)) * 1000L); 1629 message.setTextOnly(c.getInt(c.getColumnIndex(Mms.TEXT_ONLY)) == 0 ? false : true); // - TODO: Do we need this - yes, if we have only text, we should not make this a multipart message 1630 message.setIncludeAttachments(appParams.getAttachment() == 0 ? false : true); 1631 // c.getLong(c.getColumnIndex(Mms.DATE_SENT)); - this is never used 1632 // c.getInt(c.getColumnIndex(Mms.STATUS)); - don't know what this is 1633 1634 // The parts 1635 extractMmsParts(id, message); 1636 1637 // The addresses 1638 extractMmsAddresses(id, message); 1639 1640 c.close(); 1641 1642 return message.encode(); 1643 } 1644 else if(c != null) { 1645 c.close(); 1646 } 1647 1648 throw new IllegalArgumentException("MMS handle not found"); 1649 } 1650 1651} 1652