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