| /* |
| * Copyright (C) 2014 Samsung System LSI |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.android.bluetooth.map; |
| |
| import android.annotation.TargetApi; |
| import android.content.ContentResolver; |
| import android.content.Context; |
| import android.database.Cursor; |
| import android.net.Uri; |
| import android.net.Uri.Builder; |
| import android.os.ParcelFileDescriptor; |
| import android.provider.BaseColumns; |
| import android.provider.ContactsContract; |
| import android.provider.ContactsContract.Contacts; |
| import android.provider.ContactsContract.PhoneLookup; |
| import android.provider.Telephony.CanonicalAddressesColumns; |
| import android.provider.Telephony.Mms; |
| import android.provider.Telephony.MmsSms; |
| import android.provider.Telephony.Sms; |
| import android.provider.Telephony.Threads; |
| import android.telephony.PhoneNumberUtils; |
| import android.telephony.TelephonyManager; |
| import android.text.TextUtils; |
| import android.text.util.Rfc822Token; |
| import android.text.util.Rfc822Tokenizer; |
| import android.util.Log; |
| |
| import com.android.bluetooth.DeviceWorkArounds; |
| import com.android.bluetooth.SignedLongLong; |
| import com.android.bluetooth.map.BluetoothMapUtils.TYPE; |
| import com.android.bluetooth.map.BluetoothMapbMessageMime.MimePart; |
| import com.android.bluetooth.mapapi.BluetoothMapContract; |
| import com.android.bluetooth.mapapi.BluetoothMapContract.ConversationColumns; |
| |
| import com.google.android.mms.pdu.CharacterSets; |
| import com.google.android.mms.pdu.PduHeaders; |
| |
| import java.io.ByteArrayOutputStream; |
| import java.io.Closeable; |
| import java.io.FileInputStream; |
| import java.io.FileNotFoundException; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.UnsupportedEncodingException; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.List; |
| |
| @TargetApi(19) |
| public class BluetoothMapContent { |
| |
| private static final String TAG = "BluetoothMapContent"; |
| |
| private static final boolean D = BluetoothMapService.DEBUG; |
| private static final boolean V = BluetoothMapService.VERBOSE; |
| |
| // Parameter Mask for selection of parameters to return in listings |
| private static final int MASK_SUBJECT = 0x00000001; |
| private static final int MASK_DATETIME = 0x00000002; |
| private static final int MASK_SENDER_NAME = 0x00000004; |
| private static final int MASK_SENDER_ADDRESSING = 0x00000008; |
| private static final int MASK_RECIPIENT_NAME = 0x00000010; |
| private static final int MASK_RECIPIENT_ADDRESSING = 0x00000020; |
| private static final int MASK_TYPE = 0x00000040; |
| private static final int MASK_SIZE = 0x00000080; |
| private static final int MASK_RECEPTION_STATUS = 0x00000100; |
| private static final int MASK_TEXT = 0x00000200; |
| private static final int MASK_ATTACHMENT_SIZE = 0x00000400; |
| private static final int MASK_PRIORITY = 0x00000800; |
| private static final int MASK_READ = 0x00001000; |
| private static final int MASK_SENT = 0x00002000; |
| private static final int MASK_PROTECTED = 0x00004000; |
| private static final int MASK_REPLYTO_ADDRESSING = 0x00008000; |
| // TODO: Duplicate in proposed spec |
| // private static final int MASK_RECEPTION_STATE = 0x00010000; |
| private static final int MASK_DELIVERY_STATUS = 0x00020000; |
| private static final int MASK_CONVERSATION_ID = 0x00040000; |
| private static final int MASK_CONVERSATION_NAME = 0x00080000; |
| private static final int MASK_FOLDER_TYPE = 0x00100000; |
| // TODO: about to be removed from proposed spec |
| // private static final int MASK_SEQUENCE_NUMBER = 0x00200000; |
| private static final int MASK_ATTACHMENT_MIME = 0x00400000; |
| |
| private static final int CONVO_PARAM_MASK_CONVO_NAME = 0x00000001; |
| private static final int CONVO_PARAM_MASK_CONVO_LAST_ACTIVITY = 0x00000002; |
| private static final int CONVO_PARAM_MASK_CONVO_READ_STATUS = 0x00000004; |
| private static final int CONVO_PARAM_MASK_CONVO_VERSION_COUNTER = 0x00000008; |
| private static final int CONVO_PARAM_MASK_CONVO_SUMMARY = 0x00000010; |
| private static final int CONVO_PARAM_MASK_PARTTICIPANTS = 0x00000020; |
| private static final int CONVO_PARAM_MASK_PART_UCI = 0x00000040; |
| private static final int CONVO_PARAM_MASK_PART_DISP_NAME = 0x00000080; |
| private static final int CONVO_PARAM_MASK_PART_CHAT_STATE = 0x00000100; |
| private static final int CONVO_PARAM_MASK_PART_LAST_ACTIVITY = 0x00000200; |
| private static final int CONVO_PARAM_MASK_PART_X_BT_UID = 0x00000400; |
| private static final int CONVO_PARAM_MASK_PART_NAME = 0x00000800; |
| private static final int CONVO_PARAM_MASK_PART_PRESENCE = 0x00001000; |
| private static final int CONVO_PARAM_MASK_PART_PRESENCE_TEXT = 0x00002000; |
| private static final int CONVO_PARAM_MASK_PART_PRIORITY = 0x00004000; |
| |
| /* Default values for omitted or 0 parameterMask application parameters */ |
| // MAP specification states that the default value for parameter mask are |
| // the #REQUIRED attributes in the DTD, and not all enabled |
| public static final long PARAMETER_MASK_ALL_ENABLED = 0xFFFFFFFFL; |
| public static final long CONVO_PARAMETER_MASK_ALL_ENABLED = 0xFFFFFFFFL; |
| public static final long CONVO_PARAMETER_MASK_DEFAULT = |
| CONVO_PARAM_MASK_CONVO_NAME | CONVO_PARAM_MASK_PARTTICIPANTS | CONVO_PARAM_MASK_PART_UCI |
| | CONVO_PARAM_MASK_PART_DISP_NAME; |
| |
| |
| private static final int FILTER_READ_STATUS_UNREAD_ONLY = 0x01; |
| private static final int FILTER_READ_STATUS_READ_ONLY = 0x02; |
| private static final int FILTER_READ_STATUS_ALL = 0x00; |
| |
| /* Type of MMS address. From Telephony.java it must be one of PduHeaders.BCC, */ |
| /* PduHeaders.CC, PduHeaders.FROM, PduHeaders.TO. These are from PduHeaders.java */ |
| public static final int MMS_FROM = 0x89; |
| public static final int MMS_TO = 0x97; |
| public static final int MMS_BCC = 0x81; |
| public static final int MMS_CC = 0x82; |
| |
| /* OMA-TS-MMS-ENC defined many types in X-Mms-Message-Type. |
| Only m-send-req (128) m-retrieve-conf (132), m-notification-ind (130) |
| are interested by user */ |
| private static final String INTERESTED_MESSAGE_TYPE_CLAUSE = |
| String.format("( %s = %d OR %s = %d OR %s = %d )", Mms.MESSAGE_TYPE, |
| PduHeaders.MESSAGE_TYPE_SEND_REQ, Mms.MESSAGE_TYPE, |
| PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF, Mms.MESSAGE_TYPE, |
| PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND); |
| |
| public static final String INSERT_ADDRES_TOKEN = "insert-address-token"; |
| |
| private final Context mContext; |
| private final ContentResolver mResolver; |
| private final String mBaseUri; |
| private final BluetoothMapAccountItem mAccount; |
| /* The MasInstance reference is used to update persistent (over a connection) version counters*/ |
| private final BluetoothMapMasInstance mMasInstance; |
| private String mMessageVersion = BluetoothMapUtils.MAP_V10_STR; |
| |
| private int mRemoteFeatureMask = BluetoothMapUtils.MAP_FEATURE_DEFAULT_BITMASK; |
| private int mMsgListingVersion = BluetoothMapUtils.MAP_MESSAGE_LISTING_FORMAT_V10; |
| |
| static final String[] SMS_PROJECTION = new String[]{ |
| BaseColumns._ID, |
| Sms.THREAD_ID, |
| Sms.ADDRESS, |
| Sms.BODY, |
| Sms.DATE, |
| Sms.READ, |
| Sms.TYPE, |
| Sms.STATUS, |
| Sms.LOCKED, |
| Sms.ERROR_CODE |
| }; |
| |
| static final String[] MMS_PROJECTION = new String[]{ |
| BaseColumns._ID, |
| Mms.THREAD_ID, |
| Mms.MESSAGE_ID, |
| Mms.MESSAGE_SIZE, |
| Mms.SUBJECT, |
| Mms.CONTENT_TYPE, |
| Mms.TEXT_ONLY, |
| Mms.DATE, |
| Mms.DATE_SENT, |
| Mms.READ, |
| Mms.MESSAGE_BOX, |
| Mms.STATUS, |
| Mms.PRIORITY, |
| }; |
| |
| static final String[] SMS_CONVO_PROJECTION = new String[]{ |
| BaseColumns._ID, |
| Sms.THREAD_ID, |
| Sms.ADDRESS, |
| Sms.DATE, |
| Sms.READ, |
| Sms.TYPE, |
| Sms.STATUS, |
| Sms.LOCKED, |
| Sms.ERROR_CODE |
| }; |
| |
| static final String[] MMS_CONVO_PROJECTION = new String[]{ |
| BaseColumns._ID, |
| Mms.THREAD_ID, |
| Mms.MESSAGE_ID, |
| Mms.MESSAGE_SIZE, |
| Mms.SUBJECT, |
| Mms.CONTENT_TYPE, |
| Mms.TEXT_ONLY, |
| Mms.DATE, |
| Mms.DATE_SENT, |
| Mms.READ, |
| Mms.MESSAGE_BOX, |
| Mms.STATUS, |
| Mms.PRIORITY, |
| Mms.Addr.ADDRESS |
| }; |
| |
| /* CONVO LISTING projections and column indexes */ |
| private static final String[] MMS_SMS_THREAD_PROJECTION = { |
| Threads._ID, |
| Threads.DATE, |
| Threads.SNIPPET, |
| Threads.SNIPPET_CHARSET, |
| Threads.READ, |
| Threads.RECIPIENT_IDS |
| }; |
| |
| private static final String[] CONVO_VERSION_PROJECTION = new String[]{ |
| /* Thread information */ |
| ConversationColumns.THREAD_ID, |
| ConversationColumns.THREAD_NAME, |
| ConversationColumns.READ_STATUS, |
| ConversationColumns.LAST_THREAD_ACTIVITY, |
| ConversationColumns.SUMMARY, |
| }; |
| |
| /* Optimize the Cursor access to avoid the need to do a getColumnIndex() */ |
| private static final int MMS_SMS_THREAD_COL_ID; |
| private static final int MMS_SMS_THREAD_COL_DATE; |
| private static final int MMS_SMS_THREAD_COL_SNIPPET; |
| private static final int MMS_SMS_THREAD_COL_SNIPPET_CS; |
| private static final int MMS_SMS_THREAD_COL_READ; |
| private static final int MMS_SMS_THREAD_COL_RECIPIENT_IDS; |
| |
| static { |
| // TODO: This might not work, if the projection is mapped in the content provider... |
| // Change to init at first query? (Current use in the AOSP code is hard coded values |
| // unrelated to the projection used) |
| List<String> projection = Arrays.asList(MMS_SMS_THREAD_PROJECTION); |
| MMS_SMS_THREAD_COL_ID = projection.indexOf(Threads._ID); |
| MMS_SMS_THREAD_COL_DATE = projection.indexOf(Threads.DATE); |
| MMS_SMS_THREAD_COL_SNIPPET = projection.indexOf(Threads.SNIPPET); |
| MMS_SMS_THREAD_COL_SNIPPET_CS = projection.indexOf(Threads.SNIPPET_CHARSET); |
| MMS_SMS_THREAD_COL_READ = projection.indexOf(Threads.READ); |
| MMS_SMS_THREAD_COL_RECIPIENT_IDS = projection.indexOf(Threads.RECIPIENT_IDS); |
| } |
| |
| private class FilterInfo { |
| public static final int TYPE_SMS = 0; |
| public static final int TYPE_MMS = 1; |
| public static final int TYPE_EMAIL = 2; |
| public static final int TYPE_IM = 3; |
| |
| // TODO: Change to ENUM, to ensure correct usage |
| int mMsgType = TYPE_SMS; |
| int mPhoneType = 0; |
| String mPhoneNum = null; |
| String mPhoneAlphaTag = null; |
| /*column indices used to optimize queries */ |
| public int mMessageColId = -1; |
| public int mMessageColDate = -1; |
| public int mMessageColBody = -1; |
| public int mMessageColSubject = -1; |
| public int mMessageColFolder = -1; |
| public int mMessageColRead = -1; |
| public int mMessageColSize = -1; |
| public int mMessageColFromAddress = -1; |
| public int mMessageColToAddress = -1; |
| public int mMessageColCcAddress = -1; |
| public int mMessageColBccAddress = -1; |
| public int mMessageColReplyTo = -1; |
| public int mMessageColAccountId = -1; |
| public int mMessageColAttachment = -1; |
| public int mMessageColAttachmentSize = -1; |
| public int mMessageColAttachmentMime = -1; |
| public int mMessageColPriority = -1; |
| public int mMessageColProtected = -1; |
| public int mMessageColReception = -1; |
| public int mMessageColDelivery = -1; |
| public int mMessageColThreadId = -1; |
| public int mMessageColThreadName = -1; |
| |
| public int mSmsColFolder = -1; |
| public int mSmsColRead = -1; |
| public int mSmsColId = -1; |
| public int mSmsColSubject = -1; |
| public int mSmsColAddress = -1; |
| public int mSmsColDate = -1; |
| public int mSmsColType = -1; |
| public int mSmsColThreadId = -1; |
| |
| public int mMmsColRead = -1; |
| public int mMmsColFolder = -1; |
| public int mMmsColAttachmentSize = -1; |
| public int mMmsColTextOnly = -1; |
| public int mMmsColId = -1; |
| public int mMmsColSize = -1; |
| public int mMmsColDate = -1; |
| public int mMmsColSubject = -1; |
| public int mMmsColThreadId = -1; |
| |
| public int mConvoColConvoId = -1; |
| public int mConvoColLastActivity = -1; |
| public int mConvoColName = -1; |
| public int mConvoColRead = -1; |
| public int mConvoColVersionCounter = -1; |
| public int mConvoColSummary = -1; |
| public int mContactColBtUid = -1; |
| public int mContactColChatState = -1; |
| public int mContactColContactUci = -1; |
| public int mContactColNickname = -1; |
| public int mContactColLastActive = -1; |
| public int mContactColName = -1; |
| public int mContactColPresenceState = -1; |
| public int mContactColPresenceText = -1; |
| public int mContactColPriority = -1; |
| |
| |
| public void setMessageColumns(Cursor c) { |
| mMessageColId = c.getColumnIndex(BluetoothMapContract.MessageColumns._ID); |
| mMessageColDate = c.getColumnIndex(BluetoothMapContract.MessageColumns.DATE); |
| mMessageColSubject = c.getColumnIndex(BluetoothMapContract.MessageColumns.SUBJECT); |
| mMessageColFolder = c.getColumnIndex(BluetoothMapContract.MessageColumns.FOLDER_ID); |
| mMessageColRead = c.getColumnIndex(BluetoothMapContract.MessageColumns.FLAG_READ); |
| mMessageColSize = c.getColumnIndex(BluetoothMapContract.MessageColumns.MESSAGE_SIZE); |
| mMessageColFromAddress = |
| c.getColumnIndex(BluetoothMapContract.MessageColumns.FROM_LIST); |
| mMessageColToAddress = c.getColumnIndex(BluetoothMapContract.MessageColumns.TO_LIST); |
| mMessageColAttachment = |
| c.getColumnIndex(BluetoothMapContract.MessageColumns.FLAG_ATTACHMENT); |
| mMessageColAttachmentSize = |
| c.getColumnIndex(BluetoothMapContract.MessageColumns.ATTACHMENT_SIZE); |
| mMessageColPriority = |
| c.getColumnIndex(BluetoothMapContract.MessageColumns.FLAG_HIGH_PRIORITY); |
| mMessageColProtected = |
| c.getColumnIndex(BluetoothMapContract.MessageColumns.FLAG_PROTECTED); |
| mMessageColReception = |
| c.getColumnIndex(BluetoothMapContract.MessageColumns.RECEPTION_STATE); |
| mMessageColDelivery = |
| c.getColumnIndex(BluetoothMapContract.MessageColumns.DEVILERY_STATE); |
| mMessageColThreadId = c.getColumnIndex(BluetoothMapContract.MessageColumns.THREAD_ID); |
| } |
| |
| public void setEmailMessageColumns(Cursor c) { |
| setMessageColumns(c); |
| mMessageColCcAddress = c.getColumnIndex(BluetoothMapContract.MessageColumns.CC_LIST); |
| mMessageColBccAddress = c.getColumnIndex(BluetoothMapContract.MessageColumns.BCC_LIST); |
| mMessageColReplyTo = |
| c.getColumnIndex(BluetoothMapContract.MessageColumns.REPLY_TO_LIST); |
| } |
| |
| public void setImMessageColumns(Cursor c) { |
| setMessageColumns(c); |
| mMessageColThreadName = |
| c.getColumnIndex(BluetoothMapContract.MessageColumns.THREAD_NAME); |
| mMessageColAttachmentMime = |
| c.getColumnIndex(BluetoothMapContract.MessageColumns.ATTACHMENT_MINE_TYPES); |
| //TODO this is temporary as text should come from parts table instead |
| mMessageColBody = c.getColumnIndex(BluetoothMapContract.MessageColumns.BODY); |
| |
| } |
| |
| public void setEmailImConvoColumns(Cursor c) { |
| mConvoColConvoId = c.getColumnIndex(BluetoothMapContract.ConversationColumns.THREAD_ID); |
| mConvoColLastActivity = |
| c.getColumnIndex(BluetoothMapContract.ConversationColumns.LAST_THREAD_ACTIVITY); |
| mConvoColName = c.getColumnIndex(BluetoothMapContract.ConversationColumns.THREAD_NAME); |
| mConvoColRead = c.getColumnIndex(BluetoothMapContract.ConversationColumns.READ_STATUS); |
| mConvoColVersionCounter = |
| c.getColumnIndex(BluetoothMapContract.ConversationColumns.VERSION_COUNTER); |
| mConvoColSummary = c.getColumnIndex(BluetoothMapContract.ConversationColumns.SUMMARY); |
| setEmailImConvoContactColumns(c); |
| } |
| |
| public void setEmailImConvoContactColumns(Cursor c) { |
| mContactColBtUid = c.getColumnIndex(BluetoothMapContract.ConvoContactColumns.X_BT_UID); |
| mContactColChatState = |
| c.getColumnIndex(BluetoothMapContract.ConvoContactColumns.CHAT_STATE); |
| mContactColContactUci = c.getColumnIndex(BluetoothMapContract.ConvoContactColumns.UCI); |
| mContactColNickname = |
| c.getColumnIndex(BluetoothMapContract.ConvoContactColumns.NICKNAME); |
| mContactColLastActive = |
| c.getColumnIndex(BluetoothMapContract.ConvoContactColumns.LAST_ACTIVE); |
| mContactColName = c.getColumnIndex(BluetoothMapContract.ConvoContactColumns.NAME); |
| mContactColPresenceState = |
| c.getColumnIndex(BluetoothMapContract.ConvoContactColumns.PRESENCE_STATE); |
| mContactColPresenceText = |
| c.getColumnIndex(BluetoothMapContract.ConvoContactColumns.STATUS_TEXT); |
| mContactColPriority = |
| c.getColumnIndex(BluetoothMapContract.ConvoContactColumns.PRIORITY); |
| } |
| |
| public void setSmsColumns(Cursor c) { |
| mSmsColId = c.getColumnIndex(BaseColumns._ID); |
| mSmsColFolder = c.getColumnIndex(Sms.TYPE); |
| mSmsColRead = c.getColumnIndex(Sms.READ); |
| mSmsColSubject = c.getColumnIndex(Sms.BODY); |
| mSmsColAddress = c.getColumnIndex(Sms.ADDRESS); |
| mSmsColDate = c.getColumnIndex(Sms.DATE); |
| mSmsColType = c.getColumnIndex(Sms.TYPE); |
| mSmsColThreadId = c.getColumnIndex(Sms.THREAD_ID); |
| } |
| |
| public void setMmsColumns(Cursor c) { |
| mMmsColId = c.getColumnIndex(BaseColumns._ID); |
| mMmsColFolder = c.getColumnIndex(Mms.MESSAGE_BOX); |
| mMmsColRead = c.getColumnIndex(Mms.READ); |
| mMmsColAttachmentSize = c.getColumnIndex(Mms.MESSAGE_SIZE); |
| mMmsColTextOnly = c.getColumnIndex(Mms.TEXT_ONLY); |
| mMmsColSize = c.getColumnIndex(Mms.MESSAGE_SIZE); |
| mMmsColDate = c.getColumnIndex(Mms.DATE); |
| mMmsColSubject = c.getColumnIndex(Mms.SUBJECT); |
| mMmsColThreadId = c.getColumnIndex(Mms.THREAD_ID); |
| } |
| } |
| |
| public BluetoothMapContent(final Context context, BluetoothMapAccountItem account, |
| BluetoothMapMasInstance mas) { |
| mContext = context; |
| mResolver = mContext.getContentResolver(); |
| mMasInstance = mas; |
| if (mResolver == null) { |
| if (D) { |
| Log.d(TAG, "getContentResolver failed"); |
| } |
| } |
| |
| if (account != null) { |
| mBaseUri = account.mBase_uri + "/"; |
| mAccount = account; |
| } else { |
| mBaseUri = null; |
| mAccount = null; |
| } |
| } |
| |
| private static void close(Closeable c) { |
| try { |
| if (c != null) { |
| c.close(); |
| } |
| } catch (IOException e) { |
| } |
| } |
| |
| private void setProtected(BluetoothMapMessageListingElement e, Cursor c, FilterInfo fi, |
| BluetoothMapAppParams ap) { |
| if ((ap.getParameterMask() & MASK_PROTECTED) != 0) { |
| String protect = "no"; |
| if (fi.mMsgType == FilterInfo.TYPE_EMAIL || fi.mMsgType == FilterInfo.TYPE_IM) { |
| int flagProtected = c.getInt(fi.mMessageColProtected); |
| if (flagProtected == 1) { |
| protect = "yes"; |
| } |
| } |
| if (V) { |
| Log.d(TAG, "setProtected: " + protect + "\n"); |
| } |
| e.setProtect(protect); |
| } |
| } |
| |
| private void setThreadId(BluetoothMapMessageListingElement e, Cursor c, FilterInfo fi, |
| BluetoothMapAppParams ap) { |
| if ((ap.getParameterMask() & MASK_CONVERSATION_ID) != 0) { |
| long threadId = 0; |
| TYPE type = TYPE.SMS_GSM; // Just used for handle encoding |
| if (fi.mMsgType == FilterInfo.TYPE_SMS) { |
| threadId = c.getLong(fi.mSmsColThreadId); |
| } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { |
| threadId = c.getLong(fi.mMmsColThreadId); |
| type = TYPE.MMS; // Just used for handle encoding |
| } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL || fi.mMsgType == FilterInfo.TYPE_IM) { |
| threadId = c.getLong(fi.mMessageColThreadId); |
| type = TYPE.EMAIL; // Just used for handle encoding |
| } |
| e.setThreadId(threadId, type); |
| if (V) { |
| Log.d(TAG, "setThreadId: " + threadId + "\n"); |
| } |
| } |
| } |
| |
| private void setThreadName(BluetoothMapMessageListingElement e, Cursor c, FilterInfo fi, |
| BluetoothMapAppParams ap) { |
| // TODO: Maybe this should be valid for SMS/MMS |
| if ((ap.getParameterMask() & MASK_CONVERSATION_NAME) != 0) { |
| if (fi.mMsgType == FilterInfo.TYPE_IM) { |
| String threadName = c.getString(fi.mMessageColThreadName); |
| e.setThreadName(threadName); |
| if (V) { |
| Log.d(TAG, "setThreadName: " + threadName + "\n"); |
| } |
| } |
| } |
| } |
| |
| |
| private void setSent(BluetoothMapMessageListingElement e, Cursor c, FilterInfo fi, |
| BluetoothMapAppParams ap) { |
| if ((ap.getParameterMask() & MASK_SENT) != 0) { |
| int msgType = 0; |
| if (fi.mMsgType == FilterInfo.TYPE_SMS) { |
| msgType = c.getInt(fi.mSmsColFolder); |
| } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { |
| msgType = c.getInt(fi.mMmsColFolder); |
| } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL || fi.mMsgType == FilterInfo.TYPE_IM) { |
| msgType = c.getInt(fi.mMessageColFolder); |
| } |
| String sent = null; |
| if (msgType == 2) { |
| sent = "yes"; |
| } else { |
| sent = "no"; |
| } |
| if (V) { |
| Log.d(TAG, "setSent: " + sent); |
| } |
| e.setSent(sent); |
| } |
| } |
| |
| private void setRead(BluetoothMapMessageListingElement e, Cursor c, FilterInfo fi, |
| BluetoothMapAppParams ap) { |
| int read = 0; |
| if (fi.mMsgType == FilterInfo.TYPE_SMS) { |
| read = c.getInt(fi.mSmsColRead); |
| } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { |
| read = c.getInt(fi.mMmsColRead); |
| } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL || fi.mMsgType == FilterInfo.TYPE_IM) { |
| read = c.getInt(fi.mMessageColRead); |
| } |
| String setread = null; |
| |
| if (V) { |
| Log.d(TAG, "setRead: " + setread); |
| } |
| e.setRead((read == 1), ((ap.getParameterMask() & MASK_READ) != 0)); |
| } |
| |
| private void setConvoRead(BluetoothMapConvoListingElement e, Cursor c, FilterInfo fi, |
| BluetoothMapAppParams ap) { |
| String setread = null; |
| int read = 0; |
| read = c.getInt(fi.mConvoColRead); |
| |
| |
| if (V) { |
| Log.d(TAG, "setRead: " + setread); |
| } |
| e.setRead((read == 1), ((ap.getParameterMask() & MASK_READ) != 0)); |
| } |
| |
| private void setPriority(BluetoothMapMessageListingElement e, Cursor c, FilterInfo fi, |
| BluetoothMapAppParams ap) { |
| if ((ap.getParameterMask() & MASK_PRIORITY) != 0) { |
| String priority = "no"; |
| if (fi.mMsgType == FilterInfo.TYPE_EMAIL || fi.mMsgType == FilterInfo.TYPE_IM) { |
| int highPriority = c.getInt(fi.mMessageColPriority); |
| if (highPriority == 1) { |
| priority = "yes"; |
| } |
| } |
| int pri = 0; |
| if (fi.mMsgType == FilterInfo.TYPE_MMS) { |
| pri = c.getInt(c.getColumnIndex(Mms.PRIORITY)); |
| } |
| if (pri == PduHeaders.PRIORITY_HIGH) { |
| priority = "yes"; |
| } |
| if (V) { |
| Log.d(TAG, "setPriority: " + priority); |
| } |
| e.setPriority(priority); |
| } |
| } |
| |
| /** |
| * For SMS we set the attachment size to 0, as all data will be text data, hence |
| * attachments for SMS is not possible. |
| * For MMS all data is actually attachments, hence we do set the attachment size to |
| * the total message size. To provide a more accurate attachment size, one could |
| * extract the length (in bytes) of the text parts. |
| */ |
| private void setAttachment(BluetoothMapMessageListingElement e, Cursor c, FilterInfo fi, |
| BluetoothMapAppParams ap) { |
| if ((ap.getParameterMask() & MASK_ATTACHMENT_SIZE) != 0) { |
| int size = 0; |
| String attachmentMimeTypes = null; |
| if (fi.mMsgType == FilterInfo.TYPE_MMS) { |
| if (c.getInt(fi.mMmsColTextOnly) == 0) { |
| size = c.getInt(fi.mMmsColAttachmentSize); |
| if (size <= 0) { |
| // We know there are attachments, since it is not TextOnly |
| // Hence the size in the database must be wrong. |
| // Set size to 1 to indicate to the client, that attachments are present |
| if (D) { |
| Log.d(TAG, "Error in message database, size reported as: " + size |
| + " Changing size to 1"); |
| } |
| size = 1; |
| } |
| // TODO: Add handling of attachemnt mime types |
| } |
| } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL) { |
| int attachment = c.getInt(fi.mMessageColAttachment); |
| size = c.getInt(fi.mMessageColAttachmentSize); |
| if (attachment == 1 && size == 0) { |
| if (D) { |
| Log.d(TAG, "Error in message database, attachment size reported as: " + size |
| + " Changing size to 1"); |
| } |
| size = 1; /* Ensure we indicate we have attachments in the size, if the |
| message has attachments, in case the e-mail client do not |
| report a size */ |
| } |
| } else if (fi.mMsgType == FilterInfo.TYPE_IM) { |
| int attachment = c.getInt(fi.mMessageColAttachment); |
| size = c.getInt(fi.mMessageColAttachmentSize); |
| if (attachment == 1 && size == 0) { |
| size = 1; /* Ensure we indicate we have attachments in the size, it the |
| message has attachments, in case the e-mail client do not |
| report a size */ |
| attachmentMimeTypes = c.getString(fi.mMessageColAttachmentMime); |
| } |
| } |
| if (V) { |
| Log.d(TAG, "setAttachmentSize: " + size + "\n" + "setAttachmentMimeTypes: " |
| + attachmentMimeTypes); |
| } |
| e.setAttachmentSize(size); |
| |
| if ((mMsgListingVersion > BluetoothMapUtils.MAP_MESSAGE_LISTING_FORMAT_V10) && ( |
| (ap.getParameterMask() & MASK_ATTACHMENT_MIME) != 0)) { |
| e.setAttachmentMimeTypes(attachmentMimeTypes); |
| } |
| } |
| } |
| |
| private void setText(BluetoothMapMessageListingElement e, Cursor c, FilterInfo fi, |
| BluetoothMapAppParams ap) { |
| if ((ap.getParameterMask() & MASK_TEXT) != 0) { |
| String hasText = ""; |
| if (fi.mMsgType == FilterInfo.TYPE_SMS) { |
| hasText = "yes"; |
| } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { |
| int textOnly = c.getInt(fi.mMmsColTextOnly); |
| if (textOnly == 1) { |
| hasText = "yes"; |
| } else { |
| long id = c.getLong(fi.mMmsColId); |
| String text = getTextPartsMms(mResolver, id); |
| if (text != null && text.length() > 0) { |
| hasText = "yes"; |
| } else { |
| hasText = "no"; |
| } |
| } |
| } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL || fi.mMsgType == FilterInfo.TYPE_IM) { |
| hasText = "yes"; |
| } |
| if (V) { |
| Log.d(TAG, "setText: " + hasText); |
| } |
| e.setText(hasText); |
| } |
| } |
| |
| private void setReceptionStatus(BluetoothMapMessageListingElement e, Cursor c, FilterInfo fi, |
| BluetoothMapAppParams ap) { |
| if ((ap.getParameterMask() & MASK_RECEPTION_STATUS) != 0) { |
| String status = "complete"; |
| if (V) { |
| Log.d(TAG, "setReceptionStatus: " + status); |
| } |
| e.setReceptionStatus(status); |
| } |
| } |
| |
| private void setDeliveryStatus(BluetoothMapMessageListingElement e, Cursor c, FilterInfo fi, |
| BluetoothMapAppParams ap) { |
| if ((ap.getParameterMask() & MASK_DELIVERY_STATUS) != 0) { |
| String deliveryStatus = "delivered"; |
| // TODO: Should be handled for SMS and MMS as well |
| if (fi.mMsgType == FilterInfo.TYPE_EMAIL || fi.mMsgType == FilterInfo.TYPE_IM) { |
| deliveryStatus = c.getString(fi.mMessageColDelivery); |
| } |
| if (V) { |
| Log.d(TAG, "setDeliveryStatus: " + deliveryStatus); |
| } |
| e.setDeliveryStatus(deliveryStatus); |
| } |
| } |
| |
| private void setSize(BluetoothMapMessageListingElement e, Cursor c, FilterInfo fi, |
| BluetoothMapAppParams ap) { |
| if ((ap.getParameterMask() & MASK_SIZE) != 0) { |
| int size = 0; |
| if (fi.mMsgType == FilterInfo.TYPE_SMS) { |
| String subject = c.getString(fi.mSmsColSubject); |
| size = subject.length(); |
| } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { |
| size = c.getInt(fi.mMmsColSize); |
| //MMS complete size = attachment_size + subject length |
| String subject = e.getSubject(); |
| if (subject == null || subject.length() == 0) { |
| // Handle setSubject if not done case |
| setSubject(e, c, fi, ap); |
| } |
| if (subject != null && subject.length() != 0) { |
| size += subject.length(); |
| } |
| } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL || fi.mMsgType == FilterInfo.TYPE_IM) { |
| size = c.getInt(fi.mMessageColSize); |
| } |
| if (size <= 0) { |
| // A message cannot have size 0 |
| // Hence the size in the database must be wrong. |
| // Set size to 1 to indicate to the client, that the message has content. |
| if (D) { |
| Log.d(TAG, "Error in message database, size reported as: " + size |
| + " Changing size to 1"); |
| } |
| size = 1; |
| } |
| if (V) { |
| Log.d(TAG, "setSize: " + size); |
| } |
| e.setSize(size); |
| } |
| } |
| |
| private TYPE getType(Cursor c, FilterInfo fi) { |
| TYPE type = null; |
| if (V) { |
| Log.d(TAG, "getType: for filterMsgType" + fi.mMsgType); |
| } |
| if (fi.mMsgType == FilterInfo.TYPE_SMS) { |
| if (V) { |
| Log.d(TAG, "getType: phoneType for SMS " + fi.mPhoneType); |
| } |
| if (fi.mPhoneType == TelephonyManager.PHONE_TYPE_CDMA) { |
| type = TYPE.SMS_CDMA; |
| } else { |
| type = TYPE.SMS_GSM; |
| } |
| } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { |
| type = TYPE.MMS; |
| } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL) { |
| type = TYPE.EMAIL; |
| } else if (fi.mMsgType == FilterInfo.TYPE_IM) { |
| type = TYPE.IM; |
| } |
| if (V) { |
| Log.d(TAG, "getType: " + type); |
| } |
| |
| return type; |
| } |
| |
| private void setFolderType(BluetoothMapMessageListingElement e, Cursor c, FilterInfo fi, |
| BluetoothMapAppParams ap) { |
| if ((ap.getParameterMask() & MASK_FOLDER_TYPE) != 0) { |
| String folderType = null; |
| int folderId = 0; |
| if (fi.mMsgType == FilterInfo.TYPE_SMS) { |
| folderId = c.getInt(fi.mSmsColFolder); |
| if (folderId == 1) { |
| folderType = BluetoothMapContract.FOLDER_NAME_INBOX; |
| } else if (folderId == 2) { |
| folderType = BluetoothMapContract.FOLDER_NAME_SENT; |
| } else if (folderId == 3) { |
| folderType = BluetoothMapContract.FOLDER_NAME_DRAFT; |
| } else if (folderId == 4 || folderId == 5 || folderId == 6) { |
| folderType = BluetoothMapContract.FOLDER_NAME_OUTBOX; |
| } else { |
| folderType = BluetoothMapContract.FOLDER_NAME_DELETED; |
| } |
| } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { |
| folderId = c.getInt(fi.mMmsColFolder); |
| if (folderId == 1) { |
| folderType = BluetoothMapContract.FOLDER_NAME_INBOX; |
| } else if (folderId == 2) { |
| folderType = BluetoothMapContract.FOLDER_NAME_SENT; |
| } else if (folderId == 3) { |
| folderType = BluetoothMapContract.FOLDER_NAME_DRAFT; |
| } else if (folderId == 4) { |
| folderType = BluetoothMapContract.FOLDER_NAME_OUTBOX; |
| } else { |
| folderType = BluetoothMapContract.FOLDER_NAME_DELETED; |
| } |
| } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL) { |
| // TODO: need to find name from id and then set folder type |
| } else if (fi.mMsgType == FilterInfo.TYPE_IM) { |
| folderId = c.getInt(fi.mMessageColFolder); |
| if (folderId == BluetoothMapContract.FOLDER_ID_INBOX) { |
| folderType = BluetoothMapContract.FOLDER_NAME_INBOX; |
| } else if (folderId == BluetoothMapContract.FOLDER_ID_SENT) { |
| folderType = BluetoothMapContract.FOLDER_NAME_SENT; |
| } else if (folderId == BluetoothMapContract.FOLDER_ID_DRAFT) { |
| folderType = BluetoothMapContract.FOLDER_NAME_DRAFT; |
| } else if (folderId == BluetoothMapContract.FOLDER_ID_OUTBOX) { |
| folderType = BluetoothMapContract.FOLDER_NAME_OUTBOX; |
| } else if (folderId == BluetoothMapContract.FOLDER_ID_DELETED) { |
| folderType = BluetoothMapContract.FOLDER_NAME_DELETED; |
| } else { |
| folderType = BluetoothMapContract.FOLDER_NAME_OTHER; |
| } |
| } |
| if (V) { |
| Log.d(TAG, "setFolderType: " + folderType); |
| } |
| e.setFolderType(folderType); |
| } |
| } |
| |
| private String getRecipientNameEmail(BluetoothMapMessageListingElement e, Cursor c, |
| FilterInfo fi) { |
| |
| String toAddress, ccAddress, bccAddress; |
| toAddress = c.getString(fi.mMessageColToAddress); |
| ccAddress = c.getString(fi.mMessageColCcAddress); |
| bccAddress = c.getString(fi.mMessageColBccAddress); |
| |
| StringBuilder sb = new StringBuilder(); |
| if (toAddress != null) { |
| Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(toAddress); |
| if (tokens.length != 0) { |
| if (D) { |
| Log.d(TAG, "toName count= " + tokens.length); |
| } |
| int i = 0; |
| boolean first = true; |
| while (i < tokens.length) { |
| if (V) { |
| Log.d(TAG, "ToName = " + tokens[i].toString()); |
| } |
| String name = tokens[i].getName(); |
| if (!first) { |
| sb.append("; "); //Delimiter |
| } |
| sb.append(name); |
| first = false; |
| i++; |
| } |
| } |
| |
| if (ccAddress != null) { |
| sb.append("; "); |
| } |
| } |
| if (ccAddress != null) { |
| Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(ccAddress); |
| if (tokens.length != 0) { |
| if (D) { |
| Log.d(TAG, "ccName count= " + tokens.length); |
| } |
| int i = 0; |
| boolean first = true; |
| while (i < tokens.length) { |
| if (V) { |
| Log.d(TAG, "ccName = " + tokens[i].toString()); |
| } |
| String name = tokens[i].getName(); |
| if (!first) { |
| sb.append("; "); //Delimiter |
| } |
| sb.append(name); |
| first = false; |
| i++; |
| } |
| } |
| if (bccAddress != null) { |
| sb.append("; "); |
| } |
| } |
| if (bccAddress != null) { |
| Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(bccAddress); |
| if (tokens.length != 0) { |
| if (D) { |
| Log.d(TAG, "bccName count= " + tokens.length); |
| } |
| int i = 0; |
| boolean first = true; |
| while (i < tokens.length) { |
| if (V) { |
| Log.d(TAG, "bccName = " + tokens[i].toString()); |
| } |
| String name = tokens[i].getName(); |
| if (!first) { |
| sb.append("; "); //Delimiter |
| } |
| sb.append(name); |
| first = false; |
| i++; |
| } |
| } |
| } |
| return sb.toString(); |
| } |
| |
| private String getRecipientAddressingEmail(BluetoothMapMessageListingElement e, Cursor c, |
| FilterInfo fi) { |
| String toAddress, ccAddress, bccAddress; |
| toAddress = c.getString(fi.mMessageColToAddress); |
| ccAddress = c.getString(fi.mMessageColCcAddress); |
| bccAddress = c.getString(fi.mMessageColBccAddress); |
| |
| StringBuilder sb = new StringBuilder(); |
| if (toAddress != null) { |
| Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(toAddress); |
| if (tokens.length != 0) { |
| if (D) { |
| Log.d(TAG, "toAddress count= " + tokens.length); |
| } |
| int i = 0; |
| boolean first = true; |
| while (i < tokens.length) { |
| if (V) { |
| Log.d(TAG, "ToAddress = " + tokens[i].toString()); |
| } |
| String email = tokens[i].getAddress(); |
| if (!first) { |
| sb.append("; "); //Delimiter |
| } |
| sb.append(email); |
| first = false; |
| i++; |
| } |
| } |
| |
| if (ccAddress != null) { |
| sb.append("; "); |
| } |
| } |
| if (ccAddress != null) { |
| Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(ccAddress); |
| if (tokens.length != 0) { |
| if (D) { |
| Log.d(TAG, "ccAddress count= " + tokens.length); |
| } |
| int i = 0; |
| boolean first = true; |
| while (i < tokens.length) { |
| if (V) { |
| Log.d(TAG, "ccAddress = " + tokens[i].toString()); |
| } |
| String email = tokens[i].getAddress(); |
| if (!first) { |
| sb.append("; "); //Delimiter |
| } |
| sb.append(email); |
| first = false; |
| i++; |
| } |
| } |
| if (bccAddress != null) { |
| sb.append("; "); |
| } |
| } |
| if (bccAddress != null) { |
| Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(bccAddress); |
| if (tokens.length != 0) { |
| if (D) { |
| Log.d(TAG, "bccAddress count= " + tokens.length); |
| } |
| int i = 0; |
| boolean first = true; |
| while (i < tokens.length) { |
| if (V) { |
| Log.d(TAG, "bccAddress = " + tokens[i].toString()); |
| } |
| String email = tokens[i].getAddress(); |
| if (!first) { |
| sb.append("; "); //Delimiter |
| } |
| sb.append(email); |
| first = false; |
| i++; |
| } |
| } |
| } |
| return sb.toString(); |
| } |
| |
| private void setRecipientAddressing(BluetoothMapMessageListingElement e, Cursor c, |
| FilterInfo fi, BluetoothMapAppParams ap) { |
| if ((ap.getParameterMask() & MASK_RECIPIENT_ADDRESSING) != 0) { |
| String address = null; |
| if (fi.mMsgType == FilterInfo.TYPE_SMS) { |
| int msgType = c.getInt(fi.mSmsColType); |
| if (msgType == Sms.MESSAGE_TYPE_INBOX) { |
| address = fi.mPhoneNum; |
| } else { |
| address = c.getString(c.getColumnIndex(Sms.ADDRESS)); |
| } |
| if ((address == null) && msgType == Sms.MESSAGE_TYPE_DRAFT) { |
| // Fetch address for Drafts folder from "canonical_address" table |
| int threadIdInd = c.getColumnIndex(Sms.THREAD_ID); |
| String threadIdStr = c.getString(threadIdInd); |
| // If a draft message has no recipient, it has no thread ID |
| // hence threadIdStr could possibly be null |
| if (threadIdStr != null) { |
| address = getCanonicalAddressSms(mResolver, Integer.valueOf(threadIdStr)); |
| } |
| if (V) { |
| Log.v(TAG, "threadId = " + threadIdStr + " adress:" + address + "\n"); |
| } |
| } |
| } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { |
| long id = c.getLong(c.getColumnIndex(BaseColumns._ID)); |
| address = getAddressMms(mResolver, id, MMS_TO); |
| } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL) { |
| /* Might be another way to handle addresses */ |
| address = getRecipientAddressingEmail(e, c, fi); |
| } |
| if (V) { |
| Log.v(TAG, "setRecipientAddressing: " + address); |
| } |
| if (address == null) { |
| address = ""; |
| } |
| e.setRecipientAddressing(address); |
| } |
| } |
| |
| private void setRecipientName(BluetoothMapMessageListingElement e, Cursor c, FilterInfo fi, |
| BluetoothMapAppParams ap) { |
| if ((ap.getParameterMask() & MASK_RECIPIENT_NAME) != 0) { |
| String name = null; |
| if (fi.mMsgType == FilterInfo.TYPE_SMS) { |
| int msgType = c.getInt(fi.mSmsColType); |
| if (msgType != 1) { |
| String phone = c.getString(fi.mSmsColAddress); |
| if (phone != null && !phone.isEmpty()) { |
| name = getContactNameFromPhone(phone, mResolver); |
| } |
| } else { |
| name = fi.mPhoneAlphaTag; |
| } |
| } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { |
| long id = c.getLong(fi.mMmsColId); |
| String phone; |
| if (e.getRecipientAddressing() != null) { |
| phone = getAddressMms(mResolver, id, MMS_TO); |
| } else { |
| phone = e.getRecipientAddressing(); |
| } |
| if (phone != null && !phone.isEmpty()) { |
| name = getContactNameFromPhone(phone, mResolver); |
| } |
| } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL) { |
| /* Might be another way to handle address and names */ |
| name = getRecipientNameEmail(e, c, fi); |
| } |
| if (V) { |
| Log.v(TAG, "setRecipientName: " + name); |
| } |
| if (name == null) { |
| name = ""; |
| } |
| e.setRecipientName(name); |
| } |
| } |
| |
| private void setSenderAddressing(BluetoothMapMessageListingElement e, Cursor c, FilterInfo fi, |
| BluetoothMapAppParams ap) { |
| if ((ap.getParameterMask() & MASK_SENDER_ADDRESSING) != 0) { |
| String address = ""; |
| String tempAddress; |
| if (fi.mMsgType == FilterInfo.TYPE_SMS) { |
| int msgType = c.getInt(fi.mSmsColType); |
| if (msgType == 1) { // INBOX |
| tempAddress = c.getString(fi.mSmsColAddress); |
| } else { |
| tempAddress = fi.mPhoneNum; |
| } |
| if (tempAddress == null) { |
| /* This can only happen on devices with no SIM - |
| hence will typically not have any SMS messages. */ |
| } else { |
| address = PhoneNumberUtils.extractNetworkPortion(tempAddress); |
| /* extractNetworkPortion can return N if the number is a service "number" = |
| * a string with the a name in (i.e. "Some-Tele-company" would return N |
| * because of the N in compaNy) |
| * Hence we need to check if the number is actually a string with alpha chars. |
| * */ |
| Boolean alpha = PhoneNumberUtils.stripSeparators(tempAddress) |
| .matches("[0-9]*[a-zA-Z]+[0-9]*"); |
| |
| if (address == null || address.length() < 2 || alpha) { |
| address = tempAddress; // if the number is a service acsii text just use it |
| } |
| } |
| } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { |
| long id = c.getLong(fi.mMmsColId); |
| tempAddress = getAddressMms(mResolver, id, MMS_FROM); |
| address = PhoneNumberUtils.extractNetworkPortion(tempAddress); |
| if (address == null || address.length() < 1) { |
| address = tempAddress; // if the number is a service acsii text just use it |
| } |
| } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL/* || |
| fi.mMsgType == FilterInfo.TYPE_IM*/) { |
| String nameEmail = c.getString(fi.mMessageColFromAddress); |
| Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(nameEmail); |
| if (tokens.length != 0) { |
| if (D) { |
| Log.d(TAG, "Originator count= " + tokens.length); |
| } |
| int i = 0; |
| boolean first = true; |
| while (i < tokens.length) { |
| if (V) { |
| Log.d(TAG, "SenderAddress = " + tokens[i].toString()); |
| } |
| String[] emails = new String[1]; |
| emails[0] = tokens[i].getAddress(); |
| String name = tokens[i].getName(); |
| if (!first) { |
| address += "; "; //Delimiter |
| } |
| address += emails[0]; |
| first = false; |
| i++; |
| } |
| } |
| } else if (fi.mMsgType == FilterInfo.TYPE_IM) { |
| // TODO: For IM we add the contact ID in the addressing |
| long contactId = c.getLong(fi.mMessageColFromAddress); |
| // TODO: This is a BAD hack, that we map the contact ID to a conversation ID!!! |
| // We need to reach a conclusion on what to do |
| Uri contactsUri = Uri.parse(mBaseUri + BluetoothMapContract.TABLE_CONVOCONTACT); |
| Cursor contacts = |
| mResolver.query(contactsUri, BluetoothMapContract.BT_CONTACT_PROJECTION, |
| BluetoothMapContract.ConvoContactColumns.CONVO_ID + " = " |
| + contactId, null, null); |
| try { |
| // TODO this will not work for group-chats |
| if (contacts != null && contacts.moveToFirst()) { |
| address = contacts.getString(contacts.getColumnIndex( |
| BluetoothMapContract.ConvoContactColumns.UCI)); |
| } |
| } finally { |
| if (contacts != null) { |
| contacts.close(); |
| } |
| } |
| |
| } |
| if (V) { |
| Log.v(TAG, "setSenderAddressing: " + address); |
| } |
| if (address == null) { |
| address = ""; |
| } |
| e.setSenderAddressing(address); |
| } |
| } |
| |
| private void setSenderName(BluetoothMapMessageListingElement e, Cursor c, FilterInfo fi, |
| BluetoothMapAppParams ap) { |
| if ((ap.getParameterMask() & MASK_SENDER_NAME) != 0) { |
| String name = ""; |
| if (fi.mMsgType == FilterInfo.TYPE_SMS) { |
| int msgType = c.getInt(c.getColumnIndex(Sms.TYPE)); |
| if (msgType == 1) { |
| String phone = c.getString(fi.mSmsColAddress); |
| if (phone != null && !phone.isEmpty()) { |
| name = getContactNameFromPhone(phone, mResolver); |
| } |
| } else { |
| name = fi.mPhoneAlphaTag; |
| } |
| } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { |
| long id = c.getLong(fi.mMmsColId); |
| String phone; |
| if (e.getSenderAddressing() != null) { |
| phone = getAddressMms(mResolver, id, MMS_FROM); |
| } else { |
| phone = e.getSenderAddressing(); |
| } |
| if (phone != null && !phone.isEmpty()) { |
| name = getContactNameFromPhone(phone, mResolver); |
| } |
| } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL/* || |
| fi.mMsgType == FilterInfo.TYPE_IM*/) { |
| String nameEmail = c.getString(fi.mMessageColFromAddress); |
| Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(nameEmail); |
| if (tokens.length != 0) { |
| if (D) { |
| Log.d(TAG, "Originator count= " + tokens.length); |
| } |
| int i = 0; |
| boolean first = true; |
| while (i < tokens.length) { |
| if (V) { |
| Log.d(TAG, "senderName = " + tokens[i].toString()); |
| } |
| String[] emails = new String[1]; |
| emails[0] = tokens[i].getAddress(); |
| String nameIn = tokens[i].getName(); |
| if (!first) { |
| name += "; "; //Delimiter |
| } |
| name += nameIn; |
| first = false; |
| i++; |
| } |
| } |
| } else if (fi.mMsgType == FilterInfo.TYPE_IM) { |
| // For IM we add the contact ID in the addressing |
| long contactId = c.getLong(fi.mMessageColFromAddress); |
| Uri contactsUri = Uri.parse(mBaseUri + BluetoothMapContract.TABLE_CONVOCONTACT); |
| Cursor contacts = |
| mResolver.query(contactsUri, BluetoothMapContract.BT_CONTACT_PROJECTION, |
| BluetoothMapContract.ConvoContactColumns.CONVO_ID + " = " |
| + contactId, null, null); |
| try { |
| // TODO this will not work for group-chats |
| if (contacts != null && contacts.moveToFirst()) { |
| name = contacts.getString(contacts.getColumnIndex( |
| BluetoothMapContract.ConvoContactColumns.NAME)); |
| } |
| } finally { |
| if (contacts != null) { |
| contacts.close(); |
| } |
| } |
| } |
| if (V) { |
| Log.v(TAG, "setSenderName: " + name); |
| } |
| if (name == null) { |
| name = ""; |
| } |
| e.setSenderName(name); |
| } |
| } |
| |
| |
| private void setDateTime(BluetoothMapMessageListingElement e, Cursor c, FilterInfo fi, |
| BluetoothMapAppParams ap) { |
| if ((ap.getParameterMask() & MASK_DATETIME) != 0) { |
| long date = 0; |
| if (fi.mMsgType == FilterInfo.TYPE_SMS) { |
| date = c.getLong(fi.mSmsColDate); |
| } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { |
| /* Use Mms.DATE for all messages. Although contract class states */ |
| /* Mms.DATE_SENT are for outgoing messages. But that is not working. */ |
| date = c.getLong(fi.mMmsColDate) * 1000L; |
| |
| /* int msgBox = c.getInt(c.getColumnIndex(Mms.MESSAGE_BOX)); */ |
| /* if (msgBox == Mms.MESSAGE_BOX_INBOX) { */ |
| /* date = c.getLong(c.getColumnIndex(Mms.DATE)) * 1000L; */ |
| /* } else { */ |
| /* date = c.getLong(c.getColumnIndex(Mms.DATE_SENT)) * 1000L; */ |
| /* } */ |
| } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL || fi.mMsgType == FilterInfo.TYPE_IM) { |
| date = c.getLong(fi.mMessageColDate); |
| } |
| e.setDateTime(date); |
| } |
| } |
| |
| |
| private void setLastActivity(BluetoothMapConvoListingElement e, Cursor c, FilterInfo fi, |
| BluetoothMapAppParams ap) { |
| long date = 0; |
| if (fi.mMsgType == FilterInfo.TYPE_SMS || fi.mMsgType == FilterInfo.TYPE_MMS) { |
| date = c.getLong(MMS_SMS_THREAD_COL_DATE); |
| } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL || fi.mMsgType == FilterInfo.TYPE_IM) { |
| date = c.getLong(fi.mConvoColLastActivity); |
| } |
| e.setLastActivity(date); |
| if (V) { |
| Log.v(TAG, "setDateTime: " + e.getLastActivityString()); |
| } |
| |
| } |
| |
| public static String getTextPartsMms(ContentResolver r, long id) { |
| String text = ""; |
| String selection = new String("mid=" + id); |
| String uriStr = new String(Mms.CONTENT_URI + "/" + id + "/part"); |
| Uri uriAddress = Uri.parse(uriStr); |
| // TODO: maybe use a projection with only "ct" and "text" |
| Cursor c = r.query(uriAddress, null, selection, null, null); |
| try { |
| if (c != null && c.moveToFirst()) { |
| do { |
| String ct = c.getString(c.getColumnIndex("ct")); |
| if (ct.equals("text/plain")) { |
| String part = c.getString(c.getColumnIndex("text")); |
| if (part != null) { |
| text += part; |
| } |
| } |
| } while (c.moveToNext()); |
| } |
| } finally { |
| if (c != null) { |
| c.close(); |
| } |
| } |
| |
| return text; |
| } |
| |
| private void setSubject(BluetoothMapMessageListingElement e, Cursor c, FilterInfo fi, |
| BluetoothMapAppParams ap) { |
| String subject = ""; |
| int subLength = ap.getSubjectLength(); |
| if (subLength == BluetoothMapAppParams.INVALID_VALUE_PARAMETER) { |
| subLength = 256; |
| } |
| |
| // Fix Subject Display issue with HONDA Carkit - Ignore subject Mask. |
| if (DeviceWorkArounds.addressStartsWith(BluetoothMapService.getRemoteDevice().getAddress(), |
| DeviceWorkArounds.HONDA_CARKIT) |
| || (ap.getParameterMask() & MASK_SUBJECT) != 0) { |
| if (fi.mMsgType == FilterInfo.TYPE_SMS) { |
| subject = c.getString(fi.mSmsColSubject); |
| } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { |
| subject = c.getString(fi.mMmsColSubject); |
| if (subject == null || subject.length() == 0) { |
| /* Get subject from mms text body parts - if any exists */ |
| long id = c.getLong(fi.mMmsColId); |
| subject = getTextPartsMms(mResolver, id); |
| } |
| } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL || fi.mMsgType == FilterInfo.TYPE_IM) { |
| subject = c.getString(fi.mMessageColSubject); |
| } |
| if (subject != null && subject.length() > subLength) { |
| subject = subject.substring(0, subLength); |
| } else if (subject == null) { |
| subject = ""; |
| } |
| if (V) { |
| Log.d(TAG, "setSubject: " + subject); |
| } |
| e.setSubject(subject); |
| } |
| } |
| |
| private void setHandle(BluetoothMapMessageListingElement e, Cursor c, FilterInfo fi, |
| BluetoothMapAppParams ap) { |
| long handle = -1; |
| if (fi.mMsgType == FilterInfo.TYPE_SMS) { |
| handle = c.getLong(fi.mSmsColId); |
| } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { |
| handle = c.getLong(fi.mMmsColId); |
| } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL || fi.mMsgType == FilterInfo.TYPE_IM) { |
| handle = c.getLong(fi.mMessageColId); |
| } |
| if (V) { |
| Log.d(TAG, "setHandle: " + handle); |
| } |
| e.setHandle(handle); |
| } |
| |
| private BluetoothMapMessageListingElement element(Cursor c, FilterInfo fi, |
| BluetoothMapAppParams ap) { |
| BluetoothMapMessageListingElement e = new BluetoothMapMessageListingElement(); |
| setHandle(e, c, fi, ap); |
| setDateTime(e, c, fi, ap); |
| e.setType(getType(c, fi), (ap.getParameterMask() & MASK_TYPE) != 0); |
| setRead(e, c, fi, ap); |
| // we set number and name for sender/recipient later |
| // they require lookup on contacts so no need to |
| // do it for all elements unless they are to be used. |
| e.setCursorIndex(c.getPosition()); |
| return e; |
| } |
| |
| private BluetoothMapConvoListingElement createConvoElement(Cursor c, FilterInfo fi, |
| BluetoothMapAppParams ap) { |
| BluetoothMapConvoListingElement e = new BluetoothMapConvoListingElement(); |
| setLastActivity(e, c, fi, ap); |
| e.setType(getType(c, fi)); |
| // setConvoRead(e, c, fi, ap); |
| e.setCursorIndex(c.getPosition()); |
| return e; |
| } |
| |
| /* TODO: Change to use SmsMmsContacts.getContactNameFromPhone() with proper use of |
| * caching. */ |
| public static String getContactNameFromPhone(String phone, ContentResolver resolver) { |
| String name = null; |
| //Handle possible exception for empty phone address |
| if (TextUtils.isEmpty(phone)) { |
| return name; |
| } |
| |
| Uri uri = |
| Uri.withAppendedPath(PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI, Uri.encode(phone)); |
| |
| String[] projection = {Contacts._ID, Contacts.DISPLAY_NAME}; |
| String selection = Contacts.IN_VISIBLE_GROUP + "=1"; |
| String orderBy = Contacts.DISPLAY_NAME + " ASC"; |
| Cursor c = null; |
| try { |
| c = resolver.query(uri, projection, selection, null, orderBy); |
| if (c != null) { |
| int colIndex = c.getColumnIndex(Contacts.DISPLAY_NAME); |
| if (c.getCount() >= 1) { |
| c.moveToFirst(); |
| name = c.getString(colIndex); |
| } |
| } |
| } finally { |
| if (c != null) { |
| c.close(); |
| } |
| } |
| return name; |
| } |
| |
| private static final String[] RECIPIENT_ID_PROJECTION = {Threads.RECIPIENT_IDS}; |
| |
| /** |
| * Get SMS RecipientAddresses for DRAFT folder based on threadId |
| * |
| */ |
| public static String getCanonicalAddressSms(ContentResolver r, int threadId) { |
| |
| /* |
| 1. Get Recipient Ids from Threads.CONTENT_URI |
| 2. Get Recipient Address for corresponding Id from canonical-addresses table. |
| */ |
| |
| //Uri sAllCanonical = Uri.parse("content://mms-sms/canonical-addresses"); |
| Uri sAllCanonical = |
| MmsSms.CONTENT_URI.buildUpon().appendPath("canonical-addresses").build(); |
| Uri sAllThreadsUri = |
| Threads.CONTENT_URI.buildUpon().appendQueryParameter("simple", "true").build(); |
| Cursor cr = null; |
| String recipientAddress = ""; |
| String recipientIds = null; |
| String whereClause = "_id=" + threadId; |
| if (V) { |
| Log.v(TAG, "whereClause is " + whereClause); |
| } |
| try { |
| cr = r.query(sAllThreadsUri, RECIPIENT_ID_PROJECTION, whereClause, null, null); |
| if (cr != null && cr.moveToFirst()) { |
| recipientIds = cr.getString(0); |
| if (V) { |
| Log.v(TAG, |
| "cursor.getCount(): " + cr.getCount() + "recipientIds: " + recipientIds |
| + "selection: " + whereClause); |
| } |
| } |
| } finally { |
| if (cr != null) { |
| cr.close(); |
| cr = null; |
| } |
| } |
| if (V) { |
| Log.v(TAG, "recipientIds with spaces: " + recipientIds + "\n"); |
| } |
| if (recipientIds != null) { |
| String[] recipients = null; |
| whereClause = ""; |
| recipients = recipientIds.split(" "); |
| for (String id : recipients) { |
| if (whereClause.length() != 0) { |
| whereClause += " OR "; |
| } |
| whereClause += "_id=" + id; |
| } |
| if (V) { |
| Log.v(TAG, "whereClause is " + whereClause); |
| } |
| try { |
| cr = r.query(sAllCanonical, null, whereClause, null, null); |
| if (cr != null && cr.moveToFirst()) { |
| do { |
| //TODO: Multiple Recipeints are appended with ";" for now. |
| if (recipientAddress.length() != 0) { |
| recipientAddress += ";"; |
| } |
| recipientAddress += |
| cr.getString(cr.getColumnIndex(CanonicalAddressesColumns.ADDRESS)); |
| } while (cr.moveToNext()); |
| } |
| } finally { |
| if (cr != null) { |
| cr.close(); |
| } |
| } |
| } |
| |
| if (V) { |
| Log.v(TAG, "Final recipientAddress : " + recipientAddress); |
| } |
| return recipientAddress; |
| } |
| |
| public static String getAddressMms(ContentResolver r, long id, int type) { |
| String selection = new String("msg_id=" + id + " AND type=" + type); |
| String uriStr = new String(Mms.CONTENT_URI + "/" + id + "/addr"); |
| Uri uriAddress = Uri.parse(uriStr); |
| String addr = null; |
| String[] projection = {Mms.Addr.ADDRESS}; |
| Cursor c = null; |
| try { |
| c = r.query(uriAddress, projection, selection, null, null); // TODO: Add projection |
| int colIndex = c.getColumnIndex(Mms.Addr.ADDRESS); |
| if (c != null) { |
| if (c.moveToFirst()) { |
| addr = c.getString(colIndex); |
| if (addr.equals(INSERT_ADDRES_TOKEN)) { |
| addr = ""; |
| } |
| } |
| } |
| } finally { |
| if (c != null) { |
| c.close(); |
| } |
| } |
| return addr; |
| } |
| |
| /** |
| * Matching functions for originator and recipient for MMS |
| * @return true if found a match |
| */ |
| private boolean matchRecipientMms(Cursor c, FilterInfo fi, String recip) { |
| boolean res; |
| long id = c.getLong(c.getColumnIndex(BaseColumns._ID)); |
| String phone = getAddressMms(mResolver, id, MMS_TO); |
| if (phone != null && phone.length() > 0) { |
| if (phone.matches(recip)) { |
| if (V) { |
| Log.v(TAG, "matchRecipientMms: match recipient phone = " + phone); |
| } |
| res = true; |
| } else { |
| String name = getContactNameFromPhone(phone, mResolver); |
| if (name != null && name.length() > 0 && name.matches(recip)) { |
| if (V) { |
| Log.v(TAG, "matchRecipientMms: match recipient name = " + name); |
| } |
| res = true; |
| } else { |
| res = false; |
| } |
| } |
| } else { |
| res = false; |
| } |
| return res; |
| } |
| |
| private boolean matchRecipientSms(Cursor c, FilterInfo fi, String recip) { |
| boolean res; |
| int msgType = c.getInt(c.getColumnIndex(Sms.TYPE)); |
| if (msgType == 1) { |
| String phone = fi.mPhoneNum; |
| String name = fi.mPhoneAlphaTag; |
| if (phone != null && phone.length() > 0 && phone.matches(recip)) { |
| if (V) { |
| Log.v(TAG, "matchRecipientSms: match recipient phone = " + phone); |
| } |
| res = true; |
| } else if (name != null && name.length() > 0 && name.matches(recip)) { |
| if (V) { |
| Log.v(TAG, "matchRecipientSms: match recipient name = " + name); |
| } |
| res = true; |
| } else { |
| res = false; |
| } |
| } else { |
| String phone = c.getString(c.getColumnIndex(Sms.ADDRESS)); |
| if (phone != null && phone.length() > 0) { |
| if (phone.matches(recip)) { |
| if (V) { |
| Log.v(TAG, "matchRecipientSms: match recipient phone = " + phone); |
| } |
| res = true; |
| } else { |
| String name = getContactNameFromPhone(phone, mResolver); |
| if (name != null && name.length() > 0 && name.matches(recip)) { |
| if (V) { |
| Log.v(TAG, "matchRecipientSms: match recipient name = " + name); |
| } |
| res = true; |
| } else { |
| res = false; |
| } |
| } |
| } else { |
| res = false; |
| } |
| } |
| return res; |
| } |
| |
| private boolean matchRecipient(Cursor c, FilterInfo fi, BluetoothMapAppParams ap) { |
| boolean res; |
| String recip = ap.getFilterRecipient(); |
| if (recip != null && recip.length() > 0) { |
| recip = recip.replace("*", ".*"); |
| recip = ".*" + recip + ".*"; |
| if (fi.mMsgType == FilterInfo.TYPE_SMS) { |
| res = matchRecipientSms(c, fi, recip); |
| } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { |
| res = matchRecipientMms(c, fi, recip); |
| } else { |
| if (D) { |
| Log.d(TAG, "matchRecipient: Unknown msg type: " + fi.mMsgType); |
| } |
| res = false; |
| } |
| } else { |
| res = true; |
| } |
| return res; |
| } |
| |
| private boolean matchOriginatorMms(Cursor c, FilterInfo fi, String orig) { |
| boolean res; |
| long id = c.getLong(c.getColumnIndex(BaseColumns._ID)); |
| String phone = getAddressMms(mResolver, id, MMS_FROM); |
| if (phone != null && phone.length() > 0) { |
| if (phone.matches(orig)) { |
| if (V) { |
| Log.v(TAG, "matchOriginatorMms: match originator phone = " + phone); |
| } |
| res = true; |
| } else { |
| String name = getContactNameFromPhone(phone, mResolver); |
| if (name != null && name.length() > 0 && name.matches(orig)) { |
| if (V) { |
| Log.v(TAG, "matchOriginatorMms: match originator name = " + name); |
| } |
| res = true; |
| } else { |
| res = false; |
| } |
| } |
| } else { |
| res = false; |
| } |
| return res; |
| } |
| |
| private boolean matchOriginatorSms(Cursor c, FilterInfo fi, String orig) { |
| boolean res; |
| int msgType = c.getInt(c.getColumnIndex(Sms.TYPE)); |
| if (msgType == 1) { |
| String phone = c.getString(c.getColumnIndex(Sms.ADDRESS)); |
| if (phone != null && phone.length() > 0) { |
| if (phone.matches(orig)) { |
| if (V) { |
| Log.v(TAG, "matchOriginatorSms: match originator phone = " + phone); |
| } |
| res = true; |
| } else { |
| String name = getContactNameFromPhone(phone, mResolver); |
| if (name != null && name.length() > 0 && name.matches(orig)) { |
| if (V) { |
| Log.v(TAG, "matchOriginatorSms: match originator name = " + name); |
| } |
| res = true; |
| } else { |
| res = false; |
| } |
| } |
| } else { |
| res = false; |
| } |
| } else { |
| String phone = fi.mPhoneNum; |
| String name = fi.mPhoneAlphaTag; |
| if (phone != null && phone.length() > 0 && phone.matches(orig)) { |
| if (V) { |
| Log.v(TAG, "matchOriginatorSms: match originator phone = " + phone); |
| } |
| res = true; |
| } else if (name != null && name.length() > 0 && name.matches(orig)) { |
| if (V) { |
| Log.v(TAG, "matchOriginatorSms: match originator name = " + name); |
| } |
| res = true; |
| } else { |
| res = false; |
| } |
| } |
| return res; |
| } |
| |
| private boolean matchOriginator(Cursor c, FilterInfo fi, BluetoothMapAppParams ap) { |
| boolean res; |
| String orig = ap.getFilterOriginator(); |
| if (orig != null && orig.length() > 0) { |
| orig = orig.replace("*", ".*"); |
| orig = ".*" + orig + ".*"; |
| if (fi.mMsgType == FilterInfo.TYPE_SMS) { |
| res = matchOriginatorSms(c, fi, orig); |
| } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { |
| res = matchOriginatorMms(c, fi, orig); |
| } else { |
| if (D) { |
| Log.d(TAG, "matchOriginator: Unknown msg type: " + fi.mMsgType); |
| } |
| res = false; |
| } |
| } else { |
| res = true; |
| } |
| return res; |
| } |
| |
| private boolean matchAddresses(Cursor c, FilterInfo fi, BluetoothMapAppParams ap) { |
| return matchOriginator(c, fi, ap) && matchRecipient(c, fi, ap); |
| } |
| |
| /* |
| * Where filter functions |
| * */ |
| private String setWhereFilterFolderTypeSms(String folder) { |
| String where = ""; |
| if (BluetoothMapContract.FOLDER_NAME_INBOX.equalsIgnoreCase(folder)) { |
| where = Sms.TYPE + " = 1 AND " + Sms.THREAD_ID + " <> -1"; |
| } else if (BluetoothMapContract.FOLDER_NAME_OUTBOX.equalsIgnoreCase(folder)) { |
| where = "(" + Sms.TYPE + " = 4 OR " + Sms.TYPE + " = 5 OR " + Sms.TYPE + " = 6) AND " |
| + Sms.THREAD_ID + " <> -1"; |
| } else if (BluetoothMapContract.FOLDER_NAME_SENT.equalsIgnoreCase(folder)) { |
| where = Sms.TYPE + " = 2 AND " + Sms.THREAD_ID + " <> -1"; |
| } else if (BluetoothMapContract.FOLDER_NAME_DRAFT.equalsIgnoreCase(folder)) { |
| where = Sms.TYPE + " = 3 AND " + "(" + Sms.THREAD_ID + " IS NULL OR " + Sms.THREAD_ID |
| + " <> -1 )"; |
| } else if (BluetoothMapContract.FOLDER_NAME_DELETED.equalsIgnoreCase(folder)) { |
| where = Sms.THREAD_ID + " = -1"; |
| } |
| |
| return where; |
| } |
| |
| private String setWhereFilterFolderTypeMms(String folder) { |
| String where = ""; |
| if (BluetoothMapContract.FOLDER_NAME_INBOX.equalsIgnoreCase(folder)) { |
| where = Mms.MESSAGE_BOX + " = 1 AND " + Mms.THREAD_ID + " <> -1"; |
| } else if (BluetoothMapContract.FOLDER_NAME_OUTBOX.equalsIgnoreCase(folder)) { |
| where = Mms.MESSAGE_BOX + " = 4 AND " + Mms.THREAD_ID + " <> -1"; |
| } else if (BluetoothMapContract.FOLDER_NAME_SENT.equalsIgnoreCase(folder)) { |
| where = Mms.MESSAGE_BOX + " = 2 AND " + Mms.THREAD_ID + " <> -1"; |
| } else if (BluetoothMapContract.FOLDER_NAME_DRAFT.equalsIgnoreCase(folder)) { |
| where = Mms.MESSAGE_BOX + " = 3 AND " + "(" + Mms.THREAD_ID + " IS NULL OR " |
| + Mms.THREAD_ID + " <> -1 )"; |
| } else if (BluetoothMapContract.FOLDER_NAME_DELETED.equalsIgnoreCase(folder)) { |
| where = Mms.THREAD_ID + " = -1"; |
| } |
| |
| return where; |
| } |
| |
| private String setWhereFilterFolderTypeEmail(long folderId) { |
| String where = ""; |
| if (folderId >= 0) { |
| where = BluetoothMapContract.MessageColumns.FOLDER_ID + " = " + folderId; |
| } else { |
| Log.e(TAG, "setWhereFilterFolderTypeEmail: not valid!"); |
| throw new IllegalArgumentException("Invalid folder ID"); |
| } |
| return where; |
| } |
| |
| private String setWhereFilterFolderTypeIm(long folderId) { |
| String where = ""; |
| if (folderId > BluetoothMapContract.FOLDER_ID_OTHER) { |
| where = BluetoothMapContract.MessageColumns.FOLDER_ID + " = " + folderId; |
| } else { |
| Log.e(TAG, "setWhereFilterFolderTypeIm: not valid!"); |
| throw new IllegalArgumentException("Invalid folder ID"); |
| } |
| return where; |
| } |
| |
| private String setWhereFilterFolderType(BluetoothMapFolderElement folderElement, |
| FilterInfo fi) { |
| String where = "1=1"; |
| if (!folderElement.shouldIgnore()) { |
| if (fi.mMsgType == FilterInfo.TYPE_SMS) { |
| where = setWhereFilterFolderTypeSms(folderElement.getName()); |
| } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { |
| where = setWhereFilterFolderTypeMms(folderElement.getName()); |
| } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL) { |
| where = setWhereFilterFolderTypeEmail(folderElement.getFolderId()); |
| } else if (fi.mMsgType == FilterInfo.TYPE_IM) { |
| where = setWhereFilterFolderTypeIm(folderElement.getFolderId()); |
| } |
| } |
| |
| return where; |
| } |
| |
| private String setWhereFilterReadStatus(BluetoothMapAppParams ap, FilterInfo fi) { |
| String where = ""; |
| if (ap.getFilterReadStatus() != -1) { |
| if (fi.mMsgType == FilterInfo.TYPE_SMS) { |
| if ((ap.getFilterReadStatus() & 0x01) != 0) { |
| where = " AND " + Sms.READ + "= 0"; |
| } |
| |
| if ((ap.getFilterReadStatus() & 0x02) != 0) { |
| where = " AND " + Sms.READ + "= 1"; |
| } |
| } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { |
| if ((ap.getFilterReadStatus() & 0x01) != 0) { |
| where = " AND " + Mms.READ + "= 0"; |
| } |
| |
| if ((ap.getFilterReadStatus() & 0x02) != 0) { |
| where = " AND " + Mms.READ + "= 1"; |
| } |
| } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL || fi.mMsgType == FilterInfo.TYPE_IM) { |
| if ((ap.getFilterReadStatus() & 0x01) != 0) { |
| where = " AND " + BluetoothMapContract.MessageColumns.FLAG_READ + "= 0"; |
| } |
| if ((ap.getFilterReadStatus() & 0x02) != 0) { |
| where = " AND " + BluetoothMapContract.MessageColumns.FLAG_READ + "= 1"; |
| } |
| } |
| } |
| return where; |
| } |
| |
| private String setWhereFilterPeriod(BluetoothMapAppParams ap, FilterInfo fi) { |
| String where = ""; |
| |
| if ((ap.getFilterPeriodBegin() != -1)) { |
| if (fi.mMsgType == FilterInfo.TYPE_SMS) { |
| where = " AND " + Sms.DATE + " >= " + ap.getFilterPeriodBegin(); |
| } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { |
| where = " AND " + Mms.DATE + " >= " + (ap.getFilterPeriodBegin() / 1000L); |
| } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL || fi.mMsgType == FilterInfo.TYPE_IM) { |
| where = " AND " + BluetoothMapContract.MessageColumns.DATE + " >= " |
| + (ap.getFilterPeriodBegin()); |
| } |
| } |
| |
| if ((ap.getFilterPeriodEnd() != -1)) { |
| if (fi.mMsgType == FilterInfo.TYPE_SMS) { |
| where += " AND " + Sms.DATE + " < " + ap.getFilterPeriodEnd(); |
| } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { |
| where += " AND " + Mms.DATE + " < " + (ap.getFilterPeriodEnd() / 1000L); |
| } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL || fi.mMsgType == FilterInfo.TYPE_IM) { |
| where += " AND " + BluetoothMapContract.MessageColumns.DATE + " < " |
| + (ap.getFilterPeriodEnd()); |
| } |
| } |
| return where; |
| } |
| |
| private String setWhereFilterLastActivity(BluetoothMapAppParams ap, FilterInfo fi) { |
| String where = ""; |
| if ((ap.getFilterLastActivityBegin() != -1)) { |
| if (fi.mMsgType == FilterInfo.TYPE_SMS) { |
| where = " AND " + Sms.DATE + " >= " + ap.getFilterLastActivityBegin(); |
| } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { |
| where = " AND " + Mms.DATE + " >= " + (ap.getFilterLastActivityBegin() / 1000L); |
| } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL || fi.mMsgType == FilterInfo.TYPE_IM) { |
| where = " AND " + BluetoothMapContract.ConversationColumns.LAST_THREAD_ACTIVITY |
| + " >= " + (ap.getFilterPeriodBegin()); |
| } |
| } |
| if ((ap.getFilterLastActivityEnd() != -1)) { |
| if (fi.mMsgType == FilterInfo.TYPE_SMS) { |
| where += " AND " + Sms.DATE + " < " + ap.getFilterLastActivityEnd(); |
| } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { |
| where += " AND " + Mms.DATE + " < " + (ap.getFilterPeriodEnd() / 1000L); |
| } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL || fi.mMsgType == FilterInfo.TYPE_IM) { |
| where += " AND " + BluetoothMapContract.ConversationColumns.LAST_THREAD_ACTIVITY |
| + " < " + (ap.getFilterLastActivityEnd()); |
| } |
| } |
| return where; |
| } |
| |
| |
| private String setWhereFilterOriginatorEmail(BluetoothMapAppParams ap) { |
| String where = ""; |
| String orig = ap.getFilterOriginator(); |
| |
| /* Be aware of wild cards in the beginning of string, may not be valid? */ |
| if (orig != null && orig.length() > 0) { |
| orig = orig.replace("*", "%"); |
| where = " AND " + BluetoothMapContract.MessageColumns.FROM_LIST + " LIKE '%" + orig |
| + "%'"; |
| } |
| return where; |
| } |
| |
| private String setWhereFilterOriginatorIM(BluetoothMapAppParams ap) { |
| String where = ""; |
| String orig = ap.getFilterOriginator(); |
| |
| /* Be aware of wild cards in the beginning of string, may not be valid? */ |
| if (orig != null && orig.length() > 0) { |
| orig = orig.replace("*", "%"); |
| where = " AND " + BluetoothMapContract.MessageColumns.FROM_LIST + " LIKE '%" + orig |
| + "%'"; |
| } |
| return where; |
| } |
| |
| private String setWhereFilterPriority(BluetoothMapAppParams ap, FilterInfo fi) { |
| String where = ""; |
| int pri = ap.getFilterPriority(); |
| /*only MMS have priority info */ |
| if (fi.mMsgType == FilterInfo.TYPE_MMS) { |
| if (pri == 0x0002) { |
| where += " AND " + Mms.PRIORITY + "<=" + Integer.toString( |
| PduHeaders.PRIORITY_NORMAL); |
| } else if (pri == 0x0001) { |
| where += " AND " + Mms.PRIORITY + "=" + Integer.toString(PduHeaders.PRIORITY_HIGH); |
| } |
| } |
| if (fi.mMsgType == FilterInfo.TYPE_EMAIL || fi.mMsgType == FilterInfo.TYPE_IM) { |
| if (pri == 0x0002) { |
| where += " AND " + BluetoothMapContract.MessageColumns.FLAG_HIGH_PRIORITY + "!=1"; |
| } else if (pri == 0x0001) { |
| where += " AND " + BluetoothMapContract.MessageColumns.FLAG_HIGH_PRIORITY + "=1"; |
| } |
| } |
| // TODO: no priority filtering in IM |
| return where; |
| } |
| |
| private String setWhereFilterRecipientEmail(BluetoothMapAppParams ap) { |
| String where = ""; |
| String recip = ap.getFilterRecipient(); |
| |
| /* Be aware of wild cards in the beginning of string, may not be valid? */ |
| if (recip != null && recip.length() > 0) { |
| recip = recip.replace("*", "%"); |
| where = " AND (" + BluetoothMapContract.MessageColumns.TO_LIST + " LIKE '%" + recip |
| + "%' OR " + BluetoothMapContract.MessageColumns.CC_LIST + " LIKE '%" + recip |
| + "%' OR " + BluetoothMapContract.MessageColumns.BCC_LIST + " LIKE '%" + recip |
| + "%' )"; |
| } |
| return where; |
| } |
| |
| private String setWhereFilterMessageHandle(BluetoothMapAppParams ap, FilterInfo fi) { |
| String where = ""; |
| long id = -1; |
| String msgHandle = ap.getFilterMsgHandleString(); |
| if (msgHandle != null) { |
| id = BluetoothMapUtils.getCpHandle(msgHandle); |
| if (D) { |
| Log.d(TAG, "id: " + id); |
| } |
| } |
| if (id != -1) { |
| if (fi.mMsgType == FilterInfo.TYPE_SMS) { |
| where = " AND " + Sms._ID + " = " + id; |
| } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { |
| where = " AND " + Mms._ID + " = " + id; |
| } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL || fi.mMsgType == FilterInfo.TYPE_IM) { |
| where = " AND " + BluetoothMapContract.MessageColumns._ID + " = " + id; |
| } |
| } |
| return where; |
| } |
| |
| private String setWhereFilterThreadId(BluetoothMapAppParams ap, FilterInfo fi) { |
| String where = ""; |
| long id = -1; |
| String msgHandle = ap.getFilterConvoIdString(); |
| if (msgHandle != null) { |
| id = BluetoothMapUtils.getMsgHandleAsLong(msgHandle); |
| if (D) { |
| Log.d(TAG, "id: " + id); |
| } |
| } |
| if (id > 0) { |
| if (fi.mMsgType == FilterInfo.TYPE_SMS) { |
| where = " AND " + Sms.THREAD_ID + " = " + id; |
| } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { |
| where = " AND " + Mms.THREAD_ID + " = " + id; |
| } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL || fi.mMsgType == FilterInfo.TYPE_IM) { |
| where = " AND " + BluetoothMapContract.MessageColumns.THREAD_ID + " = " + id; |
| } |
| } |
| |
| return where; |
| } |
| |
| private String setWhereFilter(BluetoothMapFolderElement folderElement, FilterInfo fi, |
| BluetoothMapAppParams ap) { |
| String where = ""; |
| where += setWhereFilterFolderType(folderElement, fi); |
| |
| String msgHandleWhere = setWhereFilterMessageHandle(ap, fi); |
| /* if message handle filter is available, the other filters should be ignored */ |
| if (msgHandleWhere.isEmpty()) { |
| where += setWhereFilterReadStatus(ap, fi); |
| where += setWhereFilterPriority(ap, fi); |
| where += setWhereFilterPeriod(ap, fi); |
| if (fi.mMsgType == FilterInfo.TYPE_EMAIL) { |
| where += setWhereFilterOriginatorEmail(ap); |
| where += setWhereFilterRecipientEmail(ap); |
| } |
| if (fi.mMsgType == FilterInfo.TYPE_IM) { |
| where += setWhereFilterOriginatorIM(ap); |
| // TODO: set 'where' filer recipient? |
| } |
| where += setWhereFilterThreadId(ap, fi); |
| } else { |
| where += msgHandleWhere; |
| } |
| |
| return where; |
| } |
| |
| |
| /* Used only for SMS/MMS */ |
| private void setConvoWhereFilterSmsMms(StringBuilder selection, ArrayList<String> selectionArgs, |
| FilterInfo fi, BluetoothMapAppParams ap) { |
| |
| if (smsSelected(fi, ap) || mmsSelected(ap)) { |
| |
| // Filter Read Status |
| if (ap.getFilterReadStatus() != BluetoothMapAppParams.INVALID_VALUE_PARAMETER) { |
| if ((ap.getFilterReadStatus() & FILTER_READ_STATUS_UNREAD_ONLY) != 0) { |
| selection.append(" AND ").append(Threads.READ).append(" = 0"); |
| } |
| if ((ap.getFilterReadStatus() & FILTER_READ_STATUS_READ_ONLY) != 0) { |
| selection.append(" AND ").append(Threads.READ).append(" = 1"); |
| } |
| } |
| |
| // Filter time |
| if ((ap.getFilterLastActivityBegin() |
| != BluetoothMapAppParams.INVALID_VALUE_PARAMETER)) { |
| selection.append(" AND ") |
| .append(Threads.DATE) |
| .append(" >= ") |
| .append(ap.getFilterLastActivityBegin()); |
| } |
| if ((ap.getFilterLastActivityEnd() != BluetoothMapAppParams.INVALID_VALUE_PARAMETER)) { |
| selection.append(" AND ") |
| .append(Threads.DATE) |
| .append(" <= ") |
| .append(ap.getFilterLastActivityEnd()); |
| } |
| |
| // Filter ConvoId |
| long convoId = -1; |
| if (ap.getFilterConvoId() != null) { |
| convoId = ap.getFilterConvoId().getLeastSignificantBits(); |
| } |
| if (convoId > 0) { |
| selection.append(" AND ") |
| .append(Threads._ID) |
| .append(" = ") |
| .append(Long.toString(convoId)); |
| } |
| } |
| } |
| |
| |
| /** |
| * Determine from application parameter if sms should be included. |
| * The filter mask is set for message types not selected |
| * @param fi |
| * @param ap |
| * @return boolean true if sms is selected, false if not |
| */ |
| private boolean smsSelected(FilterInfo fi, BluetoothMapAppParams ap) { |
| int msgType = ap.getFilterMessageType(); |
| int phoneType = fi.mPhoneType; |
| |
| if (D) { |
| Log.d(TAG, "smsSelected msgType: " + msgType); |
| } |
| |
| if (msgType == BluetoothMapAppParams.INVALID_VALUE_PARAMETER) { |
| return true; |
| } |
| |
| if ((msgType & (BluetoothMapAppParams.FILTER_NO_SMS_CDMA |
| | BluetoothMapAppParams.FILTER_NO_SMS_GSM)) == 0) { |
| return true; |
| } |
| |
| if (((msgType & BluetoothMapAppParams.FILTER_NO_SMS_GSM) == 0) && (phoneType |
| == TelephonyManager.PHONE_TYPE_GSM)) { |
| return true; |
| } |
| |
| if (((msgType & BluetoothMapAppParams.FILTER_NO_SMS_CDMA) == 0) && (phoneType |
| == TelephonyManager.PHONE_TYPE_CDMA)) { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /** |
| * Determine from application parameter if mms should be included. |
| * The filter mask is set for message types not selected |
| * @param fi |
| * @param ap |
| * @return boolean true if mms is selected, false if not |
| */ |
| private boolean mmsSelected(BluetoothMapAppParams ap) { |
| int msgType = ap.getFilterMessageType(); |
| |
| if (D) { |
| Log.d(TAG, "mmsSelected msgType: " + msgType); |
| } |
| |
| if (msgType == BluetoothMapAppParams.INVALID_VALUE_PARAMETER) { |
| return true; |
| } |
| |
| if ((msgType & BluetoothMapAppParams.FILTER_NO_MMS) == 0) { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /** |
| * Determine from application parameter if email should be included. |
| * The filter mask is set for message types not selected |
| * @param fi |
| * @param ap |
| * @return boolean true if email is selected, false if not |
| */ |
| private boolean emailSelected(BluetoothMapAppParams ap) { |
| int msgType = ap.getFilterMessageType(); |
| |
| if (D) { |
| Log.d(TAG, "emailSelected msgType: " + msgType); |
| } |
| |
| if (msgType == BluetoothMapAppParams.INVALID_VALUE_PARAMETER) { |
| return true; |
| } |
| |
| if ((msgType & BluetoothMapAppParams.FILTER_NO_EMAIL) == 0) { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /** |
| * Determine from application parameter if IM should be included. |
| * The filter mask is set for message types not selected |
| * @param fi |
| * @param ap |
| * @return boolean true if im is selected, false if not |
| */ |
| private boolean imSelected(BluetoothMapAppParams ap) { |
| int msgType = ap.getFilterMessageType(); |
| |
| if (D) { |
| Log.d(TAG, "imSelected msgType: " + msgType); |
| } |
| |
| if (msgType == BluetoothMapAppParams.INVALID_VALUE_PARAMETER) { |
| return true; |
| } |
| |
| if ((msgType & BluetoothMapAppParams.FILTER_NO_IM) == 0) { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| private void setFilterInfo(FilterInfo fi) { |
| TelephonyManager tm = |
| (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); |
| if (tm != null) { |
| fi.mPhoneType = tm.getPhoneType(); |
| fi.mPhoneNum = tm.getLine1Number(); |
| fi.mPhoneAlphaTag = tm.getLine1AlphaTag(); |
| if (D) { |
| Log.d(TAG, "phone type = " + fi.mPhoneType + " phone num = " + fi.mPhoneNum |
| + " phone alpha tag = " + fi.mPhoneAlphaTag); |
| } |
| } |
| } |
| |
| /** |
| * Get a listing of message in folder after applying filter. |
| * @param folderElement Must contain a valid folder string != null |
| * @param ap Parameters specifying message content and filters |
| * @return Listing object containing requested messages |
| */ |
| public BluetoothMapMessageListing msgListing(BluetoothMapFolderElement folderElement, |
| BluetoothMapAppParams ap) { |
| if (D) { |
| Log.d(TAG, "msgListing: messageType = " + ap.getFilterMessageType()); |
| } |
| |
| BluetoothMapMessageListing bmList = new BluetoothMapMessageListing(); |
| |
| /* We overwrite the parameter mask here if it is 0 or not present, as this |
| * should cause all parameters to be included in the message list. */ |
| if (ap.getParameterMask() == BluetoothMapAppParams.INVALID_VALUE_PARAMETER |
| || ap.getParameterMask() == 0) { |
| ap.setParameterMask(PARAMETER_MASK_ALL_ENABLED); |
| if (V) { |
| Log.v(TAG, "msgListing(): appParameterMask is zero or not present, " |
| + "changing to all enabled by default: " + ap.getParameterMask()); |
| } |
| } |
| if (V) { |
| Log.v(TAG, "folderElement hasSmsMmsContent = " + folderElement.hasSmsMmsContent() |
| + " folderElement.hasEmailContent = " + folderElement.hasEmailContent() |
| + " folderElement.hasImContent = " + folderElement.hasImContent()); |
| } |
| |
| /* Cache some info used throughout filtering */ |
| FilterInfo fi = new FilterInfo(); |
| setFilterInfo(fi); |
| Cursor smsCursor = null; |
| Cursor mmsCursor = null; |
| Cursor emailCursor = null; |
| Cursor imCursor = null; |
| String limit = ""; |
| int countNum = ap.getMaxListCount(); |
| int offsetNum = ap.getStartOffset(); |
| if (ap.getMaxListCount() > 0) { |
| limit = " LIMIT " + (ap.getMaxListCount() + ap.getStartOffset()); |
| } |
| try { |
| if (smsSelected(fi, ap) && folderElement.hasSmsMmsContent()) { |
| if (ap.getFilterMessageType() == (BluetoothMapAppParams.FILTER_NO_EMAIL |
| | BluetoothMapAppParams.FILTER_NO_MMS |
| | BluetoothMapAppParams.FILTER_NO_SMS_GSM |
| | BluetoothMapAppParams.FILTER_NO_IM) || ap.getFilterMessageType() == ( |
| BluetoothMapAppParams.FILTER_NO_EMAIL | BluetoothMapAppParams.FILTER_NO_MMS |
| | BluetoothMapAppParams.FILTER_NO_SMS_CDMA |
| | BluetoothMapAppParams.FILTER_NO_IM)) { |
| //set real limit and offset if only this type is used |
| // (only if offset/limit is used) |
| limit = " LIMIT " + ap.getMaxListCount() + " OFFSET " + ap.getStartOffset(); |
| if (D) { |
| Log.d(TAG, "SMS Limit => " + limit); |
| } |
| offsetNum = 0; |
| } |
| fi.mMsgType = FilterInfo.TYPE_SMS; |
| if (ap.getFilterPriority() != 1) { /*SMS cannot have high priority*/ |
| String where = setWhereFilter(folderElement, fi, ap); |
| if (D) { |
| Log.d(TAG, "msgType: " + fi.mMsgType + " where: " + where); |
| } |
| smsCursor = mResolver.query(Sms.CONTENT_URI, SMS_PROJECTION, where, null, |
| Sms.DATE + " DESC" + limit); |
| if (smsCursor != null) { |
| BluetoothMapMessageListingElement e = null; |
| // store column index so we dont have to look them up anymore (optimization) |
| if (D) { |
| Log.d(TAG, "Found " + smsCursor.getCount() + " sms messages."); |
| } |
| fi.setSmsColumns(smsCursor); |
| while (smsCursor.moveToNext()) { |
| if (matchAddresses(smsCursor, fi, ap)) { |
| if (V) { |
| BluetoothMapUtils.printCursor(smsCursor); |
| } |
| e = element(smsCursor, fi, ap); |
| bmList.add(e); |
| } |
| } |
| } |
| } |
| } |
| |
| if (mmsSelected(ap) && folderElement.hasSmsMmsContent()) { |
| if (ap.getFilterMessageType() == (BluetoothMapAppParams.FILTER_NO_EMAIL |
| | BluetoothMapAppParams.FILTER_NO_SMS_CDMA |
| | BluetoothMapAppParams.FILTER_NO_SMS_GSM |
| | BluetoothMapAppParams.FILTER_NO_IM)) { |
| //set real limit and offset if only this type is used |
| //(only if offset/limit is used) |
| limit = " LIMIT " + ap.getMaxListCount() + " OFFSET " + ap.getStartOffset(); |
| if (D) { |
| Log.d(TAG, "MMS Limit => " + limit); |
| } |
| offsetNum = 0; |
| } |
| fi.mMsgType = FilterInfo.TYPE_MMS; |
| String where = setWhereFilter(folderElement, fi, ap); |
| where += " AND " + INTERESTED_MESSAGE_TYPE_CLAUSE; |
| if (!where.isEmpty()) { |
| if (D) { |
| Log.d(TAG, "msgType: " + fi.mMsgType + " where: " + where); |
| } |
| mmsCursor = mResolver.query(Mms.CONTENT_URI, MMS_PROJECTION, where, null, |
| Mms.DATE + " DESC" + limit); |
| if (mmsCursor != null) { |
| BluetoothMapMessageListingElement e = null; |
| // store column index so we dont have to look them up anymore (optimization) |
| fi.setMmsColumns(mmsCursor); |
| if (D) { |
| Log.d(TAG, "Found " + mmsCursor.getCount() + " mms messages."); |
| } |
| while (mmsCursor.moveToNext()) { |
| if (matchAddresses(mmsCursor, fi, ap)) { |
| if (V) { |
| BluetoothMapUtils.printCursor(mmsCursor); |
| } |
| e = element(mmsCursor, fi, ap); |
| bmList.add(e); |
| } |
| } |
| } |
| } |
| } |
| |
| if (emailSelected(ap) && folderElement.hasEmailContent()) { |
| if (ap.getFilterMessageType() == (BluetoothMapAppParams.FILTER_NO_MMS |
| | BluetoothMapAppParams.FILTER_NO_SMS_CDMA |
| | BluetoothMapAppParams.FILTER_NO_SMS_GSM |
| | BluetoothMapAppParams.FILTER_NO_IM)) { |
| //set real limit and offset if only this type is used |
| //(only if offset/limit is used) |
| limit = " LIMIT " + ap.getMaxListCount() + " OFFSET " + ap.getStartOffset(); |
| if (D) { |
| Log.d(TAG, "Email Limit => " + limit); |
| } |
| offsetNum = 0; |
| } |
| fi.mMsgType = FilterInfo.TYPE_EMAIL; |
| String where = setWhereFilter(folderElement, fi, ap); |
| |
| if (!where.isEmpty()) { |
| if (D) { |
| Log.d(TAG, "msgType: " + fi.mMsgType + " where: " + where); |
| } |
| Uri contentUri = Uri.parse(mBaseUri + BluetoothMapContract.TABLE_MESSAGE); |
| emailCursor = |
| mResolver.query(contentUri, BluetoothMapContract.BT_MESSAGE_PROJECTION, |
| where, null, |
| BluetoothMapContract.MessageColumns.DATE + " DESC" + limit); |
| if (emailCursor != null) { |
| BluetoothMapMessageListingElement e = null; |
| // store column index so we dont have to look them up anymore (optimization) |
| fi.setEmailMessageColumns(emailCursor); |
| int cnt = 0; |
| if (D) { |
| Log.d(TAG, "Found " + emailCursor.getCount() + " email messages."); |
| } |
| while (emailCursor.moveToNext()) { |
| if (V) { |
| BluetoothMapUtils.printCursor(emailCursor); |
| } |
| e = element(emailCursor, fi, ap); |
| bmList.add(e); |
| } |
| // emailCursor.close(); |
| } |
| } |
| } |
| |
| if (imSelected(ap) && folderElement.hasImContent()) { |
| if (ap.getFilterMessageType() == (BluetoothMapAppParams.FILTER_NO_MMS |
| | BluetoothMapAppParams.FILTER_NO_SMS_CDMA |
| | BluetoothMapAppParams.FILTER_NO_SMS_GSM |
| | BluetoothMapAppParams.FILTER_NO_EMAIL)) { |
| //set real limit and offset if only this type is used |
| //(only if offset/limit is used) |
| limit = " LIMIT " + ap.getMaxListCount() + " OFFSET " + ap.getStartOffset(); |
| if (D) { |
| Log.d(TAG, "IM Limit => " + limit); |
| } |
| offsetNum = 0; |
| } |
| fi.mMsgType = FilterInfo.TYPE_IM; |
| String where = setWhereFilter(folderElement, fi, ap); |
| if (D) { |
| Log.d(TAG, "msgType: " + fi.mMsgType + " where: " + where); |
| } |
| |
| Uri contentUri = Uri.parse(mBaseUri + BluetoothMapContract.TABLE_MESSAGE); |
| imCursor = mResolver.query(contentUri, |
| BluetoothMapContract.BT_INSTANT_MESSAGE_PROJECTION, where, null, |
| BluetoothMapContract.MessageColumns.DATE + " DESC" + limit); |
| if (imCursor != null) { |
| BluetoothMapMessageListingElement e = null; |
| // store column index so we dont have to look them up anymore (optimization) |
| fi.setImMessageColumns(imCursor); |
| if (D) { |
| Log.d(TAG, "Found " + imCursor.getCount() + " im messages."); |
| } |
| while (imCursor.moveToNext()) { |
| if (V) { |
| BluetoothMapUtils.printCursor(imCursor); |
| } |
| e = element(imCursor, fi, ap); |
| bmList.add(e); |
| } |
| } |
| } |
| |
| /* Enable this if post sorting and segmenting needed */ |
| bmList.sort(); |
| bmList.segment(ap.getMaxListCount(), offsetNum); |
| List<BluetoothMapMessageListingElement> list = bmList.getList(); |
| int listSize = list.size(); |
| Cursor tmpCursor = null; |
| for (int x = 0; x < listSize; x++) { |
| BluetoothMapMessageListingElement ele = list.get(x); |
| /* If OBEX "GET" request header includes "ParameterMask" with 'Type' NOT set, |
| * then ele.getType() returns "null" even for a valid cursor. |
| * Avoid NullPointerException in equals() check when 'mType' value is "null" */ |
| TYPE tmpType = ele.getType(); |
| if (smsCursor != null && ((TYPE.SMS_GSM).equals(tmpType) || (TYPE.SMS_CDMA).equals( |
| tmpType))) { |
| tmpCursor = smsCursor; |
| fi.mMsgType = FilterInfo.TYPE_SMS; |
| } else if (mmsCursor != null && (TYPE.MMS).equals(tmpType)) { |
| tmpCursor = mmsCursor; |
| fi.mMsgType = FilterInfo.TYPE_MMS; |
| } else if (emailCursor != null && ((TYPE.EMAIL).equals(tmpType))) { |
| tmpCursor = emailCursor; |
| fi.mMsgType = FilterInfo.TYPE_EMAIL; |
| } else if (imCursor != null && ((TYPE.IM).equals(tmpType))) { |
| tmpCursor = imCursor; |
| fi.mMsgType = FilterInfo.TYPE_IM; |
| } |
| if (tmpCursor != null) { |
| tmpCursor.moveToPosition(ele.getCursorIndex()); |
| setSenderAddressing(ele, tmpCursor, fi, ap); |
| setSenderName(ele, tmpCursor, fi, ap); |
| setRecipientAddressing(ele, tmpCursor, fi, ap); |
| setRecipientName(ele, tmpCursor, fi, ap); |
| setSubject(ele, tmpCursor, fi, ap); |
| setSize(ele, tmpCursor, fi, ap); |
| setText(ele, tmpCursor, fi, ap); |
| setPriority(ele, tmpCursor, fi, ap); |
| setSent(ele, tmpCursor, fi, ap); |
| setProtected(ele, tmpCursor, fi, ap); |
| setReceptionStatus(ele, tmpCursor, fi, ap); |
| setAttachment(ele, tmpCursor, fi, ap); |
| |
| if (mMsgListingVersion > BluetoothMapUtils.MAP_MESSAGE_LISTING_FORMAT_V10) { |
| setDeliveryStatus(ele, tmpCursor, fi, ap); |
| setThreadId(ele, tmpCursor, fi, ap); |
| setThreadName(ele, tmpCursor, fi, ap); |
| setFolderType(ele, tmpCursor, fi, ap); |
| } |
| } |
| } |
| } finally { |
| if (emailCursor != null) { |
| emailCursor.close(); |
| } |
| if (smsCursor != null) { |
| smsCursor.close(); |
| } |
| if (mmsCursor != null) { |
| mmsCursor.close(); |
| } |
| if (imCursor != null) { |
| imCursor.close(); |
| } |
| } |
| |
| |
| if (D) { |
| Log.d(TAG, "messagelisting end"); |
| } |
| return bmList; |
| } |
| |
| /** |
| * Get the size of the message listing |
| * @param folderElement Must contain a valid folder string != null |
| * @param ap Parameters specifying message content and filters |
| * @return Integer equal to message listing size |
| */ |
| public int msgListingSize(BluetoothMapFolderElement folderElement, BluetoothMapAppParams ap) { |
| if (D) { |
| Log.d(TAG, "msgListingSize: folder = " + folderElement.getName()); |
| } |
| int cnt = 0; |
| |
| /* Cache some info used throughout filtering */ |
| FilterInfo fi = new FilterInfo(); |
| setFilterInfo(fi); |
| |
| if (smsSelected(fi, ap) && folderElement.hasSmsMmsContent()) { |
| fi.mMsgType = FilterInfo.TYPE_SMS; |
| String where = setWhereFilter(folderElement, fi, ap); |
| Cursor c = mResolver.query(Sms.CONTENT_URI, SMS_PROJECTION, where, null, |
| Sms.DATE + " DESC"); |
| try { |
| if (c != null) { |
| cnt = c.getCount(); |
| } |
| } finally { |
| if (c != null) { |
| c.close(); |
| } |
| } |
| } |
| |
| if (mmsSelected(ap) && folderElement.hasSmsMmsContent()) { |
| fi.mMsgType = FilterInfo.TYPE_MMS; |
| String where = setWhereFilter(folderElement, fi, ap); |
| Cursor c = mResolver.query(Mms.CONTENT_URI, MMS_PROJECTION, where, null, |
| Mms.DATE + " DESC"); |
| try { |
| if (c != null) { |
| cnt += c.getCount(); |
| } |
| } finally { |
| if (c != null) { |
| c.close(); |
| } |
| } |
| } |
| |
| if (emailSelected(ap) && folderElement.hasEmailContent()) { |
| fi.mMsgType = FilterInfo.TYPE_EMAIL; |
| String where = setWhereFilter(folderElement, fi, ap); |
| if (!where.isEmpty()) { |
| Uri contentUri = Uri.parse(mBaseUri + BluetoothMapContract.TABLE_MESSAGE); |
| Cursor c = mResolver.query(contentUri, BluetoothMapContract.BT_MESSAGE_PROJECTION, |
| where, null, BluetoothMapContract.MessageColumns.DATE + " DESC"); |
| try { |
| if (c != null) { |
| cnt += c.getCount(); |
| } |
| } finally { |
| if (c != null) { |
| c.close(); |
| } |
| } |
| } |
| } |
| |
| if (imSelected(ap) && folderElement.hasImContent()) { |
| fi.mMsgType = FilterInfo.TYPE_IM; |
| String where = setWhereFilter(folderElement, fi, ap); |
| if (!where.isEmpty()) { |
| Uri contentUri = Uri.parse(mBaseUri + BluetoothMapContract.TABLE_MESSAGE); |
| Cursor c = mResolver.query(contentUri, |
| BluetoothMapContract.BT_INSTANT_MESSAGE_PROJECTION, where, null, |
| BluetoothMapContract.MessageColumns.DATE + " DESC"); |
| try { |
| if (c != null) { |
| cnt += c.getCount(); |
| } |
| } finally { |
| if (c != null) { |
| c.close(); |
| } |
| } |
| } |
| } |
| |
| if (D) { |
| Log.d(TAG, "msgListingSize: size = " + cnt); |
| } |
| return cnt; |
| } |
| |
| /** |
| * Return true if there are unread messages in the requested list of messages |
| * @param folderElement folder where the message listing should come from |
| * @param ap application parameter object |
| * @return true if unread messages are in the list, else false |
| */ |
| public boolean msgListingHasUnread(BluetoothMapFolderElement folderElement, |
| BluetoothMapAppParams ap) { |
| if (D) { |
| Log.d(TAG, "msgListingHasUnread: folder = " + folderElement.getName()); |
| } |
| int cnt = 0; |
| |
| /* Cache some info used throughout filtering */ |
| FilterInfo fi = new FilterInfo(); |
| setFilterInfo(fi); |
| |
| if (smsSelected(fi, ap) && folderElement.hasSmsMmsContent()) { |
| fi.mMsgType = FilterInfo.TYPE_SMS; |
| String where = setWhereFilterFolderType(folderElement, fi); |
| where += " AND " + Sms.READ + "=0 "; |
| where += setWhereFilterPeriod(ap, fi); |
| Cursor c = mResolver.query(Sms.CONTENT_URI, SMS_PROJECTION, where, null, |
| Sms.DATE + " DESC"); |
| try { |
| if (c != null) { |
| cnt = c.getCount(); |
| } |
| } finally { |
| if (c != null) { |
| c.close(); |
| } |
| } |
| } |
| |
| if (mmsSelected(ap) && folderElement.hasSmsMmsContent()) { |
| fi.mMsgType = FilterInfo.TYPE_MMS; |
| String where = setWhereFilterFolderType(folderElement, fi); |
| where += " AND " + Mms.READ + "=0 "; |
| where += setWhereFilterPeriod(ap, fi); |
| Cursor c = mResolver.query(Mms.CONTENT_URI, MMS_PROJECTION, where, null, |
| Sms.DATE + " DESC"); |
| try { |
| if (c != null) { |
| cnt += c.getCount(); |
| } |
| } finally { |
| if (c != null) { |
| c.close(); |
| } |
| } |
| } |
| |
| |
| if (emailSelected(ap) && folderElement.getFolderId() != -1) { |
| fi.mMsgType = FilterInfo.TYPE_EMAIL; |
| String where = setWhereFilterFolderType(folderElement, fi); |
| if (!where.isEmpty()) { |
| where += " AND " + BluetoothMapContract.MessageColumns.FLAG_READ + "=0 "; |
| where += setWhereFilterPeriod(ap, fi); |
| Uri contentUri = Uri.parse(mBaseUri + BluetoothMapContract.TABLE_MESSAGE); |
| Cursor c = mResolver.query(contentUri, BluetoothMapContract.BT_MESSAGE_PROJECTION, |
| where, null, BluetoothMapContract.MessageColumns.DATE + " DESC"); |
| try { |
| if (c != null) { |
| cnt += c.getCount(); |
| } |
| } finally { |
| if (c != null) { |
| c.close(); |
| } |
| } |
| } |
| } |
| |
| if (imSelected(ap) && folderElement.hasImContent()) { |
| fi.mMsgType = FilterInfo.TYPE_IM; |
| String where = setWhereFilter(folderElement, fi, ap); |
| if (!where.isEmpty()) { |
| where += " AND " + BluetoothMapContract.MessageColumns.FLAG_READ + "=0 "; |
| where += setWhereFilterPeriod(ap, fi); |
| Uri contentUri = Uri.parse(mBaseUri + BluetoothMapContract.TABLE_MESSAGE); |
| Cursor c = mResolver.query(contentUri, |
| BluetoothMapContract.BT_INSTANT_MESSAGE_PROJECTION, where, null, |
| BluetoothMapContract.MessageColumns.DATE + " DESC"); |
| try { |
| if (c != null) { |
| cnt += c.getCount(); |
| } |
| } finally { |
| if (c != null) { |
| c.close(); |
| } |
| } |
| } |
| } |
| |
| if (D) { |
| Log.d(TAG, "msgListingHasUnread: numUnread = " + cnt); |
| } |
| return cnt > 0; |
| } |
| |
| /** |
| * Build the conversation listing. |
| * @param ap The Application Parameters |
| * @param sizeOnly TRUE: don't populate the list members, only build the list to get the size. |
| * @return |
| */ |
| public BluetoothMapConvoListing convoListing(BluetoothMapAppParams ap, boolean sizeOnly) { |
| |
| if (D) { |
| Log.d(TAG, "convoListing: " + " messageType = " + ap.getFilterMessageType()); |
| } |
| BluetoothMapConvoListing convoList = new BluetoothMapConvoListing(); |
| |
| /* We overwrite the parameter mask here if it is 0 or not present, as this |
| * should cause all parameters to be included in the message list. */ |
| if (ap.getConvoParameterMask() == BluetoothMapAppParams.INVALID_VALUE_PARAMETER |
| || ap.getConvoParameterMask() == 0) { |
| ap.setConvoParameterMask(CONVO_PARAMETER_MASK_DEFAULT); |
| if (D) { |
| Log.v(TAG, "convoListing(): appParameterMask is zero or not present, " |
| + "changing to default: " + ap.getConvoParameterMask()); |
| } |
| } |
| |
| /* Possible filters: |
| * - Recipient name (contacts DB) or id (for SMS/MMS this is the thread-id contact-id) |
| * - Activity start/begin |
| * - Read status |
| * - Thread_id |
| * The strategy for SMS/MMS |
| * With no filter on name - use limit and offset. |
| * With a filter on name - build the complete list of conversations and create a filter |
| * mechanism |
| * |
| * The strategy for IM: |
| * Join the conversation table with the contacts table in a way that makes it possible to |
| * get the data needed in a single query. |
| * Manually handle limit/offset |
| * */ |
| |
| /* Cache some info used throughout filtering */ |
| FilterInfo fi = new FilterInfo(); |
| setFilterInfo(fi); |
| Cursor smsMmsCursor = null; |
| Cursor imEmailCursor = null; |
| int offsetNum; |
| if (sizeOnly) { |
| offsetNum = 0; |
| } else { |
| offsetNum = ap.getStartOffset(); |
| } |
| // Inverse meaning - hence a 1 is include. |
| int msgTypesInclude = |
| ((~ap.getFilterMessageType()) & BluetoothMapAppParams.FILTER_MSG_TYPE_MASK); |
| int maxThreads = ap.getMaxListCount() + ap.getStartOffset(); |
| |
| |
| try { |
| if (smsSelected(fi, ap) || mmsSelected(ap)) { |
| String limit = ""; |
| if ((!sizeOnly) && (ap.getMaxListCount() > 0) && (ap.getFilterRecipient() |
| == null)) { |
| /* We can only use limit if we do not have a contacts filter */ |
| limit = " LIMIT " + maxThreads; |
| } |
| StringBuilder sortOrder = new StringBuilder(Threads.DATE + " DESC"); |
| if ((!sizeOnly) && ((msgTypesInclude & ~(BluetoothMapAppParams.FILTER_NO_SMS_GSM |
| | BluetoothMapAppParams.FILTER_NO_SMS_CDMA) |
| | BluetoothMapAppParams.FILTER_NO_MMS) == 0) |
| && ap.getFilterRecipient() == null) { |
| // SMS/MMS messages only and no recipient filter - use optimization. |
| limit = " LIMIT " + ap.getMaxListCount() + " OFFSET " + ap.getStartOffset(); |
| if (D) { |
| Log.d(TAG, "SMS Limit => " + limit); |
| } |
| offsetNum = 0; |
| } |
| StringBuilder selection = new StringBuilder(120); // This covers most cases |
| ArrayList<String> selectionArgs = new ArrayList<String>(12); // Covers all cases |
| selection.append("1=1 "); // just to simplify building the where-clause |
| setConvoWhereFilterSmsMms(selection, selectionArgs, fi, ap); |
| String[] args = null; |
| if (selectionArgs.size() > 0) { |
| args = new String[selectionArgs.size()]; |
| selectionArgs.toArray(args); |
| } |
| Uri uri = Threads.CONTENT_URI.buildUpon() |
| .appendQueryParameter("simple", "true") |
| .build(); |
| sortOrder.append(limit); |
| if (D) { |
| Log.d(TAG, "Query using selection: " + selection.toString() + " - sortOrder: " |
| + sortOrder.toString()); |
| } |
| // TODO: Optimize: Reduce projection based on convo parameter mask |
| smsMmsCursor = |
| mResolver.query(uri, MMS_SMS_THREAD_PROJECTION, selection.toString(), args, |
| sortOrder.toString()); |
| if (smsMmsCursor != null) { |
| // store column index so we don't have to look them up anymore (optimization) |
| if (D) { |
| Log.d(TAG, "Found " + smsMmsCursor.getCount() + " sms/mms conversations."); |
| } |
| BluetoothMapConvoListingElement convoElement = null; |
| smsMmsCursor.moveToPosition(-1); |
| if (ap.getFilterRecipient() == null) { |
| int count = 0; |
| // We have no Recipient filter, add contacts after the list is reduced |
| while (smsMmsCursor.moveToNext()) { |
| convoElement = createConvoElement(smsMmsCursor, fi, ap); |
| convoList.add(convoElement); |
| count++; |
| if (!sizeOnly && count >= maxThreads) { |
| break; |
| } |
| } |
| } else { |
| // We must be able to filter on recipient, add contacts now |
| SmsMmsContacts contacts = new SmsMmsContacts(); |
| while (smsMmsCursor.moveToNext()) { |
| int count = 0; |
| convoElement = createConvoElement(smsMmsCursor, fi, ap); |
| String idsStr = |
| smsMmsCursor.getString(MMS_SMS_THREAD_COL_RECIPIENT_IDS); |
| // Add elements only if we do find a contact - if not we cannot apply |
| // the filter, hence the item is irrelevant |
| // TODO: Perhaps the spec. should be changes to be able to search on |
| // phone number as well? |
| if (addSmsMmsContacts(convoElement, contacts, idsStr, |
| ap.getFilterRecipient(), ap)) { |
| convoList.add(convoElement); |
| if (!sizeOnly && count >= maxThreads) { |
| break; |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| if (emailSelected(ap) || imSelected(ap)) { |
| int count = 0; |
| if (emailSelected(ap)) { |
| fi.mMsgType = FilterInfo.TYPE_EMAIL; |
| } else if (imSelected(ap)) { |
| fi.mMsgType = FilterInfo.TYPE_IM; |
| } |
| if (D) { |
| Log.d(TAG, "msgType: " + fi.mMsgType); |
| } |
| Uri contentUri = Uri.parse(mBaseUri + BluetoothMapContract.TABLE_CONVERSATION); |
| |
| contentUri = appendConvoListQueryParameters(ap, contentUri); |
| if (V) { |
| Log.v(TAG, "URI with parameters: " + contentUri.toString()); |
| } |
| // TODO: Optimize: Reduce projection based on convo parameter mask |
| imEmailCursor = |
| mResolver.query(contentUri, BluetoothMapContract.BT_CONVERSATION_PROJECTION, |
| null, null, |
| BluetoothMapContract.ConversationColumns.LAST_THREAD_ACTIVITY |
| + " DESC, " |
| + BluetoothMapContract.ConversationColumns.THREAD_ID |
| + " ASC"); |
| if (imEmailCursor != null) { |
| BluetoothMapConvoListingElement e = null; |
| // store column index so we don't have to look them up anymore (optimization) |
| // Here we rely on only a single account-based message type for each MAS. |
| fi.setEmailImConvoColumns(imEmailCursor); |
| boolean isValid = imEmailCursor.moveToNext(); |
| if (D) { |
| Log.d(TAG, "Found " + imEmailCursor.getCount() |
| + " EMAIL/IM conversations. isValid = " + isValid); |
| } |
| while (isValid && ((sizeOnly) || (count < maxThreads))) { |
| long threadId = imEmailCursor.getLong(fi.mConvoColConvoId); |
| long nextThreadId; |
| count++; |
| e = createConvoElement(imEmailCursor, fi, ap); |
| convoList.add(e); |
| |
| do { |
| nextThreadId = imEmailCursor.getLong(fi.mConvoColConvoId); |
| if (V) { |
| Log.i(TAG, " threadId = " + threadId + " newThreadId = " |
| + nextThreadId); |
| } |
| // TODO: This seems rather inefficient in the case where we do not need |
| // to reduce the list. |
| } while ((nextThreadId == threadId) && (isValid = |
| imEmailCursor.moveToNext())); |
| } |
| } |
| } |
| |
| if (D) { |
| Log.d(TAG, "Done adding conversations - list size:" + convoList.getCount()); |
| } |
| |
| // If sizeOnly - we are all done here - return the list as is - no need to populate the |
| // list. |
| if (sizeOnly) { |
| return convoList; |
| } |
| |
| /* Enable this if post sorting and segmenting needed */ |
| /* This is too early */ |
| convoList.sort(); |
| convoList.segment(ap.getMaxListCount(), offsetNum); |
| List<BluetoothMapConvoListingElement> list = convoList.getList(); |
| int listSize = list.size(); |
| if (V) { |
| Log.i(TAG, "List Size:" + listSize); |
| } |
| Cursor tmpCursor = null; |
| SmsMmsContacts contacts = new SmsMmsContacts(); |
| for (int x = 0; x < listSize; x++) { |
| BluetoothMapConvoListingElement ele = list.get(x); |
| TYPE type = ele.getType(); |
| switch (type) { |
| case SMS_CDMA: |
| case SMS_GSM: |
| case MMS: { |
| tmpCursor = null; // SMS/MMS needs special treatment |
| if (smsMmsCursor != null) { |
| populateSmsMmsConvoElement(ele, smsMmsCursor, ap, contacts); |
| } |
| if (D) { |
| fi.mMsgType = FilterInfo.TYPE_IM; |
| } |
| break; |
| } |
| case EMAIL: |
| tmpCursor = imEmailCursor; |
| fi.mMsgType = FilterInfo.TYPE_EMAIL; |
| break; |
| case IM: |
| tmpCursor = imEmailCursor; |
| fi.mMsgType = FilterInfo.TYPE_IM; |
| break; |
| default: |
| tmpCursor = null; |
| break; |
| } |
| |
| if (D) { |
| Log.d(TAG, "Working on cursor of type " + fi.mMsgType); |
| } |
| |
| if (tmpCursor != null) { |
| populateImEmailConvoElement(ele, tmpCursor, ap, fi); |
| } else { |
| // No, it will be for SMS/MMS at the moment |
| if (D) { |
| Log.d(TAG, "tmpCursor is Null - something is wrong - or the message is" |
| + " of type SMS/MMS"); |
| } |
| } |
| } |
| } finally { |
| if (imEmailCursor != null) { |
| imEmailCursor.close(); |
| } |
| if (smsMmsCursor != null) { |
| smsMmsCursor.close(); |
| } |
| if (D) { |
| Log.d(TAG, "conversation end"); |
| } |
| } |
| return convoList; |
| } |
| |
| |
| /** |
| * Refreshes the entire list of SMS/MMS conversation version counters. Use it to generate a |
| * new ConvoListVersinoCounter in mSmsMmsConvoListVersion |
| * @return |
| */ |
| /* package */ |
| boolean refreshSmsMmsConvoVersions() { |
| boolean listChangeDetected = false; |
| Cursor cursor = null; |
| Uri uri = Threads.CONTENT_URI.buildUpon().appendQueryParameter("simple", "true").build(); |
| cursor = |
| mResolver.query(uri, MMS_SMS_THREAD_PROJECTION, null, null, Threads.DATE + " DESC"); |
| try { |
| if (cursor != null) { |
| // store column index so we don't have to look them up anymore (optimization) |
| if (D) { |
| Log.d(TAG, "Found " + cursor.getCount() + " sms/mms conversations."); |
| } |
| BluetoothMapConvoListingElement convoElement = null; |
| cursor.moveToPosition(-1); |
| synchronized (getSmsMmsConvoList()) { |
| int size = Math.max(getSmsMmsConvoList().size(), cursor.getCount()); |
| HashMap<Long, BluetoothMapConvoListingElement> newList = |
| new HashMap<Long, BluetoothMapConvoListingElement>(size); |
| while (cursor.moveToNext()) { |
| // TODO: Extract to function, that can be called at listing, which returns |
| // the versionCounter(existing or new). |
| boolean convoChanged = false; |
| Long id = cursor.getLong(MMS_SMS_THREAD_COL_ID); |
| convoElement = getSmsMmsConvoList().remove(id); |
| if (convoElement == null) { |
| // New conversation added |
| convoElement = new BluetoothMapConvoListingElement(); |
| convoElement.setConvoId(BluetoothMapUtils.CONVO_ID_TYPE_SMS_MMS, id); |
| listChangeDetected = true; |
| convoElement.setVersionCounter(0); |
| } |
| // Currently we only need to compare name, lastActivity and read_status, and |
| // name is not used for SMS/MMS. |
| // msg delete will be handled by update folderVersionCounter(). |
| long lastActivity = cursor.getLong(MMS_SMS_THREAD_COL_DATE); |
| boolean read = cursor.getInt(MMS_SMS_THREAD_COL_READ) == 1; |
| |
| if (lastActivity != convoElement.getLastActivity()) { |
| convoChanged = true; |
| convoElement.setLastActivity(lastActivity); |
| } |
| |
| if (read != convoElement.getReadBool()) { |
| convoChanged = true; |
| convoElement.setRead(read, false); |
| } |
| |
| String idsStr = cursor.getString(MMS_SMS_THREAD_COL_RECIPIENT_IDS); |
| if (!idsStr.equals(convoElement.getSmsMmsContacts())) { |
| // This should not trigger a change in conversationVersionCounter |
| // only the |
| // ConvoListVersionCounter. |
| listChangeDetected = true; |
| convoElement.setSmsMmsContacts(idsStr); |
| } |
| |
| if (convoChanged) { |
| listChangeDetected = true; |
| convoElement.incrementVersionCounter(); |
| } |
| newList.put(id, convoElement); |
| } |
| // If we still have items on the old list, something was deleted |
| if (getSmsMmsConvoList().size() != 0) { |
| listChangeDetected = true; |
| } |
| setSmsMmsConvoList(newList); |
| } |
| |
| if (listChangeDetected) { |
| mMasInstance.updateSmsMmsConvoListVersionCounter(); |
| } |
| } |
| } finally { |
| if (cursor != null) { |
| cursor.close(); |
| } |
| } |
| return listChangeDetected; |
| } |
| |
| /** |
| * Refreshes the entire list of SMS/MMS conversation version counters. Use it to generate a |
| * new ConvoListVersinoCounter in mSmsMmsConvoListVersion |
| * @return |
| */ |
| /* package */ |
| boolean refreshImEmailConvoVersions() { |
| boolean listChangeDetected = false; |
| FilterInfo fi = new FilterInfo(); |
| |
| Uri contentUri = Uri.parse(mBaseUri + BluetoothMapContract.TABLE_CONVERSATION); |
| |
| if (V) { |
| Log.v(TAG, "URI with parameters: " + contentUri.toString()); |
| } |
| Cursor imEmailCursor = mResolver.query(contentUri, CONVO_VERSION_PROJECTION, null, null, |
| BluetoothMapContract.ConversationColumns.LAST_THREAD_ACTIVITY + " DESC, " |
| + BluetoothMapContract.ConversationColumns.THREAD_ID + " ASC"); |
| try { |
| if (imEmailCursor != null) { |
| BluetoothMapConvoListingElement convoElement = null; |
| // store column index so we don't have to look them up anymore (optimization) |
| // Here we rely on only a single account-based message type for each MAS. |
| fi.setEmailImConvoColumns(imEmailCursor); |
| boolean isValid = imEmailCursor.moveToNext(); |
| if (V) { |
| Log.d(TAG, "Found " + imEmailCursor.getCount() |
| + " EMAIL/IM conversations. isValid = " + isValid); |
| } |
| synchronized (getImEmailConvoList()) { |
| int size = Math.max(getImEmailConvoList().size(), imEmailCursor.getCount()); |
| boolean convoChanged = false; |
| HashMap<Long, BluetoothMapConvoListingElement> newList = |
| new HashMap<Long, BluetoothMapConvoListingElement>(size); |
| while (isValid) { |
| long id = imEmailCursor.getLong(fi.mConvoColConvoId); |
| long nextThreadId; |
| convoElement = getImEmailConvoList().remove(id); |
| if (convoElement == null) { |
| // New conversation added |
| convoElement = new BluetoothMapConvoListingElement(); |
| convoElement.setConvoId(BluetoothMapUtils.CONVO_ID_TYPE_EMAIL_IM, id); |
| listChangeDetected = true; |
| convoElement.setVersionCounter(0); |
| } |
| String name = imEmailCursor.getString(fi.mConvoColName); |
| String summary = imEmailCursor.getString(fi.mConvoColSummary); |
| long lastActivity = imEmailCursor.getLong(fi.mConvoColLastActivity); |
| boolean read = imEmailCursor.getInt(fi.mConvoColRead) == 1; |
| |
| if (lastActivity != convoElement.getLastActivity()) { |
| convoChanged = true; |
| convoElement.setLastActivity(lastActivity); |
| } |
| |
| if (read != convoElement.getReadBool()) { |
| convoChanged = true; |
| convoElement.setRead(read, false); |
| } |
| |
| if (name != null && !name.equals(convoElement.getName())) { |
| convoChanged = true; |
| convoElement.setName(name); |
| } |
| |
| if (summary != null && !summary.equals(convoElement.getFullSummary())) { |
| convoChanged = true; |
| convoElement.setSummary(summary); |
| } |
| /* If the query returned one row for each contact, skip all the |
| dublicates */ |
| do { |
| nextThreadId = imEmailCursor.getLong(fi.mConvoColConvoId); |
| if (V) { |
| Log.i(TAG, " threadId = " + id + " newThreadId = " + nextThreadId); |
| } |
| } while ((nextThreadId == id) && (isValid = imEmailCursor.moveToNext())); |
| |
| if (convoChanged) { |
| listChangeDetected = true; |
| convoElement.incrementVersionCounter(); |
| } |
| newList.put(id, convoElement); |
| } |
| // If we still have items on the old list, something was deleted |
| if (getImEmailConvoList().size() != 0) { |
| listChangeDetected = true; |
| } |
| setImEmailConvoList(newList); |
| } |
| } |
| } finally { |
| if (imEmailCursor != null) { |
| imEmailCursor.close(); |
| } |
| } |
| |
| if (listChangeDetected) { |
| mMasInstance.updateImEmailConvoListVersionCounter(); |
| } |
| return listChangeDetected; |
| } |
| |
| /** |
| * Update the convoVersionCounter within the element passed as parameter. |
| * This function has the side effect to update the ConvoListVersionCounter if needed. |
| * This function ignores changes to contacts as this shall not change the convoVersionCounter, |
| * only the convoListVersion counter, which will be updated upon request. |
| * @param ele Element to update shall not be null. |
| */ |
| private void updateSmsMmsConvoVersion(Cursor cursor, BluetoothMapConvoListingElement ele) { |
| long id = ele.getCpConvoId(); |
| BluetoothMapConvoListingElement convoElement = getSmsMmsConvoList().get(id); |
| boolean listChangeDetected = false; |
| boolean convoChanged = false; |
| if (convoElement == null) { |
| // New conversation added |
| convoElement = new BluetoothMapConvoListingElement(); |
| getSmsMmsConvoList().put(id, convoElement); |
| convoElement.setConvoId(BluetoothMapUtils.CONVO_ID_TYPE_SMS_MMS, id); |
| listChangeDetected = true; |
| convoElement.setVersionCounter(0); |
| } |
| long lastActivity = cursor.getLong(MMS_SMS_THREAD_COL_DATE); |
| boolean read = cursor.getInt(MMS_SMS_THREAD_COL_READ) == 1; |
| |
| if (lastActivity != convoElement.getLastActivity()) { |
| convoChanged = true; |
| convoElement.setLastActivity(lastActivity); |
| } |
| |
| if (read != convoElement.getReadBool()) { |
| convoChanged = true; |
| convoElement.setRead(read, false); |
| } |
| |
| if (convoChanged) { |
| listChangeDetected = true; |
| convoElement.incrementVersionCounter(); |
| } |
| if (listChangeDetected) { |
| mMasInstance.updateSmsMmsConvoListVersionCounter(); |
| } |
| ele.setVersionCounter(convoElement.getVersionCounter()); |
| } |
| |
| /** |
| * Update the convoVersionCounter within the element passed as parameter. |
| * This function has the side effect to update the ConvoListVersionCounter if needed. |
| * This function ignores changes to contacts as this shall not change the convoVersionCounter, |
| * only the convoListVersion counter, which will be updated upon request. |
| * @param ele Element to update shall not be null. |
| */ |
| private void updateImEmailConvoVersion(Cursor cursor, FilterInfo fi, |
| BluetoothMapConvoListingElement ele) { |
| long id = ele.getCpConvoId(); |
| BluetoothMapConvoListingElement convoElement = getImEmailConvoList().get(id); |
| boolean listChangeDetected = false; |
| boolean convoChanged = false; |
| if (convoElement == null) { |
| // New conversation added |
| if (V) { |
| Log.d(TAG, "Added new conversation with ID = " + id); |
| } |
| convoElement = new BluetoothMapConvoListingElement(); |
| convoElement.setConvoId(BluetoothMapUtils.CONVO_ID_TYPE_EMAIL_IM, id); |
| getImEmailConvoList().put(id, convoElement); |
| listChangeDetected = true; |
| convoElement.setVersionCounter(0); |
| } |
| String name = cursor.getString(fi.mConvoColName); |
| long lastActivity = cursor.getLong(fi.mConvoColLastActivity); |
| boolean read = cursor.getInt(fi.mConvoColRead) == 1; |
| |
| if (lastActivity != convoElement.getLastActivity()) { |
| convoChanged = true; |
| convoElement.setLastActivity(lastActivity); |
| } |
| |
| if (read != convoElement.getReadBool()) { |
| convoChanged = true; |
| convoElement.setRead(read, false); |
| } |
| |
| if (name != null && !name.equals(convoElement.getName())) { |
| convoChanged = true; |
| convoElement.setName(name); |
| } |
| |
| if (convoChanged) { |
| listChangeDetected = true; |
| if (V) { |
| Log.d(TAG, "conversation with ID = " + id + " changed"); |
| } |
| convoElement.incrementVersionCounter(); |
| } |
| if (listChangeDetected) { |
| mMasInstance.updateImEmailConvoListVersionCounter(); |
| } |
| ele.setVersionCounter(convoElement.getVersionCounter()); |
| } |
| |
| /** |
| * @param ele |
| * @param smsMmsCursor |
| * @param ap |
| * @param contacts |
| */ |
| private void populateSmsMmsConvoElement(BluetoothMapConvoListingElement ele, |
| Cursor smsMmsCursor, BluetoothMapAppParams ap, SmsMmsContacts contacts) { |
| smsMmsCursor.moveToPosition(ele.getCursorIndex()); |
| // TODO: If we ever get beyond 31 bit, change to long |
| int parameterMask = (int) ap.getConvoParameterMask(); // We always set a default value |
| |
| // TODO: How to determine whether the convo-IDs can be used across message |
| // types? |
| ele.setConvoId(BluetoothMapUtils.CONVO_ID_TYPE_SMS_MMS, |
| smsMmsCursor.getLong(MMS_SMS_THREAD_COL_ID)); |
| |
| boolean read = smsMmsCursor.getInt(MMS_SMS_THREAD_COL_READ) == 1; |
| if ((parameterMask & CONVO_PARAM_MASK_CONVO_READ_STATUS) != 0) { |
| ele.setRead(read, true); |
| } else { |
| ele.setRead(read, false); |
| } |
| |
| if ((parameterMask & CONVO_PARAM_MASK_CONVO_LAST_ACTIVITY) != 0) { |
| long timeStamp = smsMmsCursor.getLong(MMS_SMS_THREAD_COL_DATE); |
| ele.setLastActivity(timeStamp); |
| } else { |
| // We need to delete the time stamp, if it was added for multi msg-type |
| ele.setLastActivity(-1); |
| } |
| |
| if ((parameterMask & CONVO_PARAM_MASK_CONVO_VERSION_COUNTER) != 0) { |
| updateSmsMmsConvoVersion(smsMmsCursor, ele); |
| } |
| |
| if ((parameterMask & CONVO_PARAM_MASK_CONVO_NAME) != 0) { |
| ele.setName(""); // We never have a thread name for SMS/MMS |
| } |
| |
| if ((parameterMask & CONVO_PARAM_MASK_CONVO_SUMMARY) != 0) { |
| String summary = smsMmsCursor.getString(MMS_SMS_THREAD_COL_SNIPPET); |
| String cs = smsMmsCursor.getString(MMS_SMS_THREAD_COL_SNIPPET_CS); |
| if (summary != null && cs != null && !cs.equals("UTF-8")) { |
| try { |
| // TODO: Not sure this is how to convert to UTF-8 |
| summary = new String(summary.getBytes(cs), "UTF-8"); |
| } catch (UnsupportedEncodingException e) { |
| Log.e(TAG, "populateSmsMmsConvoElement: " + e); |
| } |
| } |
| ele.setSummary(summary); |
| } |
| |
| if ((parameterMask & CONVO_PARAM_MASK_PARTTICIPANTS) != 0) { |
| if (ap.getFilterRecipient() == null) { |
| // Add contacts only if not already added |
| String idsStr = smsMmsCursor.getString(MMS_SMS_THREAD_COL_RECIPIENT_IDS); |
| addSmsMmsContacts(ele, contacts, idsStr, null, ap); |
| } |
| } |
| } |
| |
| /** |
| * @param ele |
| * @param tmpCursor |
| * @param fi |
| */ |
| private void populateImEmailConvoElement(BluetoothMapConvoListingElement ele, Cursor tmpCursor, |
| BluetoothMapAppParams ap, FilterInfo fi) { |
| tmpCursor.moveToPosition(ele.getCursorIndex()); |
| // TODO: If we ever get beyond 31 bit, change to long |
| int parameterMask = (int) ap.getConvoParameterMask(); // We always set a default value |
| long threadId = tmpCursor.getLong(fi.mConvoColConvoId); |
| |
| // Mandatory field |
| ele.setConvoId(BluetoothMapUtils.CONVO_ID_TYPE_EMAIL_IM, threadId); |
| |
| if ((parameterMask & CONVO_PARAM_MASK_CONVO_NAME) != 0) { |
| ele.setName(tmpCursor.getString(fi.mConvoColName)); |
| } |
| |
| boolean reportRead = false; |
| if ((parameterMask & CONVO_PARAM_MASK_CONVO_READ_STATUS) != 0) { |
| reportRead = true; |
| } |
| ele.setRead((1 == tmpCursor.getInt(fi.mConvoColRead)), reportRead); |
| |
| long timestamp = tmpCursor.getLong(fi.mConvoColLastActivity); |
| if ((parameterMask & CONVO_PARAM_MASK_CONVO_LAST_ACTIVITY) != 0) { |
| ele.setLastActivity(timestamp); |
| } else { |
| // We need to delete the time stamp, if it was added for multi msg-type |
| ele.setLastActivity(-1); |
| } |
| |
| |
| if ((parameterMask & CONVO_PARAM_MASK_CONVO_VERSION_COUNTER) != 0) { |
| updateImEmailConvoVersion(tmpCursor, fi, ele); |
| } |
| if ((parameterMask & CONVO_PARAM_MASK_CONVO_SUMMARY) != 0) { |
| ele.setSummary(tmpCursor.getString(fi.mConvoColSummary)); |
| } |
| // TODO: For optimization, we could avoid joining the contact and convo tables |
| // if we have no filter nor this bit is set. |
| if ((parameterMask & CONVO_PARAM_MASK_PARTTICIPANTS) != 0) { |
| do { |
| BluetoothMapConvoContactElement c = new BluetoothMapConvoContactElement(); |
| if ((parameterMask & CONVO_PARAM_MASK_PART_X_BT_UID) != 0) { |
| c.setBtUid(new SignedLongLong(tmpCursor.getLong(fi.mContactColBtUid), 0)); |
| } |
| if ((parameterMask & CONVO_PARAM_MASK_PART_CHAT_STATE) != 0) { |
| c.setChatState(tmpCursor.getInt(fi.mContactColChatState)); |
| } |
| if ((parameterMask & CONVO_PARAM_MASK_PART_PRESENCE) != 0) { |
| c.setPresenceAvailability(tmpCursor.getInt(fi.mContactColPresenceState)); |
| } |
| if ((parameterMask & CONVO_PARAM_MASK_PART_PRESENCE_TEXT) != 0) { |
| c.setPresenceStatus(tmpCursor.getString(fi.mContactColPresenceText)); |
| } |
| if ((parameterMask & CONVO_PARAM_MASK_PART_PRIORITY) != 0) { |
| c.setPriority(tmpCursor.getInt(fi.mContactColPriority)); |
| } |
| if ((parameterMask & CONVO_PARAM_MASK_PART_DISP_NAME) != 0) { |
| c.setDisplayName(tmpCursor.getString(fi.mContactColNickname)); |
| } |
| if ((parameterMask & CONVO_PARAM_MASK_PART_UCI) != 0) { |
| c.setContactId(tmpCursor.getString(fi.mContactColContactUci)); |
| } |
| if ((parameterMask & CONVO_PARAM_MASK_PART_LAST_ACTIVITY) != 0) { |
| c.setLastActivity(tmpCursor.getLong(fi.mContactColLastActive)); |
| } |
| if ((parameterMask & CONVO_PARAM_MASK_PART_NAME) != 0) { |
| c.setName(tmpCursor.getString(fi.mContactColName)); |
| } |
| ele.addContact(c); |
| } while (tmpCursor.moveToNext() && tmpCursor.getLong(fi.mConvoColConvoId) == threadId); |
| } |
| } |
| |
| /** |
| * Extract the ConvoList parameters from appParams and build the matching URI with |
| * query parameters. |
| * @param ap the appParams from the request |
| * @param contentUri the URI to append parameters to |
| * @return the new URI with the appended parameters (if any) |
| */ |
| private Uri appendConvoListQueryParameters(BluetoothMapAppParams ap, Uri contentUri) { |
| Builder newUri = contentUri.buildUpon(); |
| String str = ap.getFilterRecipient(); |
| if (str != null) { |
| str = str.trim(); |
| str = str.replace("*", "%"); |
| newUri.appendQueryParameter(BluetoothMapContract.FILTER_ORIGINATOR_SUBSTRING, str); |
| } |
| long time = ap.getFilterLastActivityBegin(); |
| if (time > 0) { |
| newUri.appendQueryParameter(BluetoothMapContract.FILTER_PERIOD_BEGIN, |
| Long.toString(time)); |
| } |
| time = ap.getFilterLastActivityEnd(); |
| if (time > 0) { |
| newUri.appendQueryParameter(BluetoothMapContract.FILTER_PERIOD_END, |
| Long.toString(time)); |
| } |
| int readStatus = ap.getFilterReadStatus(); |
| if (readStatus > 0) { |
| if (readStatus == 1) { |
| // Conversations with Unread messages only |
| newUri.appendQueryParameter(BluetoothMapContract.FILTER_READ_STATUS, "false"); |
| } else if (readStatus == 2) { |
| // Conversations with all read messages only |
| newUri.appendQueryParameter(BluetoothMapContract.FILTER_READ_STATUS, "true"); |
| } |
| // if both are set it will be the same as requesting an empty list, but |
| // as it makes no sense with such a structure in a bit mask, we treat |
| // requesting both the same as no filtering. |
| } |
| long convoId = -1; |
| if (ap.getFilterConvoId() != null) { |
| convoId = ap.getFilterConvoId().getLeastSignificantBits(); |
| } |
| if (convoId > 0) { |
| newUri.appendQueryParameter(BluetoothMapContract.FILTER_THREAD_ID, |
| Long.toString(convoId)); |
| } |
| return newUri.build(); |
| } |
| |
| /** |
| * Procedure if we have a filter: |
| * - loop through all ids to examine if there is a match (this will build the cache) |
| * - If there is a match loop again to add all contacts. |
| * |
| * Procedure if we don't have a filter |
| * - Add all contacts |
| * |
| * @param convoElement |
| * @param contacts |
| * @param idsStr |
| * @param recipientFilter |
| * @return |
| */ |
| private boolean addSmsMmsContacts(BluetoothMapConvoListingElement convoElement, |
| SmsMmsContacts contacts, String idsStr, String recipientFilter, |
| BluetoothMapAppParams ap) { |
| BluetoothMapConvoContactElement contactElement; |
| int parameterMask = (int) ap.getConvoParameterMask(); // We always set a default value |
| boolean foundContact = false; |
| String[] ids = idsStr.split(" "); |
| long[] longIds = new long[ids.length]; |
| if (recipientFilter != null) { |
| recipientFilter = recipientFilter.trim(); |
| } |
| |
| for (int i = 0; i < ids.length; i++) { |
| long longId; |
| try { |
| longId = Long.parseLong(ids[i]); |
| longIds[i] = longId; |
| if (recipientFilter == null) { |
| // If there is not filter, all we need to do is to parse the ids |
| foundContact = true; |
| continue; |
| } |
| String addr = contacts.getPhoneNumber(mResolver, longId); |
| if (addr == null) { |
| // This can only happen if all messages from a contact is deleted while |
| // performing the query. |
| continue; |
| } |
| MapContact contact = |
| contacts.getContactNameFromPhone(addr, mResolver, recipientFilter); |
| if (D) { |
| Log.d(TAG, " id " + longId + ": " + addr); |
| if (contact != null) { |
| Log.d(TAG, " contact name: " + contact.getName() + " X-BT-UID: " + contact |
| .getXBtUid()); |
| } |
| } |
| if (contact == null) { |
| continue; |
| } |
| foundContact = true; |
| } catch (NumberFormatException ex) { |
| // skip this id |
| continue; |
| } |
| } |
| |
| if (foundContact) { |
| foundContact = false; |
| for (long id : longIds) { |
| String addr = contacts.getPhoneNumber(mResolver, id); |
| if (addr == null) { |
| // This can only happen if all messages from a contact is deleted while |
| // performing the query. |
| continue; |
| } |
| foundContact = true; |
| MapContact contact = contacts.getContactNameFromPhone(addr, mResolver); |
| |
| if (contact == null) { |
| // We do not have a contact, we need to manually add one |
| contactElement = new BluetoothMapConvoContactElement(); |
| if ((parameterMask & CONVO_PARAM_MASK_PART_NAME) != 0) { |
| contactElement.setName(addr); // Use the phone number as name |
| } |
| if ((parameterMask & CONVO_PARAM_MASK_PART_UCI) != 0) { |
| contactElement.setContactId(addr); |
| } |
| } else { |
| contactElement = |
| BluetoothMapConvoContactElement.createFromMapContact(contact, addr); |
| // Remove the parameters not to be reported |
| if ((parameterMask & CONVO_PARAM_MASK_PART_UCI) == 0) { |
| contactElement.setContactId(null); |
| } |
| if ((parameterMask & CONVO_PARAM_MASK_PART_X_BT_UID) == 0) { |
| contactElement.setBtUid(null); |
| } |
| if ((parameterMask & CONVO_PARAM_MASK_PART_DISP_NAME) == 0) { |
| contactElement.setDisplayName(null); |
| } |
| } |
| convoElement.addContact(contactElement); |
| } |
| } |
| return foundContact; |
| } |
| |
| /** |
| * Get the folder name of an SMS message or MMS message. |
| * @param c the cursor pointing at the message |
| * @return the folder name. |
| */ |
| private String getFolderName(int type, int threadId) { |
| |
| if (threadId == -1) { |
| return BluetoothMapContract.FOLDER_NAME_DELETED; |
| } |
| |
| switch (type) { |
| case 1: |
| return BluetoothMapContract.FOLDER_NAME_INBOX; |
| case 2: |
| return BluetoothMapContract.FOLDER_NAME_SENT; |
| case 3: |
| return BluetoothMapContract.FOLDER_NAME_DRAFT; |
| case 4: // Just name outbox, failed and queued "outbox" |
| case 5: |
| case 6: |
| return BluetoothMapContract.FOLDER_NAME_OUTBOX; |
| } |
| return ""; |
| } |
| |
| public byte[] getMessage(String handle, BluetoothMapAppParams appParams, |
| BluetoothMapFolderElement folderElement, String version) |
| throws UnsupportedEncodingException { |
| TYPE type = BluetoothMapUtils.getMsgTypeFromHandle(handle); |
| mMessageVersion = version; |
| long id = BluetoothMapUtils.getCpHandle(handle); |
| if (appParams.getFractionRequest() == BluetoothMapAppParams.FRACTION_REQUEST_NEXT) { |
| throw new IllegalArgumentException("FRACTION_REQUEST_NEXT does not make sence as" |
| + " we always return the full message."); |
| } |
| switch (type) { |
| case SMS_GSM: |
| case SMS_CDMA: |
| return getSmsMessage(id, appParams.getCharset()); |
| case MMS: |
| return getMmsMessage(id, appParams); |
| case EMAIL: |
| return getEmailMessage(id, appParams, folderElement); |
| case IM: |
| return getIMMessage(id, appParams, folderElement); |
| } |
| throw new IllegalArgumentException("Invalid message handle."); |
| } |
| |
| private String setVCardFromPhoneNumber(BluetoothMapbMessage message, String phone, |
| boolean incoming) { |
| String contactId = null, contactName = null; |
| String[] phoneNumbers = new String[1]; |
| //Handle possible exception for empty phone address |
| if (TextUtils.isEmpty(phone)) { |
| return contactName; |
| } |
| // |
| // Use only actual phone number, because the MCE cannot know which |
| // number the message is from. |
| // |
| phoneNumbers[0] = phone; |
| String[] emailAddresses = null; |
| Cursor p; |
| |
| Uri uri = |
| Uri.withAppendedPath(PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI, Uri.encode(phone)); |
| |
| String[] projection = {Contacts._ID, Contacts.DISPLAY_NAME}; |
| String selection = Contacts.IN_VISIBLE_GROUP + "=1"; |
| String orderBy = Contacts._ID + " ASC"; |
| |
| // Get the contact _ID and name |
| p = mResolver.query(uri, projection, selection, null, orderBy); |
| try { |
| if (p != null && p.moveToFirst()) { |
| contactId = p.getString(p.getColumnIndex(Contacts._ID)); |
| contactName = p.getString(p.getColumnIndex(Contacts.DISPLAY_NAME)); |
| } |
| } finally { |
| close(p); |
| } |
| // Bail out if we are unable to find a contact, based on the phone number |
| if (contactId != null) { |
| Cursor q = null; |
| // Fetch the contact e-mail addresses |
| try { |
| q = mResolver.query(ContactsContract.CommonDataKinds.Email.CONTENT_URI, null, |
| ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?", |
| new String[]{contactId}, null); |
| if (q != null && q.moveToFirst()) { |
| int i = 0; |
| emailAddresses = new String[q.getCount()]; |
| do { |
| String emailAddress = q.getString( |
| q.getColumnIndex(ContactsContract.CommonDataKinds.Email.ADDRESS)); |
| emailAddresses[i++] = emailAddress; |
| } while (q != null && q.moveToNext()); |
| } |
| } finally { |
| close(q); |
| } |
| } |
| |
| if (incoming) { |
| if (V) { |
| Log.d(TAG, "Adding originator for phone:" + phone); |
| } |
| // Use version 3.0 as we only have a formatted name |
| message.addOriginator(contactName, contactName, phoneNumbers, emailAddresses, null, |
| null); |
| } else { |
| if (V) { |
| Log.d(TAG, "Adding recipient for phone:" + phone); |
| } |
| // Use version 3.0 as we only have a formatted name |
| message.addRecipient(contactName, contactName, phoneNumbers, emailAddresses, null, |
| null); |
| } |
| return contactName; |
| } |
| |
| public static final int MAP_MESSAGE_CHARSET_NATIVE = 0; |
| public static final int MAP_MESSAGE_CHARSET_UTF8 = 1; |
| |
| public byte[] getSmsMessage(long id, int charset) throws UnsupportedEncodingException { |
| int type, threadId; |
| long time = -1; |
| String msgBody; |
| BluetoothMapbMessageSms message = new BluetoothMapbMessageSms(); |
| TelephonyManager tm = |
| (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); |
| |
| Cursor c = mResolver.query(Sms.CONTENT_URI, SMS_PROJECTION, "_ID = " + id, null, null); |
| if (c == null || !c.moveToFirst()) { |
| throw new IllegalArgumentException("SMS handle not found"); |
| } |
| |
| try { |
| if (c != null && c.moveToFirst()) { |
| if (V) { |
| Log.v(TAG, "c.count: " + c.getCount()); |
| } |
| |
| if (tm.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) { |
| message.setType(TYPE.SMS_CDMA); |
| } else { |
| // set SMS_GSM by default |
| message.setType(TYPE.SMS_GSM); |
| } |
| message.setVersionString(mMessageVersion); |
| String read = c.getString(c.getColumnIndex(Sms.READ)); |
| if (read.equalsIgnoreCase("1")) { |
| message.setStatus(true); |
| } else { |
| message.setStatus(false); |
| } |
| |
| type = c.getInt(c.getColumnIndex(Sms.TYPE)); |
| threadId = c.getInt(c.getColumnIndex(Sms.THREAD_ID)); |
| message.setFolder(getFolderName(type, threadId)); |
| |
| msgBody = c.getString(c.getColumnIndex(Sms.BODY)); |
| |
| String phone = c.getString(c.getColumnIndex(Sms.ADDRESS)); |
| if ((phone == null) && type == Sms.MESSAGE_TYPE_DRAFT) { |
| //Fetch address for Drafts folder from "canonical_address" table |
| phone = getCanonicalAddressSms(mResolver, threadId); |
| } |
| time = c.getLong(c.getColumnIndex(Sms.DATE)); |
| if (type == 1) { // Inbox message needs to set the vCard as originator |
| setVCardFromPhoneNumber(message, phone, true); |
| } else { // Other messages sets the vCard as the recipient |
| setVCardFromPhoneNumber(message, phone, false); |
| } |
| if (charset == MAP_MESSAGE_CHARSET_NATIVE) { |
| if (type == 1) { //Inbox |
| message.setSmsBodyPdus( |
| BluetoothMapSmsPdu.getDeliverPdus(msgBody, phone, time)); |
| } else { |
| message.setSmsBodyPdus(BluetoothMapSmsPdu.getSubmitPdus(msgBody, phone)); |
| } |
| } else /*if (charset == MAP_MESSAGE_CHARSET_UTF8)*/ { |
| message.setSmsBody(msgBody); |
| } |
| return message.encode(); |
| } |
| } finally { |
| if (c != null) { |
| c.close(); |
| } |
| } |
| |
| return message.encode(); |
| } |
| |
| private void extractMmsAddresses(long id, BluetoothMapbMessageMime message) { |
| final String[] projection = null; |
| String selection = new String(Mms.Addr.MSG_ID + "=" + id); |
| String uriStr = new String(Mms.CONTENT_URI + "/" + id + "/addr"); |
| Uri uriAddress = Uri.parse(uriStr); |
| String contactName = null; |
| |
| Cursor c = mResolver.query(uriAddress, projection, selection, null, null); |
| try { |
| if (c.moveToFirst()) { |
| do { |
| String address = c.getString(c.getColumnIndex(Mms.Addr.ADDRESS)); |
| if (address.equals(INSERT_ADDRES_TOKEN)) { |
| continue; |
| } |
| Integer type = c.getInt(c.getColumnIndex(Mms.Addr.TYPE)); |
| switch (type) { |
| case MMS_FROM: |
| contactName = setVCardFromPhoneNumber(message, address, true); |
| message.addFrom(contactName, address); |
| break; |
| case MMS_TO: |
| contactName = setVCardFromPhoneNumber(message, address, false); |
| message.addTo(contactName, address); |
| break; |
| case MMS_CC: |
| contactName = setVCardFromPhoneNumber(message, address, false); |
| message.addCc(contactName, address); |
| break; |
| case MMS_BCC: |
| contactName = setVCardFromPhoneNumber(message, address, false); |
| message.addBcc(contactName, address); |
| break; |
| default: |
| break; |
| } |
| } while (c.moveToNext()); |
| } |
| } finally { |
| if (c != null) { |
| c.close(); |
| } |
| } |
| } |
| |
| |
| /** |
| * Read out a mime data part and return the data in a byte array. |
| * @param contentPartUri TODO |
| * @param partid the content provider id of the Mime Part. |
| * @return |
| */ |
| private byte[] readRawDataPart(Uri contentPartUri, long partid) { |
| String uriStr = new String(contentPartUri + "/" + partid); |
| Uri uriAddress = Uri.parse(uriStr); |
| InputStream is = null; |
| ByteArrayOutputStream os = new ByteArrayOutputStream(); |
| int bufferSize = 8192; |
| byte[] buffer = new byte[bufferSize]; |
| byte[] retVal = null; |
| |
| try { |
| is = mResolver.openInputStream(uriAddress); |
| int len = 0; |
| while ((len = is.read(buffer)) != -1) { |
| os.write(buffer, 0, len); // We need to specify the len, as it can be != bufferSize |
| } |
| retVal = os.toByteArray(); |
| } catch (IOException e) { |
| // do nothing for now |
| Log.w(TAG, "Error reading part data", e); |
| } finally { |
| close(os); |
| close(is); |
| } |
| return retVal; |
| } |
| |
| /** |
| * Read out the mms parts and update the bMessage object provided i {@linkplain message} |
| * @param id the content provider ID of the message |
| * @param message the bMessage object to add the information to |
| */ |
| private void extractMmsParts(long id, BluetoothMapbMessageMime message) { |
| /* Handling of filtering out non-text parts for exclude |
| * attachments is handled within the bMessage object. */ |
| final String[] projection = null; |
| String selection = new String(Mms.Part.MSG_ID + "=" + id); |
| String uriStr = new String(Mms.CONTENT_URI + "/" + id + "/part"); |
| Uri uriAddress = Uri.parse(uriStr); |
| BluetoothMapbMessageMime.MimePart part; |
| Cursor c = mResolver.query(uriAddress, projection, selection, null, null); |
| try { |
| if (c.moveToFirst()) { |
| do { |
| Long partId = c.getLong(c.getColumnIndex(BaseColumns._ID)); |
| String contentType = c.getString(c.getColumnIndex(Mms.Part.CONTENT_TYPE)); |
| String name = c.getString(c.getColumnIndex(Mms.Part.NAME)); |
| String charset = c.getString(c.getColumnIndex(Mms.Part.CHARSET)); |
| String filename = c.getString(c.getColumnIndex(Mms.Part.FILENAME)); |
| String text = c.getString(c.getColumnIndex(Mms.Part.TEXT)); |
| Integer fd = c.getInt(c.getColumnIndex(Mms.Part._DATA)); |
| String cid = c.getString(c.getColumnIndex(Mms.Part.CONTENT_ID)); |
| String cl = c.getString(c.getColumnIndex(Mms.Part.CONTENT_LOCATION)); |
| String cdisp = c.getString(c.getColumnIndex(Mms.Part.CONTENT_DISPOSITION)); |
| |
| if (V) { |
| Log.d(TAG, " _id : " + partId + "\n ct : " + contentType |
| + "\n partname : " + name + "\n charset : " + charset |
| + "\n filename : " + filename + "\n text : " + text |
| + "\n fd : " + fd + "\n cid : " + cid + "\n cl : " + cl |
| + "\n cdisp : " + cdisp); |
| } |
| |
| part = message.addMimePart(); |
| part.mContentType = contentType; |
| part.mPartName = name; |
| part.mContentId = cid; |
| part.mContentLocation = cl; |
| part.mContentDisposition = cdisp; |
| |
| try { |
| if (text != null) { |
| part.mData = text.getBytes("UTF-8"); |
| part.mCharsetName = "utf-8"; |
| } else { |
| part.mData = |
| readRawDataPart(Uri.parse(Mms.CONTENT_URI + "/part"), partId); |
| if (charset != null) { |
| part.mCharsetName = |
| CharacterSets.getMimeName(Integer.parseInt(charset)); |
| } |
| } |
| } catch (NumberFormatException e) { |
| Log.d(TAG, "extractMmsParts", e); |
| part.mData = null; |
| part.mCharsetName = null; |
| } catch (UnsupportedEncodingException e) { |
| Log.d(TAG, "extractMmsParts", e); |
| part.mData = null; |
| part.mCharsetName = null; |
| } |
| part.mFileName = filename; |
| } while (c.moveToNext()); |
| message.updateCharset(); |
| } |
| |
| } finally { |
| if (c != null) { |
| c.close(); |
| } |
| } |
| } |
| |
| /** |
| * Read out the mms parts and update the bMessage object provided i {@linkplain message} |
| * @param id the content provider ID of the message |
| * @param message the bMessage object to add the information to |
| */ |
| private void extractIMParts(long id, BluetoothMapbMessageMime message) { |
| /* Handling of filtering out non-text parts for exclude |
| * attachments is handled within the bMessage object. */ |
| final String[] projection = null; |
| String selection = new String(BluetoothMapContract.MessageColumns._ID + "=" + id); |
| String uriStr = |
| new String(mBaseUri + BluetoothMapContract.TABLE_MESSAGE + "/" + id + "/part"); |
| Uri uriAddress = Uri.parse(uriStr); |
| BluetoothMapbMessageMime.MimePart part; |
| Cursor c = mResolver.query(uriAddress, projection, selection, null, null); |
| try { |
| if (c.moveToFirst()) { |
| do { |
| Long partId = c.getLong( |
| c.getColumnIndex(BluetoothMapContract.MessagePartColumns._ID)); |
| String charset = c.getString( |
| c.getColumnIndex(BluetoothMapContract.MessagePartColumns.CHARSET)); |
| String filename = c.getString( |
| c.getColumnIndex(BluetoothMapContract.MessagePartColumns.FILENAME)); |
| String text = c.getString( |
| c.getColumnIndex(BluetoothMapContract.MessagePartColumns.TEXT)); |
| String body = c.getString( |
| c.getColumnIndex(BluetoothMapContract.MessagePartColumns.RAW_DATA)); |
| String cid = c.getString( |
| c.getColumnIndex(BluetoothMapContract.MessagePartColumns.CONTENT_ID)); |
| |
| if (V) { |
| Log.d(TAG, " _id : " + partId + "\n charset : " + charset |
| + "\n filename : " + filename + "\n text : " + text |
| + "\n cid : " + cid); |
| } |
| |
| part = message.addMimePart(); |
| part.mContentId = cid; |
| try { |
| if (text.equalsIgnoreCase("yes")) { |
| part.mData = body.getBytes("UTF-8"); |
| part.mCharsetName = "utf-8"; |
| } else { |
| part.mData = readRawDataPart( |
| Uri.parse(mBaseUri + BluetoothMapContract.TABLE_MESSAGE_PART), |
| partId); |
| if (charset != null) { |
| part.mCharsetName = |
| CharacterSets.getMimeName(Integer.parseInt(charset)); |
| } |
| } |
| } catch (NumberFormatException e) { |
| Log.d(TAG, "extractIMParts", e); |
| part.mData = null; |
| part.mCharsetName = null; |
| } catch (UnsupportedEncodingException e) { |
| Log.d(TAG, "extractIMParts", e); |
| part.mData = null; |
| part.mCharsetName = null; |
| } |
| part.mFileName = filename; |
| } while (c.moveToNext()); |
| } |
| } finally { |
| if (c != null) { |
| c.close(); |
| } |
| } |
| |
| message.updateCharset(); |
| } |
| |
| /** |
| * |
| * @param id the content provider id for the message to fetch. |
| * @param appParams The application parameter object received from the client. |
| * @return a byte[] containing the utf-8 encoded bMessage to send to the client. |
| * @throws UnsupportedEncodingException if UTF-8 is not supported, |
| * which is guaranteed to be supported on an android device |
| */ |
| public byte[] getMmsMessage(long id, BluetoothMapAppParams appParams) |
| throws UnsupportedEncodingException { |
| int msgBox, threadId; |
| if (appParams.getCharset() == MAP_MESSAGE_CHARSET_NATIVE) { |
| throw new IllegalArgumentException( |
| "MMS charset native not allowed for MMS" + " - must be utf-8"); |
| } |
| |
| BluetoothMapbMessageMime message = new BluetoothMapbMessageMime(); |
| Cursor c = mResolver.query(Mms.CONTENT_URI, MMS_PROJECTION, "_ID = " + id, null, null); |
| try { |
| if (c != null && c.moveToFirst()) { |
| message.setType(TYPE.MMS); |
| message.setVersionString(mMessageVersion); |
| |
| // The MMS info: |
| String read = c.getString(c.getColumnIndex(Mms.READ)); |
| if (read.equalsIgnoreCase("1")) { |
| message.setStatus(true); |
| } else { |
| message.setStatus(false); |
| } |
| |
| msgBox = c.getInt(c.getColumnIndex(Mms.MESSAGE_BOX)); |
| threadId = c.getInt(c.getColumnIndex(Mms.THREAD_ID)); |
| message.setFolder(getFolderName(msgBox, threadId)); |
| message.setSubject(c.getString(c.getColumnIndex(Mms.SUBJECT))); |
| message.setMessageId(c.getString(c.getColumnIndex(Mms.MESSAGE_ID))); |
| message.setContentType(c.getString(c.getColumnIndex(Mms.CONTENT_TYPE))); |
| message.setDate(c.getLong(c.getColumnIndex(Mms.DATE)) * 1000L); |
| message.setTextOnly(c.getInt(c.getColumnIndex(Mms.TEXT_ONLY)) != 0); |
| message.setIncludeAttachments(appParams.getAttachment() != 0); |
| // c.getLong(c.getColumnIndex(Mms.DATE_SENT)); - this is never used |
| // c.getInt(c.getColumnIndex(Mms.STATUS)); - don't know what this is |
| |
| // The parts |
| extractMmsParts(id, message); |
| |
| // The addresses |
| extractMmsAddresses(id, message); |
| |
| |
| return message.encode(); |
| } |
| } finally { |
| if (c != null) { |
| c.close(); |
| } |
| } |
| |
| return message.encode(); |
| } |
| |
| /** |
| * |
| * @param id the content provider id for the message to fetch. |
| * @param appParams The application parameter object received from the client. |
| * @return a byte[] containing the utf-8 encoded bMessage to send to the client. |
| * @throws UnsupportedEncodingException if UTF-8 is not supported, |
| * which is guaranteed to be supported on an android device |
| */ |
| public byte[] getEmailMessage(long id, BluetoothMapAppParams appParams, |
| BluetoothMapFolderElement currentFolder) throws UnsupportedEncodingException { |
| // Log print out of application parameters set |
| if (D && appParams != null) { |
| Log.d(TAG, |
| "TYPE_MESSAGE (GET): Attachment = " + appParams.getAttachment() + ", Charset = " |
| + appParams.getCharset() + ", FractionRequest = " |
| + appParams.getFractionRequest()); |
| } |
| |
| // Throw exception if requester NATIVE charset for Email |
| // Exception is caught by MapObexServer sendGetMessageResp |
| if (appParams.getCharset() == MAP_MESSAGE_CHARSET_NATIVE) { |
| throw new IllegalArgumentException("EMAIL charset not UTF-8"); |
| } |
| |
| BluetoothMapbMessageEmail message = new BluetoothMapbMessageEmail(); |
| Uri contentUri = Uri.parse(mBaseUri + BluetoothMapContract.TABLE_MESSAGE); |
| Cursor c = mResolver.query(contentUri, BluetoothMapContract.BT_MESSAGE_PROJECTION, |
| "_ID = " + id, null, null); |
| try { |
| if (c != null && c.moveToFirst()) { |
| BluetoothMapFolderElement folderElement; |
| FileInputStream is = null; |
| ParcelFileDescriptor fd = null; |
| try { |
| // Handle fraction requests |
| int fractionRequest = appParams.getFractionRequest(); |
| if (fractionRequest != BluetoothMapAppParams.INVALID_VALUE_PARAMETER) { |
| // Fraction requested |
| if (V) { |
| String fractionStr = (fractionRequest == 0) ? "FIRST" : "NEXT"; |
| Log.v(TAG, "getEmailMessage - FractionRequest " + fractionStr |
| + " - send compete message"); |
| } |
| // Check if message is complete and if not - request message from server |
| if (!c.getString(c.getColumnIndex( |
| BluetoothMapContract.MessageColumns.RECEPTION_STATE)) |
| .equalsIgnoreCase(BluetoothMapContract.RECEPTION_STATE_COMPLETE)) { |
| // TODO: request message from server |
| Log.w(TAG, "getEmailMessage - receptionState not COMPLETE - Not " |
| + "Implemented!"); |
| } |
| } |
| // Set read status: |
| String read = c.getString( |
| c.getColumnIndex(BluetoothMapContract.MessageColumns.FLAG_READ)); |
| if (read != null && read.equalsIgnoreCase("1")) { |
| message.setStatus(true); |
| } else { |
| message.setStatus(false); |
| } |
| |
| // Set message type: |
| message.setType(TYPE.EMAIL); |
| message.setVersionString(mMessageVersion); |
| // Set folder: |
| long folderId = c.getLong( |
| c.getColumnIndex(BluetoothMapContract.MessageColumns.FOLDER_ID)); |
| folderElement = currentFolder.getFolderById(folderId); |
| message.setCompleteFolder(folderElement.getFullPath()); |
| |
| // Set recipient: |
| String nameEmail = c.getString( |
| c.getColumnIndex(BluetoothMapContract.MessageColumns.TO_LIST)); |
| Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(nameEmail); |
| if (tokens.length != 0) { |
| if (D) { |
| Log.d(TAG, "Recipient count= " + tokens.length); |
| } |
| int i = 0; |
| while (i < tokens.length) { |
| if (V) { |
| Log.d(TAG, "Recipient = " + tokens[i].toString()); |
| } |
| String[] emails = new String[1]; |
| emails[0] = tokens[i].getAddress(); |
| String name = tokens[i].getName(); |
| message.addRecipient(name, name, null, emails, null, null); |
| i++; |
| } |
| } |
| |
| // Set originator: |
| nameEmail = c.getString( |
| c.getColumnIndex(BluetoothMapContract.MessageColumns.FROM_LIST)); |
| tokens = Rfc822Tokenizer.tokenize(nameEmail); |
| if (tokens.length != 0) { |
| if (D) { |
| Log.d(TAG, "Originator count= " + tokens.length); |
| } |
| int i = 0; |
| while (i < tokens.length) { |
| if (V) { |
| Log.d(TAG, "Originator = " + tokens[i].toString()); |
| } |
| String[] emails = new String[1]; |
| emails[0] = tokens[i].getAddress(); |
| String name = tokens[i].getName(); |
| message.addOriginator(name, name, null, emails, null, null); |
| i++; |
| } |
| } |
| } finally { |
| if (c != null) { |
| c.close(); |
| } |
| } |
| // Find out if we get attachments |
| String attStr = (appParams.getAttachment() == 0) ? "/" |
| + BluetoothMapContract.FILE_MSG_NO_ATTACHMENTS : ""; |
| Uri uri = Uri.parse(contentUri + "/" + id + attStr); |
| |
| // Get email message body content |
| int count = 0; |
| try { |
| fd = mResolver.openFileDescriptor(uri, "r"); |
| is = new FileInputStream(fd.getFileDescriptor()); |
| StringBuilder email = new StringBuilder(""); |
| byte[] buffer = new byte[1024]; |
| while ((count = is.read(buffer)) != -1) { |
| // TODO: Handle breaks within a UTF8 character |
| email.append(new String(buffer, 0, count)); |
| if (V) { |
| Log.d(TAG, "Email part = " + new String(buffer, 0, count) + " count=" |
| + count); |
| } |
| } |
| // Set email message body: |
| message.setEmailBody(email.toString()); |
| } catch (FileNotFoundException e) { |
| Log.w(TAG, e); |
| } catch (NullPointerException e) { |
| Log.w(TAG, e); |
| } catch (IOException e) { |
| Log.w(TAG, e); |
| } finally { |
| try { |
| if (is != null) { |
| is.close(); |
| } |
| } catch (IOException e) { |
| } |
| try { |
| if (fd != null) { |
| fd.close(); |
| } |
| } catch (IOException e) { |
| } |
| } |
| return message.encode(); |
| } |
| } finally { |
| if (c != null) { |
| c.close(); |
| } |
| } |
| throw new IllegalArgumentException("EMAIL handle not found"); |
| } |
| /** |
| * |
| * @param id the content provider id for the message to fetch. |
| * @param appParams The application parameter object received from the client. |
| * @return a byte[] containing the UTF-8 encoded bMessage to send to the client. |
| * @throws UnsupportedEncodingException if UTF-8 is not supported, |
| * which is guaranteed to be supported on an android device |
| */ |
| |
| /** |
| * |
| * @param id the content provider id for the message to fetch. |
| * @param appParams The application parameter object received from the client. |
| * @return a byte[] containing the utf-8 encoded bMessage to send to the client. |
| * @throws UnsupportedEncodingException if UTF-8 is not supported, |
| * which is guaranteed to be supported on an android device |
| */ |
| public byte[] getIMMessage(long id, BluetoothMapAppParams appParams, |
| BluetoothMapFolderElement folderElement) throws UnsupportedEncodingException { |
| long threadId, folderId; |
| |
| if (appParams.getCharset() == MAP_MESSAGE_CHARSET_NATIVE) { |
| throw new IllegalArgumentException( |
| "IM charset native not allowed for IM - must be utf-8"); |
| } |
| |
| BluetoothMapbMessageMime message = new BluetoothMapbMessageMime(); |
| Uri contentUri = Uri.parse(mBaseUri + BluetoothMapContract.TABLE_MESSAGE); |
| Cursor c = mResolver.query(contentUri, BluetoothMapContract.BT_MESSAGE_PROJECTION, |
| "_ID = " + id, null, null); |
| Cursor contacts = null; |
| try { |
| if (c != null && c.moveToFirst()) { |
| message.setType(TYPE.IM); |
| message.setVersionString(mMessageVersion); |
| |
| // The IM message info: |
| int read = |
| c.getInt(c.getColumnIndex(BluetoothMapContract.MessageColumns.FLAG_READ)); |
| if (read == 1) { |
| message.setStatus(true); |
| } else { |
| message.setStatus(false); |
| } |
| |
| threadId = |
| c.getInt(c.getColumnIndex(BluetoothMapContract.MessageColumns.THREAD_ID)); |
| folderId = |
| c.getLong(c.getColumnIndex(BluetoothMapContract.MessageColumns.FOLDER_ID)); |
| folderElement = folderElement.getFolderById(folderId); |
| message.setCompleteFolder(folderElement.getFullPath()); |
| message.setSubject( |
| c.getString(c.getColumnIndex(BluetoothMapContract.MessageColumns.SUBJECT))); |
| message.setMessageId( |
| c.getString(c.getColumnIndex(BluetoothMapContract.MessageColumns._ID))); |
| message.setDate( |
| c.getLong(c.getColumnIndex(BluetoothMapContract.MessageColumns.DATE))); |
| message.setTextOnly(c.getInt( |
| c.getColumnIndex(BluetoothMapContract.MessageColumns.ATTACHMENT_SIZE)) |
| == 0); |
| |
| message.setIncludeAttachments(appParams.getAttachment() != 0); |
| |
| // c.getLong(c.getColumnIndex(Mms.DATE_SENT)); - this is never used |
| // c.getInt(c.getColumnIndex(Mms.STATUS)); - don't know what this is |
| |
| // The parts |
| |
| //FIXME use the parts when ready - until then use the body column for text-only |
| // extractIMParts(id, message); |
| //FIXME next few lines are temporary code |
| MimePart part = message.addMimePart(); |
| part.mData = |
| c.getString((c.getColumnIndex(BluetoothMapContract.MessageColumns.BODY))) |
| .getBytes("UTF-8"); |
| part.mCharsetName = "utf-8"; |
| part.mContentId = "0"; |
| part.mContentType = "text/plain"; |
| message.updateCharset(); |
| // FIXME end temp code |
| |
| Uri contactsUri = Uri.parse(mBaseUri + BluetoothMapContract.TABLE_CONVOCONTACT); |
| contacts = mResolver.query(contactsUri, BluetoothMapContract.BT_CONTACT_PROJECTION, |
| BluetoothMapContract.ConvoContactColumns.CONVO_ID + " = " + threadId, null, |
| null); |
| // TODO this will not work for group-chats |
| if (contacts != null && contacts.moveToFirst()) { |
| String name = contacts.getString( |
| contacts.getColumnIndex(BluetoothMapContract.ConvoContactColumns.NAME)); |
| String[] btUid = new String[1]; |
| btUid[0] = contacts.getString(contacts.getColumnIndex( |
| BluetoothMapContract.ConvoContactColumns.X_BT_UID)); |
| String nickname = contacts.getString(contacts.getColumnIndex( |
| BluetoothMapContract.ConvoContactColumns.NICKNAME)); |
| String[] btUci = new String[1]; |
| String[] btOwnUci = new String[1]; |
| btOwnUci[0] = mAccount.getUciFull(); |
| btUci[0] = contacts.getString( |
| contacts.getColumnIndex(BluetoothMapContract.ConvoContactColumns.UCI)); |
| if (folderId == BluetoothMapContract.FOLDER_ID_SENT |
| || folderId == BluetoothMapContract.FOLDER_ID_OUTBOX) { |
| message.addRecipient(nickname, name, null, null, btUid, btUci); |
| message.addOriginator(null, btOwnUci); |
| |
| } else { |
| message.addOriginator(nickname, name, null, null, btUid, btUci); |
| message.addRecipient(null, btOwnUci); |
| |
| } |
| } |
| return message.encode(); |
| } |
| } finally { |
| if (c != null) { |
| c.close(); |
| } |
| if (contacts != null) { |
| contacts.close(); |
| } |
| } |
| |
| throw new IllegalArgumentException("IM handle not found"); |
| } |
| |
| public void setRemoteFeatureMask(int featureMask) { |
| this.mRemoteFeatureMask = featureMask; |
| if (V) { |
| Log.d(TAG, "setRemoteFeatureMask"); |
| } |
| if ((this.mRemoteFeatureMask & BluetoothMapUtils.MAP_FEATURE_MESSAGE_LISTING_FORMAT_V11_BIT) |
| == BluetoothMapUtils.MAP_FEATURE_MESSAGE_LISTING_FORMAT_V11_BIT) { |
| if (V) { |
| Log.d(TAG, "setRemoteFeatureMask MAP_MESSAGE_LISTING_FORMAT_V11"); |
| } |
| this.mMsgListingVersion = BluetoothMapUtils.MAP_MESSAGE_LISTING_FORMAT_V11; |
| } |
| } |
| |
| public int getRemoteFeatureMask() { |
| return this.mRemoteFeatureMask; |
| } |
| |
| HashMap<Long, BluetoothMapConvoListingElement> getSmsMmsConvoList() { |
| return mMasInstance.getSmsMmsConvoList(); |
| } |
| |
| void setSmsMmsConvoList(HashMap<Long, BluetoothMapConvoListingElement> smsMmsConvoList) { |
| mMasInstance.setSmsMmsConvoList(smsMmsConvoList); |
| } |
| |
| HashMap<Long, BluetoothMapConvoListingElement> getImEmailConvoList() { |
| return mMasInstance.getImEmailConvoList(); |
| } |
| |
| void setImEmailConvoList(HashMap<Long, BluetoothMapConvoListingElement> imEmailConvoList) { |
| mMasInstance.setImEmailConvoList(imEmailConvoList); |
| } |
| } |