1ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrickpackage com.android.mms.data; 2ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick 3b51ea8318ca2e4019b666d938e3e7efdf6e643b3Tom Taylorimport java.util.ArrayList; 4b04236e2977ac69e63cc0fa123399a584b606945Tom Taylorimport java.util.Collection; 5ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrickimport java.util.HashSet; 6ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrickimport java.util.Iterator; 770c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrickimport java.util.Set; 8ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick 9b736686638eca62aa89cb15184711ef38413cb3eTom Taylorimport android.app.Activity; 1070c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrickimport android.content.AsyncQueryHandler; 11f7e8281a223af6228e6399055a6197a1edd9bc3aTom Taylorimport android.content.ContentResolver; 12ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrickimport android.content.ContentUris; 13ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrickimport android.content.ContentValues; 14ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrickimport android.content.Context; 1570c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrickimport android.database.Cursor; 16ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrickimport android.net.Uri; 176a53d30825817a8e70a3da0e591449377306959aTom Taylorimport android.os.AsyncTask; 18b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylorimport android.provider.BaseColumns; 19f7e8281a223af6228e6399055a6197a1edd9bc3aTom Taylorimport android.provider.Telephony.Mms; 20f7e8281a223af6228e6399055a6197a1edd9bc3aTom Taylorimport android.provider.Telephony.MmsSms; 21f7e8281a223af6228e6399055a6197a1edd9bc3aTom Taylorimport android.provider.Telephony.Sms; 22f7e8281a223af6228e6399055a6197a1edd9bc3aTom Taylorimport android.provider.Telephony.Sms.Conversations; 23d64419030e1fec1e751695dab3bd7236e2fb0214Roger Chenimport android.provider.Telephony.Threads; 24b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylorimport android.provider.Telephony.ThreadsColumns; 25b736686638eca62aa89cb15184711ef38413cb3eTom Taylorimport android.telephony.PhoneNumberUtils; 26ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrickimport android.text.TextUtils; 27ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrickimport android.util.Log; 28ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick 29d62ef06699b3ca5048c5642bd50300e9a2eb04a1Tom Taylorimport com.android.mms.LogTag; 307b6fe946f2e1020432e3600c8863f72449cd4e68Tom Taylorimport com.android.mms.MmsApp; 31f7e8281a223af6228e6399055a6197a1edd9bc3aTom Taylorimport com.android.mms.R; 32ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrickimport com.android.mms.transaction.MessagingNotification; 33fef537959e6beb02279e4994934ec88df080846fTom Taylorimport com.android.mms.ui.ComposeMessageActivity; 34ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrickimport com.android.mms.ui.MessageUtils; 35ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrickimport com.android.mms.util.DraftCache; 36ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick 37ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick/** 38ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick * An interface for finding information about conversations and/or creating new ones. 39ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick */ 40ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrickpublic class Conversation { 4113dbe96fc54f9b7190fd415d737f9a56dc409d10Wei Huang private static final String TAG = "Mms/conv"; 4207ce1878a36d2df1707dd4bbd9cd7235679bdc94Tom Taylor private static final boolean DEBUG = false; 43d2f67cfca13a6e415636dc253c371fb76974f5faTom Taylor private static final boolean DELETEDEBUG = false; 44ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick 45c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor public static final Uri sAllThreadsUri = 4670c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick Threads.CONTENT_URI.buildUpon().appendQueryParameter("simple", "true").build(); 4771bbae69cee0da902032743d0702e283cfe31504Ficus Kirkpatrick 48c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor public static final String[] ALL_THREADS_PROJECTION = { 4970c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick Threads._ID, Threads.DATE, Threads.MESSAGE_COUNT, Threads.RECIPIENT_IDS, 5070c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick Threads.SNIPPET, Threads.SNIPPET_CHARSET, Threads.READ, Threads.ERROR, 5170c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick Threads.HAS_ATTACHMENT 5270c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick }; 53627007213deb59ef938c80353c8f3598b01478b3Wei Huang 54c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor public static final String[] UNREAD_PROJECTION = { 55e14b79584cca1bf7ba60c53bd7e3d6386adbfc59Wei Huang Threads._ID, 56e14b79584cca1bf7ba60c53bd7e3d6386adbfc59Wei Huang Threads.READ 57436042159134e8ecbc57097340a5bd81f2912574Tom Taylor }; 58627007213deb59ef938c80353c8f3598b01478b3Wei Huang 596bbfd941885c588cc5d94e345d2cfc4197134a19Tom Taylor private static final String UNREAD_SELECTION = "(read=0 OR seen=0)"; 60e14b79584cca1bf7ba60c53bd7e3d6386adbfc59Wei Huang 61627007213deb59ef938c80353c8f3598b01478b3Wei Huang private static final String[] SEEN_PROJECTION = new String[] { 62627007213deb59ef938c80353c8f3598b01478b3Wei Huang "seen" 63627007213deb59ef938c80353c8f3598b01478b3Wei Huang }; 64627007213deb59ef938c80353c8f3598b01478b3Wei Huang 6570c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick private static final int ID = 0; 6670c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick private static final int DATE = 1; 6770c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick private static final int MESSAGE_COUNT = 2; 6870c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick private static final int RECIPIENT_IDS = 3; 6970c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick private static final int SNIPPET = 4; 7070c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick private static final int SNIPPET_CS = 5; 7170c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick private static final int READ = 6; 7270c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick private static final int ERROR = 7; 7370c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick private static final int HAS_ATTACHMENT = 8; 7471bbae69cee0da902032743d0702e283cfe31504Ficus Kirkpatrick 7570c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick 76ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick private final Context mContext; 77ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick 78ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick // The thread ID of this conversation. Can be zero in the case of a 79ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick // new conversation where the recipient set is changing as the user 80ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick // types and we have not hit the database yet to create a thread. 81ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick private long mThreadId; 8271bbae69cee0da902032743d0702e283cfe31504Ficus Kirkpatrick 831d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick private ContactList mRecipients; // The current set of recipients. 8470c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick private long mDate; // The last update time. 8570c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick private int mMessageCount; // Number of messages. 8670c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick private String mSnippet; // Text of the most recent message. 8770c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick private boolean mHasUnreadMessages; // True if there are unread messages. 8870c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick private boolean mHasAttachment; // True if any message has an attachment. 8970c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick private boolean mHasError; // True if any message is in an error state. 90f9d706cfd0c46a74ba3d79e5543f13a225328d30Tom Taylor private boolean mIsChecked; // True if user has selected the conversation for a 91f9d706cfd0c46a74ba3d79e5543f13a225328d30Tom Taylor // multi-operation such as delete. 9270c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick 936bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor private static ContentValues sReadContentValues; 946bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor private static boolean sLoadingThreads; 956bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor private static boolean sDeletingThreads; 966bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor private static Object sDeletingThreadsLock = new Object(); 97436042159134e8ecbc57097340a5bd81f2912574Tom Taylor private boolean mMarkAsReadBlocked; 986a53d30825817a8e70a3da0e591449377306959aTom Taylor private boolean mMarkAsReadWaiting; 995893069282b516db7da5e16eef051cd02508eb2aTom Taylor 100ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick private Conversation(Context context) { 101ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick mContext = context; 1021d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick mRecipients = new ContactList(); 10370c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick mThreadId = 0; 10470c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick } 10571bbae69cee0da902032743d0702e283cfe31504Ficus Kirkpatrick 106e692b9dcf5742e7a3ef3a7e64b44bb9d08f05b57Tom Taylor private Conversation(Context context, long threadId, boolean allowQuery) { 107c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor if (DEBUG) { 108c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor Log.v(TAG, "Conversation constructor threadId: " + threadId); 109c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor } 11070c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick mContext = context; 111e692b9dcf5742e7a3ef3a7e64b44bb9d08f05b57Tom Taylor if (!loadFromThreadId(threadId, allowQuery)) { 1126cba8248f3e41921b03cc74a823a6347016e69baTom Taylor mRecipients = new ContactList(); 1136cba8248f3e41921b03cc74a823a6347016e69baTom Taylor mThreadId = 0; 1146cba8248f3e41921b03cc74a823a6347016e69baTom Taylor } 11570c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick } 11670c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick 11770c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick private Conversation(Context context, Cursor cursor, boolean allowQuery) { 118c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor if (DEBUG) { 119c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor Log.v(TAG, "Conversation constructor cursor, allowQuery: " + allowQuery); 120c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor } 12170c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick mContext = context; 12270c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick fillFromCursor(context, this, cursor, allowQuery); 123ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick } 12471bbae69cee0da902032743d0702e283cfe31504Ficus Kirkpatrick 125ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick /** 126627007213deb59ef938c80353c8f3598b01478b3Wei Huang * Create a new conversation with no recipients. {@link #setRecipients} can 127ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick * be called as many times as you like; the conversation will not be 128627007213deb59ef938c80353c8f3598b01478b3Wei Huang * created in the database until {@link #ensureThreadId} is called. 129ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick */ 13059be3105fb43b5e9fcb363981dfa3de31c1bc2a8Wei Huang public static Conversation createNew(Context context) { 13170c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick return new Conversation(context); 132ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick } 133ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick 134ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick /** 135ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick * Find the conversation matching the provided thread ID. 136ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick */ 137e692b9dcf5742e7a3ef3a7e64b44bb9d08f05b57Tom Taylor public static Conversation get(Context context, long threadId, boolean allowQuery) { 138c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor if (DEBUG) { 139c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor Log.v(TAG, "Conversation get by threadId: " + threadId); 140c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor } 141e37a39111cf1e43107308e607c1c955989887c40Wei Huang Conversation conv = Cache.get(threadId); 142e37a39111cf1e43107308e607c1c955989887c40Wei Huang if (conv != null) 14370c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick return conv; 144e37a39111cf1e43107308e607c1c955989887c40Wei Huang 145e692b9dcf5742e7a3ef3a7e64b44bb9d08f05b57Tom Taylor conv = new Conversation(context, threadId, allowQuery); 146e37a39111cf1e43107308e607c1c955989887c40Wei Huang try { 147e37a39111cf1e43107308e607c1c955989887c40Wei Huang Cache.put(conv); 148e37a39111cf1e43107308e607c1c955989887c40Wei Huang } catch (IllegalStateException e) { 149c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor LogTag.error("Tried to add duplicate Conversation to Cache (from threadId): " + conv); 150c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor if (!Cache.replace(conv)) { 151c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor LogTag.error("get by threadId cache.replace failed on " + conv); 152c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor } 15371bbae69cee0da902032743d0702e283cfe31504Ficus Kirkpatrick } 154e37a39111cf1e43107308e607c1c955989887c40Wei Huang return conv; 155ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick } 15671bbae69cee0da902032743d0702e283cfe31504Ficus Kirkpatrick 157ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick /** 158ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick * Find the conversation matching the provided recipient set. 159e37a39111cf1e43107308e607c1c955989887c40Wei Huang * When called with an empty recipient list, equivalent to {@link #createNew}. 160ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick */ 161e692b9dcf5742e7a3ef3a7e64b44bb9d08f05b57Tom Taylor public static Conversation get(Context context, ContactList recipients, boolean allowQuery) { 162c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor if (DEBUG) { 163c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor Log.v(TAG, "Conversation get by recipients: " + recipients.serialize()); 164c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor } 165ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick // If there are no recipients in the list, make a new conversation. 166ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick if (recipients.size() < 1) { 167ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick return createNew(context); 168ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick } 169ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick 170e37a39111cf1e43107308e607c1c955989887c40Wei Huang Conversation conv = Cache.get(recipients); 171e37a39111cf1e43107308e607c1c955989887c40Wei Huang if (conv != null) 172e37a39111cf1e43107308e607c1c955989887c40Wei Huang return conv; 173715e32f97bd9d8ce4b5ba650b97ba4b137150456Tom Taylor 174e37a39111cf1e43107308e607c1c955989887c40Wei Huang long threadId = getOrCreateThreadId(context, recipients); 175e692b9dcf5742e7a3ef3a7e64b44bb9d08f05b57Tom Taylor conv = new Conversation(context, threadId, allowQuery); 176ef1e257cbaa7f7085eda3a9b075eca79075aab89Wink Saville Log.d(TAG, "Conversation.get: created new conversation " + /*conv.toString()*/ "xxxxxxx"); 17709a75ac1d3710e60dbe78ead3dee6863ffb380caWei Huang 17809a75ac1d3710e60dbe78ead3dee6863ffb380caWei Huang if (!conv.getRecipients().equals(recipients)) { 179c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor LogTag.error(TAG, "Conversation.get: new conv's recipients don't match input recpients " 180ef1e257cbaa7f7085eda3a9b075eca79075aab89Wink Saville + /*recipients*/ "xxxxxxx"); 18109a75ac1d3710e60dbe78ead3dee6863ffb380caWei Huang } 18267ec6c54e11c19caf894b4ffce7250fb3fd96d30Wei Huang 183e37a39111cf1e43107308e607c1c955989887c40Wei Huang try { 184e37a39111cf1e43107308e607c1c955989887c40Wei Huang Cache.put(conv); 185e37a39111cf1e43107308e607c1c955989887c40Wei Huang } catch (IllegalStateException e) { 186c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor LogTag.error("Tried to add duplicate Conversation to Cache (from recipients): " + conv); 187c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor if (!Cache.replace(conv)) { 188c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor LogTag.error("get by recipients cache.replace failed on " + conv); 189c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor } 19071bbae69cee0da902032743d0702e283cfe31504Ficus Kirkpatrick } 191e37a39111cf1e43107308e607c1c955989887c40Wei Huang 192e37a39111cf1e43107308e607c1c955989887c40Wei Huang return conv; 193ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick } 19471bbae69cee0da902032743d0702e283cfe31504Ficus Kirkpatrick 195ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick /** 196ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick * Find the conversation matching in the specified Uri. Example 197ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick * forms: {@value content://mms-sms/conversations/3} or 198ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick * {@value sms:+12124797990}. 199e37a39111cf1e43107308e607c1c955989887c40Wei Huang * When called with a null Uri, equivalent to {@link #createNew}. 200ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick */ 201e692b9dcf5742e7a3ef3a7e64b44bb9d08f05b57Tom Taylor public static Conversation get(Context context, Uri uri, boolean allowQuery) { 202c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor if (DEBUG) { 203c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor Log.v(TAG, "Conversation get by uri: " + uri); 204c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor } 205ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick if (uri == null) { 206ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick return createNew(context); 207ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick } 20871bbae69cee0da902032743d0702e283cfe31504Ficus Kirkpatrick 209e37a39111cf1e43107308e607c1c955989887c40Wei Huang if (DEBUG) Log.v(TAG, "Conversation get URI: " + uri); 210e37a39111cf1e43107308e607c1c955989887c40Wei Huang 211ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick // Handle a conversation URI 212ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick if (uri.getPathSegments().size() >= 2) { 213ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick try { 214ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick long threadId = Long.parseLong(uri.getPathSegments().get(1)); 215e692b9dcf5742e7a3ef3a7e64b44bb9d08f05b57Tom Taylor if (DEBUG) { 216e692b9dcf5742e7a3ef3a7e64b44bb9d08f05b57Tom Taylor Log.v(TAG, "Conversation get threadId: " + threadId); 217e692b9dcf5742e7a3ef3a7e64b44bb9d08f05b57Tom Taylor } 218e692b9dcf5742e7a3ef3a7e64b44bb9d08f05b57Tom Taylor return get(context, threadId, allowQuery); 219ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick } catch (NumberFormatException exception) { 220d62ef06699b3ca5048c5642bd50300e9a2eb04a1Tom Taylor LogTag.error("Invalid URI: " + uri); 221ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick } 222ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick } 22371bbae69cee0da902032743d0702e283cfe31504Ficus Kirkpatrick 22449e599163acc3200e9bda4fb7825c041b67960f1jshin String recipients = PhoneNumberUtils.replaceUnicodeDigits(getRecipients(uri)) 22549e599163acc3200e9bda4fb7825c041b67960f1jshin .replace(',', ';'); 226a56495caea392de00eea50fb84ffdc06537110baTom Taylor return get(context, ContactList.getByNumbers(recipients, 227e692b9dcf5742e7a3ef3a7e64b44bb9d08f05b57Tom Taylor allowQuery /* don't block */, true /* replace number */), allowQuery); 228ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick } 229ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick 230ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick /** 231d62ef06699b3ca5048c5642bd50300e9a2eb04a1Tom Taylor * Returns true if the recipient in the uri matches the recipient list in this 232d62ef06699b3ca5048c5642bd50300e9a2eb04a1Tom Taylor * conversation. 233d62ef06699b3ca5048c5642bd50300e9a2eb04a1Tom Taylor */ 234fcca7b38fcc90a781f1507a0a135a64e3ae8f6d8Tom Taylor public boolean sameRecipient(Uri uri, Context context) { 235d62ef06699b3ca5048c5642bd50300e9a2eb04a1Tom Taylor int size = mRecipients.size(); 236d62ef06699b3ca5048c5642bd50300e9a2eb04a1Tom Taylor if (size > 1) { 237d62ef06699b3ca5048c5642bd50300e9a2eb04a1Tom Taylor return false; 238d62ef06699b3ca5048c5642bd50300e9a2eb04a1Tom Taylor } 239d62ef06699b3ca5048c5642bd50300e9a2eb04a1Tom Taylor if (uri == null) { 240d62ef06699b3ca5048c5642bd50300e9a2eb04a1Tom Taylor return size == 0; 241d62ef06699b3ca5048c5642bd50300e9a2eb04a1Tom Taylor } 242fcca7b38fcc90a781f1507a0a135a64e3ae8f6d8Tom Taylor ContactList incomingRecipient = null; 243d62ef06699b3ca5048c5642bd50300e9a2eb04a1Tom Taylor if (uri.getPathSegments().size() >= 2) { 244fcca7b38fcc90a781f1507a0a135a64e3ae8f6d8Tom Taylor // it's a thread id for a conversation 245fcca7b38fcc90a781f1507a0a135a64e3ae8f6d8Tom Taylor Conversation otherConv = get(context, uri, false); 246fcca7b38fcc90a781f1507a0a135a64e3ae8f6d8Tom Taylor if (otherConv == null) { 247fcca7b38fcc90a781f1507a0a135a64e3ae8f6d8Tom Taylor return false; 248fcca7b38fcc90a781f1507a0a135a64e3ae8f6d8Tom Taylor } 249fcca7b38fcc90a781f1507a0a135a64e3ae8f6d8Tom Taylor incomingRecipient = otherConv.mRecipients; 250fcca7b38fcc90a781f1507a0a135a64e3ae8f6d8Tom Taylor } else { 251fcca7b38fcc90a781f1507a0a135a64e3ae8f6d8Tom Taylor String recipient = getRecipients(uri); 252fcca7b38fcc90a781f1507a0a135a64e3ae8f6d8Tom Taylor incomingRecipient = ContactList.getByNumbers(recipient, 253fcca7b38fcc90a781f1507a0a135a64e3ae8f6d8Tom Taylor false /* don't block */, false /* don't replace number */); 254d62ef06699b3ca5048c5642bd50300e9a2eb04a1Tom Taylor } 255fcca7b38fcc90a781f1507a0a135a64e3ae8f6d8Tom Taylor if (DEBUG) Log.v(TAG, "sameRecipient incomingRecipient: " + incomingRecipient + 256fcca7b38fcc90a781f1507a0a135a64e3ae8f6d8Tom Taylor " mRecipients: " + mRecipients); 257d62ef06699b3ca5048c5642bd50300e9a2eb04a1Tom Taylor return mRecipients.equals(incomingRecipient); 258d62ef06699b3ca5048c5642bd50300e9a2eb04a1Tom Taylor } 259d62ef06699b3ca5048c5642bd50300e9a2eb04a1Tom Taylor 260d62ef06699b3ca5048c5642bd50300e9a2eb04a1Tom Taylor /** 26170c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick * Returns a temporary Conversation (not representing one on disk) wrapping 26270c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick * the contents of the provided cursor. The cursor should be the one 263627007213deb59ef938c80353c8f3598b01478b3Wei Huang * returned to your AsyncQueryHandler passed in to {@link #startQueryForAll}. 26470c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick * The recipient list of this conversation can be empty if the results 26570c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick * were not in cache. 26670c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick */ 26770c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick public static Conversation from(Context context, Cursor cursor) { 2686a78e60e584cb440c63198b87f8aba6b4c07f8caTom Taylor // First look in the cache for the Conversation and return that one. That way, all the 2696a78e60e584cb440c63198b87f8aba6b4c07f8caTom Taylor // people that are looking at the cached copy will get updated when fillFromCursor() is 2706a78e60e584cb440c63198b87f8aba6b4c07f8caTom Taylor // called with this cursor. 2716a78e60e584cb440c63198b87f8aba6b4c07f8caTom Taylor long threadId = cursor.getLong(ID); 2726a78e60e584cb440c63198b87f8aba6b4c07f8caTom Taylor if (threadId > 0) { 2736a78e60e584cb440c63198b87f8aba6b4c07f8caTom Taylor Conversation conv = Cache.get(threadId); 2746a78e60e584cb440c63198b87f8aba6b4c07f8caTom Taylor if (conv != null) { 2756a78e60e584cb440c63198b87f8aba6b4c07f8caTom Taylor fillFromCursor(context, conv, cursor, false); // update the existing conv in-place 2766a78e60e584cb440c63198b87f8aba6b4c07f8caTom Taylor return conv; 2776a78e60e584cb440c63198b87f8aba6b4c07f8caTom Taylor } 2786a78e60e584cb440c63198b87f8aba6b4c07f8caTom Taylor } 2796a78e60e584cb440c63198b87f8aba6b4c07f8caTom Taylor Conversation conv = new Conversation(context, cursor, false); 2806a78e60e584cb440c63198b87f8aba6b4c07f8caTom Taylor try { 2816a78e60e584cb440c63198b87f8aba6b4c07f8caTom Taylor Cache.put(conv); 2826a78e60e584cb440c63198b87f8aba6b4c07f8caTom Taylor } catch (IllegalStateException e) { 283c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor LogTag.error(TAG, "Tried to add duplicate Conversation to Cache (from cursor): " + 284c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor conv); 285c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor if (!Cache.replace(conv)) { 286c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor LogTag.error("Converations.from cache.replace failed on " + conv); 287c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor } 2886a78e60e584cb440c63198b87f8aba6b4c07f8caTom Taylor } 2896a78e60e584cb440c63198b87f8aba6b4c07f8caTom Taylor return conv; 29070c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick } 29171bbae69cee0da902032743d0702e283cfe31504Ficus Kirkpatrick 2925893069282b516db7da5e16eef051cd02508eb2aTom Taylor private void buildReadContentValues() { 2936bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor if (sReadContentValues == null) { 2946bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor sReadContentValues = new ContentValues(2); 2956bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor sReadContentValues.put("read", 1); 2966bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor sReadContentValues.put("seen", 1); 2975893069282b516db7da5e16eef051cd02508eb2aTom Taylor } 2985893069282b516db7da5e16eef051cd02508eb2aTom Taylor } 2995893069282b516db7da5e16eef051cd02508eb2aTom Taylor 30070c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick /** 301ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick * Marks all messages in this conversation as read and updates 302ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick * relevant notifications. This method returns immediately; 3036a53d30825817a8e70a3da0e591449377306959aTom Taylor * work is dispatched to a background thread. This function should 3046a53d30825817a8e70a3da0e591449377306959aTom Taylor * always be called from the UI thread. 305ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick */ 306436042159134e8ecbc57097340a5bd81f2912574Tom Taylor public void markAsRead() { 307d645c8b53ae904bc059ee1ca7232916637c223e5Tom Taylor if (DELETEDEBUG) { 308d645c8b53ae904bc059ee1ca7232916637c223e5Tom Taylor Contact.logWithTrace(TAG, "markAsRead mMarkAsReadWaiting: " + mMarkAsReadWaiting + 309d645c8b53ae904bc059ee1ca7232916637c223e5Tom Taylor " mMarkAsReadBlocked: " + mMarkAsReadBlocked); 310d645c8b53ae904bc059ee1ca7232916637c223e5Tom Taylor } 3116a53d30825817a8e70a3da0e591449377306959aTom Taylor if (mMarkAsReadWaiting) { 3126a53d30825817a8e70a3da0e591449377306959aTom Taylor // We've already been asked to mark everything as read, but we're blocked. 3136a53d30825817a8e70a3da0e591449377306959aTom Taylor return; 3146a53d30825817a8e70a3da0e591449377306959aTom Taylor } 3156a53d30825817a8e70a3da0e591449377306959aTom Taylor if (mMarkAsReadBlocked) { 3166a53d30825817a8e70a3da0e591449377306959aTom Taylor // We're blocked so record the fact that we want to mark the messages as read 3176a53d30825817a8e70a3da0e591449377306959aTom Taylor // when we get unblocked. 3186a53d30825817a8e70a3da0e591449377306959aTom Taylor mMarkAsReadWaiting = true; 3196a53d30825817a8e70a3da0e591449377306959aTom Taylor return; 3206a53d30825817a8e70a3da0e591449377306959aTom Taylor } 3216a53d30825817a8e70a3da0e591449377306959aTom Taylor final Uri threadUri = getUri(); 322fd36e337347b5f9a945806961d61f1c0b8b3514eTom Taylor 323fd36e337347b5f9a945806961d61f1c0b8b3514eTom Taylor new AsyncTask<Void, Void, Void>() { 324fd36e337347b5f9a945806961d61f1c0b8b3514eTom Taylor protected Void doInBackground(Void... none) { 325d645c8b53ae904bc059ee1ca7232916637c223e5Tom Taylor if (DELETEDEBUG || Log.isLoggable(LogTag.APP, Log.VERBOSE)) { 326d645c8b53ae904bc059ee1ca7232916637c223e5Tom Taylor LogTag.debug("markAsRead.doInBackground"); 327fd36e337347b5f9a945806961d61f1c0b8b3514eTom Taylor } 328fd36e337347b5f9a945806961d61f1c0b8b3514eTom Taylor // If we have no Uri to mark (as in the case of a conversation that 329fd36e337347b5f9a945806961d61f1c0b8b3514eTom Taylor // has not yet made its way to disk), there's nothing to do. 330fd36e337347b5f9a945806961d61f1c0b8b3514eTom Taylor if (threadUri != null) { 3316a53d30825817a8e70a3da0e591449377306959aTom Taylor buildReadContentValues(); 332e14b79584cca1bf7ba60c53bd7e3d6386adbfc59Wei Huang 3336a53d30825817a8e70a3da0e591449377306959aTom Taylor // Check the read flag first. It's much faster to do a query than 3346a53d30825817a8e70a3da0e591449377306959aTom Taylor // to do an update. Timing this function show it's about 10x faster to 3356a53d30825817a8e70a3da0e591449377306959aTom Taylor // do the query compared to the update, even when there's nothing to 3366a53d30825817a8e70a3da0e591449377306959aTom Taylor // update. 3376a53d30825817a8e70a3da0e591449377306959aTom Taylor boolean needUpdate = true; 338e14b79584cca1bf7ba60c53bd7e3d6386adbfc59Wei Huang 3396a53d30825817a8e70a3da0e591449377306959aTom Taylor Cursor c = mContext.getContentResolver().query(threadUri, 3406a53d30825817a8e70a3da0e591449377306959aTom Taylor UNREAD_PROJECTION, UNREAD_SELECTION, null, null); 3416a53d30825817a8e70a3da0e591449377306959aTom Taylor if (c != null) { 3426a53d30825817a8e70a3da0e591449377306959aTom Taylor try { 3436a53d30825817a8e70a3da0e591449377306959aTom Taylor needUpdate = c.getCount() > 0; 3446a53d30825817a8e70a3da0e591449377306959aTom Taylor } finally { 3456a53d30825817a8e70a3da0e591449377306959aTom Taylor c.close(); 346436042159134e8ecbc57097340a5bd81f2912574Tom Taylor } 3476a53d30825817a8e70a3da0e591449377306959aTom Taylor } 348e14b79584cca1bf7ba60c53bd7e3d6386adbfc59Wei Huang 3496a53d30825817a8e70a3da0e591449377306959aTom Taylor if (needUpdate) { 3506a53d30825817a8e70a3da0e591449377306959aTom Taylor LogTag.debug("markAsRead: update read/seen for thread uri: " + 3516a53d30825817a8e70a3da0e591449377306959aTom Taylor threadUri); 3526bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor mContext.getContentResolver().update(threadUri, sReadContentValues, 3536a53d30825817a8e70a3da0e591449377306959aTom Taylor UNREAD_SELECTION, null); 354436042159134e8ecbc57097340a5bd81f2912574Tom Taylor } 3556a53d30825817a8e70a3da0e591449377306959aTom Taylor setHasUnreadMessages(false); 3565893069282b516db7da5e16eef051cd02508eb2aTom Taylor } 357c6be7e1a81769764ce0b51ce03406ad96944d8e5Tom Taylor // Always update notifications regardless of the read state, which is usually 358c6be7e1a81769764ce0b51ce03406ad96944d8e5Tom Taylor // canceling the notification of the thread that was just marked read. 359d645c8b53ae904bc059ee1ca7232916637c223e5Tom Taylor MessagingNotification.blockingUpdateAllNotifications(mContext, 360d645c8b53ae904bc059ee1ca7232916637c223e5Tom Taylor MessagingNotification.THREAD_NONE); 361fd36e337347b5f9a945806961d61f1c0b8b3514eTom Taylor 362fd36e337347b5f9a945806961d61f1c0b8b3514eTom Taylor return null; 363fd36e337347b5f9a945806961d61f1c0b8b3514eTom Taylor } 364fd36e337347b5f9a945806961d61f1c0b8b3514eTom Taylor }.execute(); 365ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick } 36671bbae69cee0da902032743d0702e283cfe31504Ficus Kirkpatrick 3676a53d30825817a8e70a3da0e591449377306959aTom Taylor /** 3686a53d30825817a8e70a3da0e591449377306959aTom Taylor * Call this with false to prevent marking messages as read. The code calls this so 3696a53d30825817a8e70a3da0e591449377306959aTom Taylor * the DB queries in markAsRead don't slow down the main query for messages. Once we've 3706a53d30825817a8e70a3da0e591449377306959aTom Taylor * queried for all the messages (see ComposeMessageActivity.onQueryComplete), then we 3716a53d30825817a8e70a3da0e591449377306959aTom Taylor * can mark messages as read. Only call this function on the UI thread. 3726a53d30825817a8e70a3da0e591449377306959aTom Taylor */ 373436042159134e8ecbc57097340a5bd81f2912574Tom Taylor public void blockMarkAsRead(boolean block) { 374d645c8b53ae904bc059ee1ca7232916637c223e5Tom Taylor if (DELETEDEBUG || Log.isLoggable(LogTag.APP, Log.VERBOSE)) { 375436042159134e8ecbc57097340a5bd81f2912574Tom Taylor LogTag.debug("blockMarkAsRead: " + block); 376436042159134e8ecbc57097340a5bd81f2912574Tom Taylor } 377436042159134e8ecbc57097340a5bd81f2912574Tom Taylor 3786a53d30825817a8e70a3da0e591449377306959aTom Taylor if (block != mMarkAsReadBlocked) { 3796a53d30825817a8e70a3da0e591449377306959aTom Taylor mMarkAsReadBlocked = block; 3806a53d30825817a8e70a3da0e591449377306959aTom Taylor if (!mMarkAsReadBlocked) { 3816a53d30825817a8e70a3da0e591449377306959aTom Taylor if (mMarkAsReadWaiting) { 3826a53d30825817a8e70a3da0e591449377306959aTom Taylor mMarkAsReadWaiting = false; 3836a53d30825817a8e70a3da0e591449377306959aTom Taylor markAsRead(); 384436042159134e8ecbc57097340a5bd81f2912574Tom Taylor } 385436042159134e8ecbc57097340a5bd81f2912574Tom Taylor } 386436042159134e8ecbc57097340a5bd81f2912574Tom Taylor } 387436042159134e8ecbc57097340a5bd81f2912574Tom Taylor } 388436042159134e8ecbc57097340a5bd81f2912574Tom Taylor 389ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick /** 390ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick * Returns a content:// URI referring to this conversation, 391ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick * or null if it does not exist on disk yet. 392ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick */ 39370c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick public synchronized Uri getUri() { 394ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick if (mThreadId <= 0) 395ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick return null; 39671bbae69cee0da902032743d0702e283cfe31504Ficus Kirkpatrick 397ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick return ContentUris.withAppendedId(Threads.CONTENT_URI, mThreadId); 398ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick } 39971bbae69cee0da902032743d0702e283cfe31504Ficus Kirkpatrick 400ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick /** 40170c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick * Return the Uri for all messages in the given thread ID. 40270c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick * @deprecated 40370c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick */ 40470c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick public static Uri getUri(long threadId) { 40570c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick // TODO: Callers using this should really just have a Conversation 40670c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick // and call getUri() on it, but this guarantees no blocking. 40770c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick return ContentUris.withAppendedId(Threads.CONTENT_URI, threadId); 40870c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick } 40971bbae69cee0da902032743d0702e283cfe31504Ficus Kirkpatrick 41070c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick /** 411ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick * Returns the thread ID of this conversation. Can be zero if 412e37a39111cf1e43107308e607c1c955989887c40Wei Huang * {@link #ensureThreadId} has not been called yet. 413ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick */ 41470c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick public synchronized long getThreadId() { 415ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick return mThreadId; 416ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick } 417ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick 418ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick /** 419ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick * Guarantees that the conversation has been created in the database. 420ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick * This will make a blocking database call if it hasn't. 42171bbae69cee0da902032743d0702e283cfe31504Ficus Kirkpatrick * 422ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick * @return The thread ID of this conversation in the database 423ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick */ 42470c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick public synchronized long ensureThreadId() { 425fef537959e6beb02279e4994934ec88df080846fTom Taylor if (DEBUG || DELETEDEBUG) { 426d62ef06699b3ca5048c5642bd50300e9a2eb04a1Tom Taylor LogTag.debug("ensureThreadId before: " + mThreadId); 42707ce1878a36d2df1707dd4bbd9cd7235679bdc94Tom Taylor } 428ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick if (mThreadId <= 0) { 429ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick mThreadId = getOrCreateThreadId(mContext, mRecipients); 430ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick } 431fef537959e6beb02279e4994934ec88df080846fTom Taylor if (DEBUG || DELETEDEBUG) { 432d62ef06699b3ca5048c5642bd50300e9a2eb04a1Tom Taylor LogTag.debug("ensureThreadId after: " + mThreadId); 43307ce1878a36d2df1707dd4bbd9cd7235679bdc94Tom Taylor } 43471bbae69cee0da902032743d0702e283cfe31504Ficus Kirkpatrick 435ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick return mThreadId; 436ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick } 437338855e07bb9c998705d4e6680f696888ca3d0e5Tom Taylor 438926da0d5c27e8d0e4246ea975c2226b83a81a5d3Tom Taylor public synchronized void clearThreadId() { 43907ce1878a36d2df1707dd4bbd9cd7235679bdc94Tom Taylor // remove ourself from the cache 440d62ef06699b3ca5048c5642bd50300e9a2eb04a1Tom Taylor if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) { 441d62ef06699b3ca5048c5642bd50300e9a2eb04a1Tom Taylor LogTag.debug("clearThreadId old threadId was: " + mThreadId + " now zero"); 442d62ef06699b3ca5048c5642bd50300e9a2eb04a1Tom Taylor } 44307ce1878a36d2df1707dd4bbd9cd7235679bdc94Tom Taylor Cache.remove(mThreadId); 44407ce1878a36d2df1707dd4bbd9cd7235679bdc94Tom Taylor 445926da0d5c27e8d0e4246ea975c2226b83a81a5d3Tom Taylor mThreadId = 0; 446338855e07bb9c998705d4e6680f696888ca3d0e5Tom Taylor } 44771bbae69cee0da902032743d0702e283cfe31504Ficus Kirkpatrick 448ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick /** 449ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick * Sets the list of recipients associated with this conversation. 450e37a39111cf1e43107308e607c1c955989887c40Wei Huang * If called, {@link #ensureThreadId} must be called before the next 451ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick * operation that depends on this conversation existing in the 452ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick * database (e.g. storing a draft message to it). 453ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick */ 4541d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick public synchronized void setRecipients(ContactList list) { 45510ca1d3969305df50fb07a17f5d23b0ed59f7868Tom Taylor if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) { 4565a9381876ce766cc761c3e6ed2ea9a67e19bd716Tom Taylor Log.d(TAG, "setRecipients before: " + this.toString()); 45710ca1d3969305df50fb07a17f5d23b0ed59f7868Tom Taylor } 458ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick mRecipients = list; 459ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick 460ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick // Invalidate thread ID because the recipient set has changed. 461ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick mThreadId = 0; 46210ca1d3969305df50fb07a17f5d23b0ed59f7868Tom Taylor 46310ca1d3969305df50fb07a17f5d23b0ed59f7868Tom Taylor if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) { 4645a9381876ce766cc761c3e6ed2ea9a67e19bd716Tom Taylor Log.d(TAG, "setRecipients after: " + this.toString()); 46510ca1d3969305df50fb07a17f5d23b0ed59f7868Tom Taylor } 466fef537959e6beb02279e4994934ec88df080846fTom Taylor } 46771bbae69cee0da902032743d0702e283cfe31504Ficus Kirkpatrick 468ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick /** 469ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick * Returns the recipient set of this conversation. 470ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick */ 4711d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick public synchronized ContactList getRecipients() { 472ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick return mRecipients; 473ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick } 47471bbae69cee0da902032743d0702e283cfe31504Ficus Kirkpatrick 475ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick /** 476ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick * Returns true if a draft message exists in this conversation. 477ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick */ 47870c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick public synchronized boolean hasDraft() { 479ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick if (mThreadId <= 0) 480ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick return false; 48171bbae69cee0da902032743d0702e283cfe31504Ficus Kirkpatrick 482ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick return DraftCache.getInstance().hasDraft(mThreadId); 483ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick } 48471bbae69cee0da902032743d0702e283cfe31504Ficus Kirkpatrick 485ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick /** 486ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick * Sets whether or not this conversation has a draft message. 487ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick */ 48870c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick public synchronized void setDraftState(boolean hasDraft) { 489ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick if (mThreadId <= 0) 490ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick return; 49171bbae69cee0da902032743d0702e283cfe31504Ficus Kirkpatrick 492ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick DraftCache.getInstance().setDraftState(mThreadId, hasDraft); 493ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick } 49471bbae69cee0da902032743d0702e283cfe31504Ficus Kirkpatrick 49570c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick /** 49670c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick * Returns the time of the last update to this conversation in milliseconds, 497e37a39111cf1e43107308e607c1c955989887c40Wei Huang * on the {@link System#currentTimeMillis} timebase. 49870c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick */ 49970c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick public synchronized long getDate() { 50070c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick return mDate; 50170c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick } 50271bbae69cee0da902032743d0702e283cfe31504Ficus Kirkpatrick 50370c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick /** 50470c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick * Returns the number of messages in this conversation, excluding the draft 50570c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick * (if it exists). 50670c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick */ 50770c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick public synchronized int getMessageCount() { 50870c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick return mMessageCount; 50970c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick } 510c06913a292eba0dc9c17ca312f837f009b8e46f5Tom Taylor /** 511c06913a292eba0dc9c17ca312f837f009b8e46f5Tom Taylor * Set the number of messages in this conversation, excluding the draft 512c06913a292eba0dc9c17ca312f837f009b8e46f5Tom Taylor * (if it exists). 513c06913a292eba0dc9c17ca312f837f009b8e46f5Tom Taylor */ 514c06913a292eba0dc9c17ca312f837f009b8e46f5Tom Taylor public synchronized void setMessageCount(int cnt) { 515c06913a292eba0dc9c17ca312f837f009b8e46f5Tom Taylor mMessageCount = cnt; 516c06913a292eba0dc9c17ca312f837f009b8e46f5Tom Taylor } 51771bbae69cee0da902032743d0702e283cfe31504Ficus Kirkpatrick 51870c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick /** 51971bbae69cee0da902032743d0702e283cfe31504Ficus Kirkpatrick * Returns a snippet of text from the most recent message in the conversation. 52070c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick */ 52170c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick public synchronized String getSnippet() { 52270c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick return mSnippet; 52370c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick } 52471bbae69cee0da902032743d0702e283cfe31504Ficus Kirkpatrick 52570c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick /** 52670c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick * Returns true if there are any unread messages in the conversation. 52770c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick */ 528e14b79584cca1bf7ba60c53bd7e3d6386adbfc59Wei Huang public boolean hasUnreadMessages() { 529e14b79584cca1bf7ba60c53bd7e3d6386adbfc59Wei Huang synchronized (this) { 530e14b79584cca1bf7ba60c53bd7e3d6386adbfc59Wei Huang return mHasUnreadMessages; 531e14b79584cca1bf7ba60c53bd7e3d6386adbfc59Wei Huang } 532e14b79584cca1bf7ba60c53bd7e3d6386adbfc59Wei Huang } 533e14b79584cca1bf7ba60c53bd7e3d6386adbfc59Wei Huang 534e14b79584cca1bf7ba60c53bd7e3d6386adbfc59Wei Huang private void setHasUnreadMessages(boolean flag) { 535e14b79584cca1bf7ba60c53bd7e3d6386adbfc59Wei Huang synchronized (this) { 536e14b79584cca1bf7ba60c53bd7e3d6386adbfc59Wei Huang mHasUnreadMessages = flag; 537e14b79584cca1bf7ba60c53bd7e3d6386adbfc59Wei Huang } 53870c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick } 53970c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick 54070c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick /** 54170c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick * Returns true if any messages in the conversation have attachments. 54270c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick */ 54370c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick public synchronized boolean hasAttachment() { 54470c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick return mHasAttachment; 54570c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick } 54670c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick 54770c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick /** 54870c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick * Returns true if any messages in the conversation are in an error state. 54970c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick */ 55070c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick public synchronized boolean hasError() { 55170c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick return mHasError; 55270c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick } 55371bbae69cee0da902032743d0702e283cfe31504Ficus Kirkpatrick 554f9d706cfd0c46a74ba3d79e5543f13a225328d30Tom Taylor /** 555f9d706cfd0c46a74ba3d79e5543f13a225328d30Tom Taylor * Returns true if this conversation is selected for a multi-operation. 556f9d706cfd0c46a74ba3d79e5543f13a225328d30Tom Taylor */ 557f9d706cfd0c46a74ba3d79e5543f13a225328d30Tom Taylor public synchronized boolean isChecked() { 558f9d706cfd0c46a74ba3d79e5543f13a225328d30Tom Taylor return mIsChecked; 559f9d706cfd0c46a74ba3d79e5543f13a225328d30Tom Taylor } 560f9d706cfd0c46a74ba3d79e5543f13a225328d30Tom Taylor 561f9d706cfd0c46a74ba3d79e5543f13a225328d30Tom Taylor public synchronized void setIsChecked(boolean isChecked) { 562f9d706cfd0c46a74ba3d79e5543f13a225328d30Tom Taylor mIsChecked = isChecked; 563f9d706cfd0c46a74ba3d79e5543f13a225328d30Tom Taylor } 564f9d706cfd0c46a74ba3d79e5543f13a225328d30Tom Taylor 5651d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick private static long getOrCreateThreadId(Context context, ContactList list) { 566ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick HashSet<String> recipients = new HashSet<String>(); 567baf7fec7d1a5b8d52ae7be04865f9e869742c261repo sync Contact cacheContact = null; 5681d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick for (Contact c : list) { 569715e32f97bd9d8ce4b5ba650b97ba4b137150456Tom Taylor cacheContact = Contact.get(c.getNumber(), false); 570baf7fec7d1a5b8d52ae7be04865f9e869742c261repo sync if (cacheContact != null) { 571baf7fec7d1a5b8d52ae7be04865f9e869742c261repo sync recipients.add(cacheContact.getNumber()); 572baf7fec7d1a5b8d52ae7be04865f9e869742c261repo sync } else { 573baf7fec7d1a5b8d52ae7be04865f9e869742c261repo sync recipients.add(c.getNumber()); 574baf7fec7d1a5b8d52ae7be04865f9e869742c261repo sync } 575ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick } 5766bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor synchronized(sDeletingThreadsLock) { 577fef537959e6beb02279e4994934ec88df080846fTom Taylor if (DELETEDEBUG) { 578fef537959e6beb02279e4994934ec88df080846fTom Taylor ComposeMessageActivity.log("Conversation getOrCreateThreadId for: " + 579fef537959e6beb02279e4994934ec88df080846fTom Taylor list.formatNamesAndNumbers(",") + " sDeletingThreads: " + sDeletingThreads); 580fef537959e6beb02279e4994934ec88df080846fTom Taylor } 5816bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor long now = System.currentTimeMillis(); 5826bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor while (sDeletingThreads) { 5836bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor try { 5846bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor sDeletingThreadsLock.wait(30000); 5856bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor } catch (InterruptedException e) { 5866bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor } 5876bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor if (System.currentTimeMillis() - now > 29000) { 5886bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor // The deleting thread task is stuck or onDeleteComplete wasn't called. 5896bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor // Unjam ourselves. 5906bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor Log.e(TAG, "getOrCreateThreadId timed out waiting for delete to complete", 5916bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor new Exception()); 5926bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor sDeletingThreads = false; 5936bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor break; 5946bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor } 5956bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor } 5966bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor long retVal = Threads.getOrCreateThreadId(context, recipients); 597fef537959e6beb02279e4994934ec88df080846fTom Taylor if (DELETEDEBUG || Log.isLoggable(LogTag.APP, Log.VERBOSE)) { 5986bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor LogTag.debug("[Conversation] getOrCreateThreadId for (%s) returned %d", 5996bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor recipients, retVal); 6006bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor } 6016bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor return retVal; 602d62ef06699b3ca5048c5642bd50300e9a2eb04a1Tom Taylor } 6036bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor } 60409a75ac1d3710e60dbe78ead3dee6863ffb380caWei Huang 6056bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor public static long getOrCreateThreadId(Context context, String address) { 6066bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor synchronized(sDeletingThreadsLock) { 607fef537959e6beb02279e4994934ec88df080846fTom Taylor if (DELETEDEBUG) { 608fef537959e6beb02279e4994934ec88df080846fTom Taylor ComposeMessageActivity.log("Conversation getOrCreateThreadId for: " + 609fef537959e6beb02279e4994934ec88df080846fTom Taylor address + " sDeletingThreads: " + sDeletingThreads); 610fef537959e6beb02279e4994934ec88df080846fTom Taylor } 6116bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor long now = System.currentTimeMillis(); 6126bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor while (sDeletingThreads) { 6136bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor try { 6146bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor sDeletingThreadsLock.wait(30000); 6156bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor } catch (InterruptedException e) { 6166bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor } 6176bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor if (System.currentTimeMillis() - now > 29000) { 6186bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor // The deleting thread task is stuck or onDeleteComplete wasn't called. 6196bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor // Unjam ourselves. 6206bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor Log.e(TAG, "getOrCreateThreadId timed out waiting for delete to complete", 6216bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor new Exception()); 6226bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor sDeletingThreads = false; 6236bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor break; 6246bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor } 6256bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor } 6266bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor long retVal = Threads.getOrCreateThreadId(context, address); 627fef537959e6beb02279e4994934ec88df080846fTom Taylor if (DELETEDEBUG || Log.isLoggable(LogTag.APP, Log.VERBOSE)) { 6286bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor LogTag.debug("[Conversation] getOrCreateThreadId for (%s) returned %d", 6296bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor address, retVal); 6306bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor } 6316bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor return retVal; 6326bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor } 633ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick } 634ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick 63570c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick /* 63670c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick * The primary key of a conversation is its recipient set; override 63770c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick * equals() and hashCode() to just pass through to the internal 63870c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick * recipient sets. 63970c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick */ 64070c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick @Override 64170c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick public synchronized boolean equals(Object obj) { 64270c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick try { 64370c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick Conversation other = (Conversation)obj; 64470c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick return (mRecipients.equals(other.mRecipients)); 64570c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick } catch (ClassCastException e) { 64670c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick return false; 64770c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick } 64870c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick } 64971bbae69cee0da902032743d0702e283cfe31504Ficus Kirkpatrick 650ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick @Override 65170c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick public synchronized int hashCode() { 65270c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick return mRecipients.hashCode(); 65370c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick } 65471bbae69cee0da902032743d0702e283cfe31504Ficus Kirkpatrick 65570c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick @Override 65670c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick public synchronized String toString() { 657ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick return String.format("[%s] (tid %d)", mRecipients.serialize(), mThreadId); 658ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick } 65971bbae69cee0da902032743d0702e283cfe31504Ficus Kirkpatrick 66070c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick /** 66163e395258ae33a101b9634a68b55ec89fac8b580Tom Taylor * Remove any obsolete conversations sitting around on disk. Obsolete threads are threads 66263e395258ae33a101b9634a68b55ec89fac8b580Tom Taylor * that aren't referenced by any message in the pdu or sms tables. 66370c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick */ 66463e395258ae33a101b9634a68b55ec89fac8b580Tom Taylor public static void asyncDeleteObsoleteThreads(AsyncQueryHandler handler, int token) { 66563e395258ae33a101b9634a68b55ec89fac8b580Tom Taylor handler.startDelete(token, null, Threads.OBSOLETE_THREADS_URI, null, null); 66670c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick } 66771bbae69cee0da902032743d0702e283cfe31504Ficus Kirkpatrick 66870c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick /** 66970c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick * Start a query for all conversations in the database on the specified 67070c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick * AsyncQueryHandler. 67171bbae69cee0da902032743d0702e283cfe31504Ficus Kirkpatrick * 67270c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick * @param handler An AsyncQueryHandler that will receive onQueryComplete 67370c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick * upon completion of the query 67470c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick * @param token The token that will be passed to onQueryComplete 67570c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick */ 67670c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick public static void startQueryForAll(AsyncQueryHandler handler, int token) { 67770c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick handler.cancelOperation(token); 6783b21f6ab04db5936d73e9f53032f1587389380ffTom Taylor 6793b21f6ab04db5936d73e9f53032f1587389380ffTom Taylor // This query looks like this in the log: 6803b21f6ab04db5936d73e9f53032f1587389380ffTom Taylor // I/Database( 147): elapsedTime4Sql|/data/data/com.android.providers.telephony/databases/ 6813b21f6ab04db5936d73e9f53032f1587389380ffTom Taylor // mmssms.db|2.253 ms|SELECT _id, date, message_count, recipient_ids, snippet, snippet_cs, 6823b21f6ab04db5936d73e9f53032f1587389380ffTom Taylor // read, error, has_attachment FROM threads ORDER BY date DESC 6833b21f6ab04db5936d73e9f53032f1587389380ffTom Taylor 6842426db8887f7f1c0c93dbab5a10663cb22575ccdTom Taylor startQuery(handler, token, null); 6852426db8887f7f1c0c93dbab5a10663cb22575ccdTom Taylor } 6862426db8887f7f1c0c93dbab5a10663cb22575ccdTom Taylor 6872426db8887f7f1c0c93dbab5a10663cb22575ccdTom Taylor /** 6882426db8887f7f1c0c93dbab5a10663cb22575ccdTom Taylor * Start a query for in the database on the specified AsyncQueryHandler with the specified 6892426db8887f7f1c0c93dbab5a10663cb22575ccdTom Taylor * "where" clause. 6902426db8887f7f1c0c93dbab5a10663cb22575ccdTom Taylor * 6912426db8887f7f1c0c93dbab5a10663cb22575ccdTom Taylor * @param handler An AsyncQueryHandler that will receive onQueryComplete 6922426db8887f7f1c0c93dbab5a10663cb22575ccdTom Taylor * upon completion of the query 6932426db8887f7f1c0c93dbab5a10663cb22575ccdTom Taylor * @param token The token that will be passed to onQueryComplete 6942426db8887f7f1c0c93dbab5a10663cb22575ccdTom Taylor * @param selection A where clause (can be null) to select particular conv items. 6952426db8887f7f1c0c93dbab5a10663cb22575ccdTom Taylor */ 6962426db8887f7f1c0c93dbab5a10663cb22575ccdTom Taylor public static void startQuery(AsyncQueryHandler handler, int token, String selection) { 6972426db8887f7f1c0c93dbab5a10663cb22575ccdTom Taylor handler.cancelOperation(token); 6982426db8887f7f1c0c93dbab5a10663cb22575ccdTom Taylor 6992426db8887f7f1c0c93dbab5a10663cb22575ccdTom Taylor // This query looks like this in the log: 7002426db8887f7f1c0c93dbab5a10663cb22575ccdTom Taylor // I/Database( 147): elapsedTime4Sql|/data/data/com.android.providers.telephony/databases/ 7012426db8887f7f1c0c93dbab5a10663cb22575ccdTom Taylor // mmssms.db|2.253 ms|SELECT _id, date, message_count, recipient_ids, snippet, snippet_cs, 7022426db8887f7f1c0c93dbab5a10663cb22575ccdTom Taylor // read, error, has_attachment FROM threads ORDER BY date DESC 7032426db8887f7f1c0c93dbab5a10663cb22575ccdTom Taylor 70470c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick handler.startQuery(token, null, sAllThreadsUri, 7052426db8887f7f1c0c93dbab5a10663cb22575ccdTom Taylor ALL_THREADS_PROJECTION, selection, null, Conversations.DEFAULT_SORT_ORDER); 70670c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick } 70771bbae69cee0da902032743d0702e283cfe31504Ficus Kirkpatrick 70870c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick /** 70970c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick * Start a delete of the conversation with the specified thread ID. 71070c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick * 71170c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick * @param handler An AsyncQueryHandler that will receive onDeleteComplete 71270c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick * upon completion of the conversation being deleted 71370c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick * @param token The token that will be passed to onDeleteComplete 714475d780550e0034379ed821a90fdbc96d810b72dTom Taylor * @param deleteAll Delete the whole thread including locked messages 7158cc338d324dc4c92b686029064882147e9054f17Tom Taylor * @param threadIds Collection of thread IDs of the conversations to be deleted 71670c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick */ 7176bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor public static void startDelete(ConversationQueryHandler handler, int token, boolean deleteAll, 7188cc338d324dc4c92b686029064882147e9054f17Tom Taylor Collection<Long> threadIds) { 7196bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor synchronized(sDeletingThreadsLock) { 720fef537959e6beb02279e4994934ec88df080846fTom Taylor if (DELETEDEBUG) { 721fef537959e6beb02279e4994934ec88df080846fTom Taylor Log.v(TAG, "Conversation startDelete sDeletingThreads: " + 7228cc338d324dc4c92b686029064882147e9054f17Tom Taylor sDeletingThreads); 723fef537959e6beb02279e4994934ec88df080846fTom Taylor } 7246bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor if (sDeletingThreads) { 7256bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor Log.e(TAG, "startDeleteAll already in the middle of a delete", new Exception()); 7266bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor } 7278cc338d324dc4c92b686029064882147e9054f17Tom Taylor MmsApp.getApplication().getPduLoaderManager().clear(); 7286bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor sDeletingThreads = true; 7297b6fe946f2e1020432e3600c8863f72449cd4e68Tom Taylor 7308cc338d324dc4c92b686029064882147e9054f17Tom Taylor for (long threadId : threadIds) { 7318cc338d324dc4c92b686029064882147e9054f17Tom Taylor Uri uri = ContentUris.withAppendedId(Threads.CONTENT_URI, threadId); 7328cc338d324dc4c92b686029064882147e9054f17Tom Taylor String selection = deleteAll ? null : "locked=0"; 7337b6fe946f2e1020432e3600c8863f72449cd4e68Tom Taylor 7348cc338d324dc4c92b686029064882147e9054f17Tom Taylor handler.setDeleteToken(token); 7358cc338d324dc4c92b686029064882147e9054f17Tom Taylor handler.startDelete(token, new Long(threadId), uri, selection, null); 7368cc338d324dc4c92b686029064882147e9054f17Tom Taylor 7378cc338d324dc4c92b686029064882147e9054f17Tom Taylor DraftCache.getInstance().setDraftState(threadId, false); 7388cc338d324dc4c92b686029064882147e9054f17Tom Taylor } 7396bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor } 74070c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick } 74170c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick 74270c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick /** 74370c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick * Start deleting all conversations in the database. 74470c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick * @param handler An AsyncQueryHandler that will receive onDeleteComplete 74570c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick * upon completion of all conversations being deleted 74670c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick * @param token The token that will be passed to onDeleteComplete 747475d780550e0034379ed821a90fdbc96d810b72dTom Taylor * @param deleteAll Delete the whole thread including locked messages 74870c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick */ 7496bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor public static void startDeleteAll(ConversationQueryHandler handler, int token, 7506bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor boolean deleteAll) { 7516bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor synchronized(sDeletingThreadsLock) { 752fef537959e6beb02279e4994934ec88df080846fTom Taylor if (DELETEDEBUG) { 753fef537959e6beb02279e4994934ec88df080846fTom Taylor Log.v(TAG, "Conversation startDeleteAll sDeletingThreads: " + 754fef537959e6beb02279e4994934ec88df080846fTom Taylor sDeletingThreads); 755fef537959e6beb02279e4994934ec88df080846fTom Taylor } 7566bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor if (sDeletingThreads) { 7576bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor Log.e(TAG, "startDeleteAll already in the middle of a delete", new Exception()); 7586bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor } 7596bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor sDeletingThreads = true; 7606bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor String selection = deleteAll ? null : "locked=0"; 7617b6fe946f2e1020432e3600c8863f72449cd4e68Tom Taylor 762c6be7e1a81769764ce0b51ce03406ad96944d8e5Tom Taylor MmsApp app = MmsApp.getApplication(); 763c6be7e1a81769764ce0b51ce03406ad96944d8e5Tom Taylor app.getPduLoaderManager().clear(); 764c6be7e1a81769764ce0b51ce03406ad96944d8e5Tom Taylor app.getThumbnailManager().clear(); 7657b6fe946f2e1020432e3600c8863f72449cd4e68Tom Taylor 7666bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor handler.setDeleteToken(token); 7676bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor handler.startDelete(token, new Long(-1), Threads.CONTENT_URI, selection, null); 7686bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor } 7696bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor } 7706bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor 7716bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor public static class ConversationQueryHandler extends AsyncQueryHandler { 7726bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor private int mDeleteToken; 7736bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor 7746bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor public ConversationQueryHandler(ContentResolver cr) { 7756bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor super(cr); 7766bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor } 7776bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor 7786bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor public void setDeleteToken(int token) { 7796bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor mDeleteToken = token; 7806bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor } 7816bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor 7826bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor /** 7836bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor * Always call this super method from your overridden onDeleteComplete function. 7846bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor */ 7856bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor @Override 7866bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor protected void onDeleteComplete(int token, Object cookie, int result) { 7876bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor if (token == mDeleteToken) { 7886bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor // Test code 7896bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor// try { 7906bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor// Thread.sleep(10000); 7916bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor// } catch (InterruptedException e) { 7926bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor// } 7936bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor 7946bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor // release lock 7956bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor synchronized(sDeletingThreadsLock) { 7966bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor sDeletingThreads = false; 797fef537959e6beb02279e4994934ec88df080846fTom Taylor if (DELETEDEBUG) { 798fef537959e6beb02279e4994934ec88df080846fTom Taylor Log.v(TAG, "Conversation onDeleteComplete sDeletingThreads: " + 799fef537959e6beb02279e4994934ec88df080846fTom Taylor sDeletingThreads); 800fef537959e6beb02279e4994934ec88df080846fTom Taylor } 8016bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor sDeletingThreadsLock.notifyAll(); 8026bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor } 8036bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor } 8046bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor } 80570c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick } 80671bbae69cee0da902032743d0702e283cfe31504Ficus Kirkpatrick 80770c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick /** 80885fbc94e34688455c3f2f36271ba65b90ceb2542Tom Taylor * Check for locked messages in all threads or a specified thread. 80985fbc94e34688455c3f2f36271ba65b90ceb2542Tom Taylor * @param handler An AsyncQueryHandler that will receive onQueryComplete 81085fbc94e34688455c3f2f36271ba65b90ceb2542Tom Taylor * upon completion of looking for locked messages 811b51ea8318ca2e4019b666d938e3e7efdf6e643b3Tom Taylor * @param threadIds A list of threads to search. null means all threads 81285fbc94e34688455c3f2f36271ba65b90ceb2542Tom Taylor * @param token The token that will be passed to onQueryComplete 81385fbc94e34688455c3f2f36271ba65b90ceb2542Tom Taylor */ 814b51ea8318ca2e4019b666d938e3e7efdf6e643b3Tom Taylor public static void startQueryHaveLockedMessages(AsyncQueryHandler handler, 815b04236e2977ac69e63cc0fa123399a584b606945Tom Taylor Collection<Long> threadIds, 81685fbc94e34688455c3f2f36271ba65b90ceb2542Tom Taylor int token) { 81785fbc94e34688455c3f2f36271ba65b90ceb2542Tom Taylor handler.cancelOperation(token); 81885fbc94e34688455c3f2f36271ba65b90ceb2542Tom Taylor Uri uri = MmsSms.CONTENT_LOCKED_URI; 819b51ea8318ca2e4019b666d938e3e7efdf6e643b3Tom Taylor 820b51ea8318ca2e4019b666d938e3e7efdf6e643b3Tom Taylor String selection = null; 821b51ea8318ca2e4019b666d938e3e7efdf6e643b3Tom Taylor if (threadIds != null) { 822b51ea8318ca2e4019b666d938e3e7efdf6e643b3Tom Taylor StringBuilder buf = new StringBuilder(); 823b51ea8318ca2e4019b666d938e3e7efdf6e643b3Tom Taylor int i = 0; 824b51ea8318ca2e4019b666d938e3e7efdf6e643b3Tom Taylor 825b51ea8318ca2e4019b666d938e3e7efdf6e643b3Tom Taylor for (long threadId : threadIds) { 826b51ea8318ca2e4019b666d938e3e7efdf6e643b3Tom Taylor if (i++ > 0) { 827b51ea8318ca2e4019b666d938e3e7efdf6e643b3Tom Taylor buf.append(" OR "); 828b51ea8318ca2e4019b666d938e3e7efdf6e643b3Tom Taylor } 829b51ea8318ca2e4019b666d938e3e7efdf6e643b3Tom Taylor // We have to build the selection arg into the selection because deep down in 830b51ea8318ca2e4019b666d938e3e7efdf6e643b3Tom Taylor // provider, the function buildUnionSubQuery takes selectionArgs, but ignores it. 831b51ea8318ca2e4019b666d938e3e7efdf6e643b3Tom Taylor buf.append(Mms.THREAD_ID).append("=").append(Long.toString(threadId)); 832b51ea8318ca2e4019b666d938e3e7efdf6e643b3Tom Taylor } 833b51ea8318ca2e4019b666d938e3e7efdf6e643b3Tom Taylor selection = buf.toString(); 834b51ea8318ca2e4019b666d938e3e7efdf6e643b3Tom Taylor } 835b51ea8318ca2e4019b666d938e3e7efdf6e643b3Tom Taylor handler.startQuery(token, threadIds, uri, 836b51ea8318ca2e4019b666d938e3e7efdf6e643b3Tom Taylor ALL_THREADS_PROJECTION, selection, null, Conversations.DEFAULT_SORT_ORDER); 837b51ea8318ca2e4019b666d938e3e7efdf6e643b3Tom Taylor } 838b51ea8318ca2e4019b666d938e3e7efdf6e643b3Tom Taylor 839b51ea8318ca2e4019b666d938e3e7efdf6e643b3Tom Taylor /** 840b51ea8318ca2e4019b666d938e3e7efdf6e643b3Tom Taylor * Check for locked messages in all threads or a specified thread. 841b51ea8318ca2e4019b666d938e3e7efdf6e643b3Tom Taylor * @param handler An AsyncQueryHandler that will receive onQueryComplete 842b51ea8318ca2e4019b666d938e3e7efdf6e643b3Tom Taylor * upon completion of looking for locked messages 843b51ea8318ca2e4019b666d938e3e7efdf6e643b3Tom Taylor * @param threadId The threadId of the thread to search. -1 means all threads 844b51ea8318ca2e4019b666d938e3e7efdf6e643b3Tom Taylor * @param token The token that will be passed to onQueryComplete 845b51ea8318ca2e4019b666d938e3e7efdf6e643b3Tom Taylor */ 846b51ea8318ca2e4019b666d938e3e7efdf6e643b3Tom Taylor public static void startQueryHaveLockedMessages(AsyncQueryHandler handler, 847b51ea8318ca2e4019b666d938e3e7efdf6e643b3Tom Taylor long threadId, 848b51ea8318ca2e4019b666d938e3e7efdf6e643b3Tom Taylor int token) { 849b51ea8318ca2e4019b666d938e3e7efdf6e643b3Tom Taylor ArrayList<Long> threadIds = null; 85085fbc94e34688455c3f2f36271ba65b90ceb2542Tom Taylor if (threadId != -1) { 851b51ea8318ca2e4019b666d938e3e7efdf6e643b3Tom Taylor threadIds = new ArrayList<Long>(); 852b51ea8318ca2e4019b666d938e3e7efdf6e643b3Tom Taylor threadIds.add(threadId); 85385fbc94e34688455c3f2f36271ba65b90ceb2542Tom Taylor } 854b51ea8318ca2e4019b666d938e3e7efdf6e643b3Tom Taylor startQueryHaveLockedMessages(handler, threadIds, token); 85585fbc94e34688455c3f2f36271ba65b90ceb2542Tom Taylor } 85685fbc94e34688455c3f2f36271ba65b90ceb2542Tom Taylor 85785fbc94e34688455c3f2f36271ba65b90ceb2542Tom Taylor /** 85870c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick * Fill the specified conversation with the values from the specified 85970c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick * cursor, possibly setting recipients to empty if {@value allowQuery} 86070c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick * is false and the recipient IDs are not in cache. The cursor should 861e37a39111cf1e43107308e607c1c955989887c40Wei Huang * be one made via {@link #startQueryForAll}. 86270c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick */ 86370c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick private static void fillFromCursor(Context context, Conversation conv, 86470c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick Cursor c, boolean allowQuery) { 86570c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick synchronized (conv) { 866338855e07bb9c998705d4e6680f696888ca3d0e5Tom Taylor conv.mThreadId = c.getLong(ID); 86770c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick conv.mDate = c.getLong(DATE); 86870c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick conv.mMessageCount = c.getInt(MESSAGE_COUNT); 86970c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick 87070c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick // Replace the snippet with a default value if it's empty. 871ff3e6009a8180701d8f344f9c128c79610e8bcfbTom Taylor String snippet = MessageUtils.cleanseMmsSubject(context, 872ff3e6009a8180701d8f344f9c128c79610e8bcfbTom Taylor MessageUtils.extractEncStrFromCursor(c, SNIPPET, SNIPPET_CS)); 87370c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick if (TextUtils.isEmpty(snippet)) { 87470c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick snippet = context.getString(R.string.no_subject_view); 87570c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick } 87670c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick conv.mSnippet = snippet; 87770c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick 878e14b79584cca1bf7ba60c53bd7e3d6386adbfc59Wei Huang conv.setHasUnreadMessages(c.getInt(READ) == 0); 87970c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick conv.mHasError = (c.getInt(ERROR) != 0); 88070c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick conv.mHasAttachment = (c.getInt(HAS_ATTACHMENT) != 0); 8815544f47dbe1ffd4af73b9b9dde0e35cd257cd795Tom Taylor } 8825544f47dbe1ffd4af73b9b9dde0e35cd257cd795Tom Taylor // Fill in as much of the conversation as we can before doing the slow stuff of looking 8835544f47dbe1ffd4af73b9b9dde0e35cd257cd795Tom Taylor // up the contacts associated with this conversation. 8846bbfd941885c588cc5d94e345d2cfc4197134a19Tom Taylor String recipientIds = c.getString(RECIPIENT_IDS); 885e692b9dcf5742e7a3ef3a7e64b44bb9d08f05b57Tom Taylor ContactList recipients = ContactList.getByIds(recipientIds, allowQuery); 8865544f47dbe1ffd4af73b9b9dde0e35cd257cd795Tom Taylor synchronized (conv) { 8875544f47dbe1ffd4af73b9b9dde0e35cd257cd795Tom Taylor conv.mRecipients = recipients; 88870c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick } 88909a75ac1d3710e60dbe78ead3dee6863ffb380caWei Huang 89009a75ac1d3710e60dbe78ead3dee6863ffb380caWei Huang if (Log.isLoggable(LogTag.THREAD_CACHE, Log.VERBOSE)) { 8915a9381876ce766cc761c3e6ed2ea9a67e19bd716Tom Taylor Log.d(TAG, "fillFromCursor: conv=" + conv + ", recipientIds=" + recipientIds); 89209a75ac1d3710e60dbe78ead3dee6863ffb380caWei Huang } 89370c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick } 89470c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick 89570c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick /** 89670c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick * Private cache for the use of the various forms of Conversation.get. 89770c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick */ 89870c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick private static class Cache { 89970c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick private static Cache sInstance = new Cache(); 90070c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick static Cache getInstance() { return sInstance; } 90170c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick private final HashSet<Conversation> mCache; 90270c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick private Cache() { 90370c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick mCache = new HashSet<Conversation>(10); 90470c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick } 90571bbae69cee0da902032743d0702e283cfe31504Ficus Kirkpatrick 90670c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick /** 90770c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick * Return the conversation with the specified thread ID, or 90870c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick * null if it's not in cache. 90970c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick */ 91070c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick static Conversation get(long threadId) { 91170c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick synchronized (sInstance) { 91209a75ac1d3710e60dbe78ead3dee6863ffb380caWei Huang if (Log.isLoggable(LogTag.THREAD_CACHE, Log.VERBOSE)) { 913d62ef06699b3ca5048c5642bd50300e9a2eb04a1Tom Taylor LogTag.debug("Conversation get with threadId: " + threadId); 91407ce1878a36d2df1707dd4bbd9cd7235679bdc94Tom Taylor } 91570c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick for (Conversation c : sInstance.mCache) { 91607ce1878a36d2df1707dd4bbd9cd7235679bdc94Tom Taylor if (DEBUG) { 917d62ef06699b3ca5048c5642bd50300e9a2eb04a1Tom Taylor LogTag.debug("Conversation get() threadId: " + threadId + 91807ce1878a36d2df1707dd4bbd9cd7235679bdc94Tom Taylor " c.getThreadId(): " + c.getThreadId()); 91907ce1878a36d2df1707dd4bbd9cd7235679bdc94Tom Taylor } 92070c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick if (c.getThreadId() == threadId) { 92170c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick return c; 92270c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick } 92370c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick } 92470c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick } 92570c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick return null; 92670c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick } 92771bbae69cee0da902032743d0702e283cfe31504Ficus Kirkpatrick 92870c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick /** 92970c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick * Return the conversation with the specified recipient 93070c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick * list, or null if it's not in cache. 93170c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick */ 9321d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick static Conversation get(ContactList list) { 93370c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick synchronized (sInstance) { 93409a75ac1d3710e60dbe78ead3dee6863ffb380caWei Huang if (Log.isLoggable(LogTag.THREAD_CACHE, Log.VERBOSE)) { 935d62ef06699b3ca5048c5642bd50300e9a2eb04a1Tom Taylor LogTag.debug("Conversation get with ContactList: " + list); 93607ce1878a36d2df1707dd4bbd9cd7235679bdc94Tom Taylor } 93770c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick for (Conversation c : sInstance.mCache) { 9381d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick if (c.getRecipients().equals(list)) { 93970c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick return c; 94070c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick } 94170c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick } 94270c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick } 94370c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick return null; 94470c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick } 94571bbae69cee0da902032743d0702e283cfe31504Ficus Kirkpatrick 94670c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick /** 94770c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick * Put the specified conversation in the cache. The caller 94870c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick * should not place an already-existing conversation in the 94970c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick * cache, but rather update it in place. 95070c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick */ 95170c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick static void put(Conversation c) { 95270c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick synchronized (sInstance) { 95370c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick // We update cache entries in place so people with long- 95470c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick // held references get updated. 95509a75ac1d3710e60dbe78ead3dee6863ffb380caWei Huang if (Log.isLoggable(LogTag.THREAD_CACHE, Log.VERBOSE)) { 9565a9381876ce766cc761c3e6ed2ea9a67e19bd716Tom Taylor Log.d(TAG, "Conversation.Cache.put: conv= " + c + ", hash: " + c.hashCode()); 95707ce1878a36d2df1707dd4bbd9cd7235679bdc94Tom Taylor } 95807ce1878a36d2df1707dd4bbd9cd7235679bdc94Tom Taylor 95970c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick if (sInstance.mCache.contains(c)) { 960c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor if (DEBUG) { 961c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor dumpCache(); 962c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor } 963338855e07bb9c998705d4e6680f696888ca3d0e5Tom Taylor throw new IllegalStateException("cache already contains " + c + 964338855e07bb9c998705d4e6680f696888ca3d0e5Tom Taylor " threadId: " + c.mThreadId); 96570c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick } 96670c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick sInstance.mCache.add(c); 96770c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick } 96870c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick } 96971bbae69cee0da902032743d0702e283cfe31504Ficus Kirkpatrick 970c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor /** 971c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor * Replace the specified conversation in the cache. This is used in cases where we 972c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor * lookup a conversation in the cache by threadId, but don't find it. The caller 973c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor * then builds a new conversation (from the cursor) and tries to add it, but gets 974c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor * an exception that the conversation is already in the cache, because the hash 975c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor * is based on the recipients and it's there under a stale threadId. In this function 976c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor * we remove the stale entry and add the new one. Returns true if the operation is 977c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor * successful 978c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor */ 979c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor static boolean replace(Conversation c) { 980c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor synchronized (sInstance) { 981c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor if (Log.isLoggable(LogTag.THREAD_CACHE, Log.VERBOSE)) { 982c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor LogTag.debug("Conversation.Cache.put: conv= " + c + ", hash: " + c.hashCode()); 983c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor } 984c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor 985c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor if (!sInstance.mCache.contains(c)) { 986c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor if (DEBUG) { 987c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor dumpCache(); 988c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor } 989c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor return false; 990c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor } 991c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor // Here it looks like we're simply removing and then re-adding the same object 992c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor // to the hashset. Because the hashkey is the conversation's recipients, and not 993c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor // the thread id, we'll actually remove the object with the stale threadId and 994c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor // then add the the conversation with updated threadId, both having the same 995c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor // recipients. 996c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor sInstance.mCache.remove(c); 997c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor sInstance.mCache.add(c); 998c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor return true; 999c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor } 1000c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor } 1001c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor 100207ce1878a36d2df1707dd4bbd9cd7235679bdc94Tom Taylor static void remove(long threadId) { 1003e2986af492c30ad92c96bcb7adf8092d4d948565Tom Taylor synchronized (sInstance) { 1004e2986af492c30ad92c96bcb7adf8092d4d948565Tom Taylor if (DEBUG) { 1005e2986af492c30ad92c96bcb7adf8092d4d948565Tom Taylor LogTag.debug("remove threadid: " + threadId); 1006e2986af492c30ad92c96bcb7adf8092d4d948565Tom Taylor dumpCache(); 1007e2986af492c30ad92c96bcb7adf8092d4d948565Tom Taylor } 1008e2986af492c30ad92c96bcb7adf8092d4d948565Tom Taylor for (Conversation c : sInstance.mCache) { 1009e2986af492c30ad92c96bcb7adf8092d4d948565Tom Taylor if (c.getThreadId() == threadId) { 1010e2986af492c30ad92c96bcb7adf8092d4d948565Tom Taylor sInstance.mCache.remove(c); 1011e2986af492c30ad92c96bcb7adf8092d4d948565Tom Taylor return; 1012e2986af492c30ad92c96bcb7adf8092d4d948565Tom Taylor } 101307ce1878a36d2df1707dd4bbd9cd7235679bdc94Tom Taylor } 101407ce1878a36d2df1707dd4bbd9cd7235679bdc94Tom Taylor } 101507ce1878a36d2df1707dd4bbd9cd7235679bdc94Tom Taylor } 101607ce1878a36d2df1707dd4bbd9cd7235679bdc94Tom Taylor 101707ce1878a36d2df1707dd4bbd9cd7235679bdc94Tom Taylor static void dumpCache() { 101809a75ac1d3710e60dbe78ead3dee6863ffb380caWei Huang synchronized (sInstance) { 101909a75ac1d3710e60dbe78ead3dee6863ffb380caWei Huang LogTag.debug("Conversation dumpCache: "); 102009a75ac1d3710e60dbe78ead3dee6863ffb380caWei Huang for (Conversation c : sInstance.mCache) { 102109a75ac1d3710e60dbe78ead3dee6863ffb380caWei Huang LogTag.debug(" conv: " + c.toString() + " hash: " + c.hashCode()); 102207ce1878a36d2df1707dd4bbd9cd7235679bdc94Tom Taylor } 102307ce1878a36d2df1707dd4bbd9cd7235679bdc94Tom Taylor } 102407ce1878a36d2df1707dd4bbd9cd7235679bdc94Tom Taylor } 102507ce1878a36d2df1707dd4bbd9cd7235679bdc94Tom Taylor 102670c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick /** 102770c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick * Remove all conversations from the cache that are not in 102870c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick * the provided set of thread IDs. 102970c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick */ 103070c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick static void keepOnly(Set<Long> threads) { 103170c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick synchronized (sInstance) { 103270c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick Iterator<Conversation> iter = sInstance.mCache.iterator(); 103370c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick while (iter.hasNext()) { 103470c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick Conversation c = iter.next(); 103570c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick if (!threads.contains(c.getThreadId())) { 103670c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick iter.remove(); 103770c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick } 103870c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick } 103970c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick } 10400f1fb760aa6a1ae040703b8ce405c96923e40603Tom Taylor if (DEBUG) { 10410f1fb760aa6a1ae040703b8ce405c96923e40603Tom Taylor LogTag.debug("after keepOnly"); 10420f1fb760aa6a1ae040703b8ce405c96923e40603Tom Taylor dumpCache(); 10430f1fb760aa6a1ae040703b8ce405c96923e40603Tom Taylor } 104470c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick } 104570c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick } 104671bbae69cee0da902032743d0702e283cfe31504Ficus Kirkpatrick 104770c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick /** 104870c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick * Set up the conversation cache. To be called once at application 104970c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick * startup time. 105070c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick */ 105170c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick public static void init(final Context context) { 1052ff013e77fd2e30fe98f8935ae04802a2b7fbf714Todor Kalaydjiev Thread thread = new Thread(new Runnable() { 1053ff013e77fd2e30fe98f8935ae04802a2b7fbf714Todor Kalaydjiev @Override 1054ff013e77fd2e30fe98f8935ae04802a2b7fbf714Todor Kalaydjiev public void run() { 1055ff013e77fd2e30fe98f8935ae04802a2b7fbf714Todor Kalaydjiev cacheAllThreads(context); 1056ff013e77fd2e30fe98f8935ae04802a2b7fbf714Todor Kalaydjiev } 1057ff013e77fd2e30fe98f8935ae04802a2b7fbf714Todor Kalaydjiev }, "Conversation.init"); 1058ff013e77fd2e30fe98f8935ae04802a2b7fbf714Todor Kalaydjiev thread.setPriority(Thread.MIN_PRIORITY); 1059ff013e77fd2e30fe98f8935ae04802a2b7fbf714Todor Kalaydjiev thread.start(); 106070c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick } 10611d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick 1062627007213deb59ef938c80353c8f3598b01478b3Wei Huang public static void markAllConversationsAsSeen(final Context context) { 1063d645c8b53ae904bc059ee1ca7232916637c223e5Tom Taylor if (DELETEDEBUG || DEBUG) { 1064d645c8b53ae904bc059ee1ca7232916637c223e5Tom Taylor Contact.logWithTrace(TAG, "Conversation.markAllConversationsAsSeen"); 1065627007213deb59ef938c80353c8f3598b01478b3Wei Huang } 1066627007213deb59ef938c80353c8f3598b01478b3Wei Huang 10678112b6847de98297068915e7d95a87a8dc842c64Todor Kalaydjiev Thread thread = new Thread(new Runnable() { 1068ddd31c4011b4191035bdfbba05a8edb1785f71afTodor Kalaydjiev @Override 1069627007213deb59ef938c80353c8f3598b01478b3Wei Huang public void run() { 1070d645c8b53ae904bc059ee1ca7232916637c223e5Tom Taylor if (DELETEDEBUG) { 1071d645c8b53ae904bc059ee1ca7232916637c223e5Tom Taylor Log.d(TAG, "Conversation.markAllConversationsAsSeen.run"); 1072d645c8b53ae904bc059ee1ca7232916637c223e5Tom Taylor } 1073627007213deb59ef938c80353c8f3598b01478b3Wei Huang blockingMarkAllSmsMessagesAsSeen(context); 1074627007213deb59ef938c80353c8f3598b01478b3Wei Huang blockingMarkAllMmsMessagesAsSeen(context); 1075627007213deb59ef938c80353c8f3598b01478b3Wei Huang 1076627007213deb59ef938c80353c8f3598b01478b3Wei Huang // Always update notifications regardless of the read state. 1077bc7f97ae7d3661a44211d404fe4e187c35afadceTom Taylor MessagingNotification.blockingUpdateAllNotifications(context, 1078bc7f97ae7d3661a44211d404fe4e187c35afadceTom Taylor MessagingNotification.THREAD_NONE); 1079627007213deb59ef938c80353c8f3598b01478b3Wei Huang } 10808112b6847de98297068915e7d95a87a8dc842c64Todor Kalaydjiev }, "Conversation.markAllConversationsAsSeen"); 10818112b6847de98297068915e7d95a87a8dc842c64Todor Kalaydjiev thread.setPriority(Thread.MIN_PRIORITY); 10828112b6847de98297068915e7d95a87a8dc842c64Todor Kalaydjiev thread.start(); 1083627007213deb59ef938c80353c8f3598b01478b3Wei Huang } 1084627007213deb59ef938c80353c8f3598b01478b3Wei Huang 1085627007213deb59ef938c80353c8f3598b01478b3Wei Huang private static void blockingMarkAllSmsMessagesAsSeen(final Context context) { 1086627007213deb59ef938c80353c8f3598b01478b3Wei Huang ContentResolver resolver = context.getContentResolver(); 1087f7e8281a223af6228e6399055a6197a1edd9bc3aTom Taylor Cursor cursor = resolver.query(Sms.Inbox.CONTENT_URI, 1088627007213deb59ef938c80353c8f3598b01478b3Wei Huang SEEN_PROJECTION, 1089627007213deb59ef938c80353c8f3598b01478b3Wei Huang "seen=0", 1090627007213deb59ef938c80353c8f3598b01478b3Wei Huang null, 1091627007213deb59ef938c80353c8f3598b01478b3Wei Huang null); 1092627007213deb59ef938c80353c8f3598b01478b3Wei Huang 1093627007213deb59ef938c80353c8f3598b01478b3Wei Huang int count = 0; 1094627007213deb59ef938c80353c8f3598b01478b3Wei Huang 1095627007213deb59ef938c80353c8f3598b01478b3Wei Huang if (cursor != null) { 1096627007213deb59ef938c80353c8f3598b01478b3Wei Huang try { 1097627007213deb59ef938c80353c8f3598b01478b3Wei Huang count = cursor.getCount(); 1098627007213deb59ef938c80353c8f3598b01478b3Wei Huang } finally { 1099627007213deb59ef938c80353c8f3598b01478b3Wei Huang cursor.close(); 1100627007213deb59ef938c80353c8f3598b01478b3Wei Huang } 1101627007213deb59ef938c80353c8f3598b01478b3Wei Huang } 1102627007213deb59ef938c80353c8f3598b01478b3Wei Huang 1103627007213deb59ef938c80353c8f3598b01478b3Wei Huang if (count == 0) { 1104627007213deb59ef938c80353c8f3598b01478b3Wei Huang return; 1105627007213deb59ef938c80353c8f3598b01478b3Wei Huang } 1106627007213deb59ef938c80353c8f3598b01478b3Wei Huang 1107d645c8b53ae904bc059ee1ca7232916637c223e5Tom Taylor if (DELETEDEBUG || Log.isLoggable(LogTag.APP, Log.VERBOSE)) { 1108627007213deb59ef938c80353c8f3598b01478b3Wei Huang Log.d(TAG, "mark " + count + " SMS msgs as seen"); 1109627007213deb59ef938c80353c8f3598b01478b3Wei Huang } 1110627007213deb59ef938c80353c8f3598b01478b3Wei Huang 1111627007213deb59ef938c80353c8f3598b01478b3Wei Huang ContentValues values = new ContentValues(1); 1112627007213deb59ef938c80353c8f3598b01478b3Wei Huang values.put("seen", 1); 1113627007213deb59ef938c80353c8f3598b01478b3Wei Huang 1114f7e8281a223af6228e6399055a6197a1edd9bc3aTom Taylor resolver.update(Sms.Inbox.CONTENT_URI, 1115627007213deb59ef938c80353c8f3598b01478b3Wei Huang values, 1116627007213deb59ef938c80353c8f3598b01478b3Wei Huang "seen=0", 1117627007213deb59ef938c80353c8f3598b01478b3Wei Huang null); 1118627007213deb59ef938c80353c8f3598b01478b3Wei Huang } 1119627007213deb59ef938c80353c8f3598b01478b3Wei Huang 1120627007213deb59ef938c80353c8f3598b01478b3Wei Huang private static void blockingMarkAllMmsMessagesAsSeen(final Context context) { 1121627007213deb59ef938c80353c8f3598b01478b3Wei Huang ContentResolver resolver = context.getContentResolver(); 1122f7e8281a223af6228e6399055a6197a1edd9bc3aTom Taylor Cursor cursor = resolver.query(Mms.Inbox.CONTENT_URI, 1123627007213deb59ef938c80353c8f3598b01478b3Wei Huang SEEN_PROJECTION, 1124627007213deb59ef938c80353c8f3598b01478b3Wei Huang "seen=0", 1125627007213deb59ef938c80353c8f3598b01478b3Wei Huang null, 1126627007213deb59ef938c80353c8f3598b01478b3Wei Huang null); 1127627007213deb59ef938c80353c8f3598b01478b3Wei Huang 1128627007213deb59ef938c80353c8f3598b01478b3Wei Huang int count = 0; 1129627007213deb59ef938c80353c8f3598b01478b3Wei Huang 1130627007213deb59ef938c80353c8f3598b01478b3Wei Huang if (cursor != null) { 1131627007213deb59ef938c80353c8f3598b01478b3Wei Huang try { 1132627007213deb59ef938c80353c8f3598b01478b3Wei Huang count = cursor.getCount(); 1133627007213deb59ef938c80353c8f3598b01478b3Wei Huang } finally { 1134627007213deb59ef938c80353c8f3598b01478b3Wei Huang cursor.close(); 1135627007213deb59ef938c80353c8f3598b01478b3Wei Huang } 1136627007213deb59ef938c80353c8f3598b01478b3Wei Huang } 1137627007213deb59ef938c80353c8f3598b01478b3Wei Huang 1138627007213deb59ef938c80353c8f3598b01478b3Wei Huang if (count == 0) { 1139627007213deb59ef938c80353c8f3598b01478b3Wei Huang return; 1140627007213deb59ef938c80353c8f3598b01478b3Wei Huang } 1141627007213deb59ef938c80353c8f3598b01478b3Wei Huang 1142d645c8b53ae904bc059ee1ca7232916637c223e5Tom Taylor if (DELETEDEBUG || Log.isLoggable(LogTag.APP, Log.VERBOSE)) { 1143627007213deb59ef938c80353c8f3598b01478b3Wei Huang Log.d(TAG, "mark " + count + " MMS msgs as seen"); 1144627007213deb59ef938c80353c8f3598b01478b3Wei Huang } 1145627007213deb59ef938c80353c8f3598b01478b3Wei Huang 1146627007213deb59ef938c80353c8f3598b01478b3Wei Huang ContentValues values = new ContentValues(1); 1147627007213deb59ef938c80353c8f3598b01478b3Wei Huang values.put("seen", 1); 1148627007213deb59ef938c80353c8f3598b01478b3Wei Huang 1149f7e8281a223af6228e6399055a6197a1edd9bc3aTom Taylor resolver.update(Mms.Inbox.CONTENT_URI, 1150627007213deb59ef938c80353c8f3598b01478b3Wei Huang values, 1151627007213deb59ef938c80353c8f3598b01478b3Wei Huang "seen=0", 1152627007213deb59ef938c80353c8f3598b01478b3Wei Huang null); 1153627007213deb59ef938c80353c8f3598b01478b3Wei Huang 1154627007213deb59ef938c80353c8f3598b01478b3Wei Huang } 1155627007213deb59ef938c80353c8f3598b01478b3Wei Huang 1156445fb5abf8804279e591d1c35657c9162625135eTom Taylor /** 1157445fb5abf8804279e591d1c35657c9162625135eTom Taylor * Are we in the process of loading and caching all the threads?. 1158445fb5abf8804279e591d1c35657c9162625135eTom Taylor */ 1159627007213deb59ef938c80353c8f3598b01478b3Wei Huang public static boolean loadingThreads() { 1160627007213deb59ef938c80353c8f3598b01478b3Wei Huang synchronized (Cache.getInstance()) { 11616bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor return sLoadingThreads; 1162627007213deb59ef938c80353c8f3598b01478b3Wei Huang } 1163627007213deb59ef938c80353c8f3598b01478b3Wei Huang } 1164627007213deb59ef938c80353c8f3598b01478b3Wei Huang 1165627007213deb59ef938c80353c8f3598b01478b3Wei Huang private static void cacheAllThreads(Context context) { 116609a75ac1d3710e60dbe78ead3dee6863ffb380caWei Huang if (Log.isLoggable(LogTag.THREAD_CACHE, Log.VERBOSE)) { 116709a75ac1d3710e60dbe78ead3dee6863ffb380caWei Huang LogTag.debug("[Conversation] cacheAllThreads: begin"); 1168627007213deb59ef938c80353c8f3598b01478b3Wei Huang } 1169627007213deb59ef938c80353c8f3598b01478b3Wei Huang synchronized (Cache.getInstance()) { 11706bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor if (sLoadingThreads) { 1171627007213deb59ef938c80353c8f3598b01478b3Wei Huang return; 1172627007213deb59ef938c80353c8f3598b01478b3Wei Huang } 11736bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor sLoadingThreads = true; 1174627007213deb59ef938c80353c8f3598b01478b3Wei Huang } 1175627007213deb59ef938c80353c8f3598b01478b3Wei Huang 1176627007213deb59ef938c80353c8f3598b01478b3Wei Huang // Keep track of what threads are now on disk so we 1177627007213deb59ef938c80353c8f3598b01478b3Wei Huang // can discard anything removed from the cache. 1178627007213deb59ef938c80353c8f3598b01478b3Wei Huang HashSet<Long> threadsOnDisk = new HashSet<Long>(); 1179627007213deb59ef938c80353c8f3598b01478b3Wei Huang 1180627007213deb59ef938c80353c8f3598b01478b3Wei Huang // Query for all conversations. 1181627007213deb59ef938c80353c8f3598b01478b3Wei Huang Cursor c = context.getContentResolver().query(sAllThreadsUri, 1182627007213deb59ef938c80353c8f3598b01478b3Wei Huang ALL_THREADS_PROJECTION, null, null, null); 1183627007213deb59ef938c80353c8f3598b01478b3Wei Huang try { 1184627007213deb59ef938c80353c8f3598b01478b3Wei Huang if (c != null) { 1185627007213deb59ef938c80353c8f3598b01478b3Wei Huang while (c.moveToNext()) { 1186627007213deb59ef938c80353c8f3598b01478b3Wei Huang long threadId = c.getLong(ID); 1187627007213deb59ef938c80353c8f3598b01478b3Wei Huang threadsOnDisk.add(threadId); 1188627007213deb59ef938c80353c8f3598b01478b3Wei Huang 1189627007213deb59ef938c80353c8f3598b01478b3Wei Huang // Try to find this thread ID in the cache. 1190627007213deb59ef938c80353c8f3598b01478b3Wei Huang Conversation conv; 1191627007213deb59ef938c80353c8f3598b01478b3Wei Huang synchronized (Cache.getInstance()) { 1192627007213deb59ef938c80353c8f3598b01478b3Wei Huang conv = Cache.get(threadId); 1193627007213deb59ef938c80353c8f3598b01478b3Wei Huang } 1194627007213deb59ef938c80353c8f3598b01478b3Wei Huang 1195627007213deb59ef938c80353c8f3598b01478b3Wei Huang if (conv == null) { 1196627007213deb59ef938c80353c8f3598b01478b3Wei Huang // Make a new Conversation and put it in 1197627007213deb59ef938c80353c8f3598b01478b3Wei Huang // the cache if necessary. 1198627007213deb59ef938c80353c8f3598b01478b3Wei Huang conv = new Conversation(context, c, true); 1199627007213deb59ef938c80353c8f3598b01478b3Wei Huang try { 1200627007213deb59ef938c80353c8f3598b01478b3Wei Huang synchronized (Cache.getInstance()) { 1201627007213deb59ef938c80353c8f3598b01478b3Wei Huang Cache.put(conv); 1202627007213deb59ef938c80353c8f3598b01478b3Wei Huang } 1203627007213deb59ef938c80353c8f3598b01478b3Wei Huang } catch (IllegalStateException e) { 120410ca1d3969305df50fb07a17f5d23b0ed59f7868Tom Taylor LogTag.error("Tried to add duplicate Conversation to Cache" + 1205c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor " for threadId: " + threadId + " new conv: " + conv); 1206c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor if (!Cache.replace(conv)) { 1207c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor LogTag.error("cacheAllThreads cache.replace failed on " + conv); 1208c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor } 1209627007213deb59ef938c80353c8f3598b01478b3Wei Huang } 1210627007213deb59ef938c80353c8f3598b01478b3Wei Huang } else { 1211627007213deb59ef938c80353c8f3598b01478b3Wei Huang // Or update in place so people with references 1212627007213deb59ef938c80353c8f3598b01478b3Wei Huang // to conversations get updated too. 1213627007213deb59ef938c80353c8f3598b01478b3Wei Huang fillFromCursor(context, conv, c, true); 1214627007213deb59ef938c80353c8f3598b01478b3Wei Huang } 1215627007213deb59ef938c80353c8f3598b01478b3Wei Huang } 1216627007213deb59ef938c80353c8f3598b01478b3Wei Huang } 1217627007213deb59ef938c80353c8f3598b01478b3Wei Huang } finally { 1218627007213deb59ef938c80353c8f3598b01478b3Wei Huang if (c != null) { 1219627007213deb59ef938c80353c8f3598b01478b3Wei Huang c.close(); 1220627007213deb59ef938c80353c8f3598b01478b3Wei Huang } 1221627007213deb59ef938c80353c8f3598b01478b3Wei Huang synchronized (Cache.getInstance()) { 12226bbfdd3cc9cbe6b31dc64f122f3308563d19e077Tom Taylor sLoadingThreads = false; 1223627007213deb59ef938c80353c8f3598b01478b3Wei Huang } 1224627007213deb59ef938c80353c8f3598b01478b3Wei Huang } 1225627007213deb59ef938c80353c8f3598b01478b3Wei Huang 1226627007213deb59ef938c80353c8f3598b01478b3Wei Huang // Purge the cache of threads that no longer exist on disk. 1227627007213deb59ef938c80353c8f3598b01478b3Wei Huang Cache.keepOnly(threadsOnDisk); 122809a75ac1d3710e60dbe78ead3dee6863ffb380caWei Huang 122909a75ac1d3710e60dbe78ead3dee6863ffb380caWei Huang if (Log.isLoggable(LogTag.THREAD_CACHE, Log.VERBOSE)) { 123009a75ac1d3710e60dbe78ead3dee6863ffb380caWei Huang LogTag.debug("[Conversation] cacheAllThreads: finished"); 123109a75ac1d3710e60dbe78ead3dee6863ffb380caWei Huang Cache.dumpCache(); 123209a75ac1d3710e60dbe78ead3dee6863ffb380caWei Huang } 1233627007213deb59ef938c80353c8f3598b01478b3Wei Huang } 123471bbae69cee0da902032743d0702e283cfe31504Ficus Kirkpatrick 1235e692b9dcf5742e7a3ef3a7e64b44bb9d08f05b57Tom Taylor private boolean loadFromThreadId(long threadId, boolean allowQuery) { 123670c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick Cursor c = mContext.getContentResolver().query(sAllThreadsUri, ALL_THREADS_PROJECTION, 123770c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick "_id=" + Long.toString(threadId), null, null); 123870c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick try { 123970c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick if (c.moveToFirst()) { 1240e692b9dcf5742e7a3ef3a7e64b44bb9d08f05b57Tom Taylor fillFromCursor(mContext, this, c, allowQuery); 12416bbfd941885c588cc5d94e345d2cfc4197134a19Tom Taylor 124209a75ac1d3710e60dbe78ead3dee6863ffb380caWei Huang if (threadId != mThreadId) { 124309a75ac1d3710e60dbe78ead3dee6863ffb380caWei Huang LogTag.error("loadFromThreadId: fillFromCursor returned differnt thread_id!" + 124409a75ac1d3710e60dbe78ead3dee6863ffb380caWei Huang " threadId=" + threadId + ", mThreadId=" + mThreadId); 124509a75ac1d3710e60dbe78ead3dee6863ffb380caWei Huang } 124670c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick } else { 1247d62ef06699b3ca5048c5642bd50300e9a2eb04a1Tom Taylor LogTag.error("loadFromThreadId: Can't find thread ID " + threadId); 12486cba8248f3e41921b03cc74a823a6347016e69baTom Taylor return false; 124970c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick } 125070c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick } finally { 125170c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick c.close(); 125270c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick } 12536cba8248f3e41921b03cc74a823a6347016e69baTom Taylor return true; 125470c73e05a792832aa28da751cdaf3fa83a7b8113Ficus Kirkpatrick } 12551149674a2a3c13c7b3b41e5057880e011a45b4b4Ben Dodson 12566d38b19aa9aef46afa855187f23e44d4c06f8878Tom Taylor public static String getRecipients(Uri uri) { 12571149674a2a3c13c7b3b41e5057880e011a45b4b4Ben Dodson String base = uri.getSchemeSpecificPart(); 12581149674a2a3c13c7b3b41e5057880e011a45b4b4Ben Dodson int pos = base.indexOf('?'); 12591149674a2a3c13c7b3b41e5057880e011a45b4b4Ben Dodson return (pos == -1) ? base : base.substring(0, pos); 12601149674a2a3c13c7b3b41e5057880e011a45b4b4Ben Dodson } 1261b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor 1262b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor public static void dump() { 1263b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor Cache.dumpCache(); 1264b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor } 1265b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor 1266b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor public static void dumpThreadsTable(Context context) { 1267b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor LogTag.debug("**** Dump of threads table ****"); 1268b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor Cursor c = context.getContentResolver().query(sAllThreadsUri, 1269b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor ALL_THREADS_PROJECTION, null, null, "date ASC"); 1270b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor try { 1271b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor c.moveToPosition(-1); 1272b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor while (c.moveToNext()) { 1273b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor String snippet = MessageUtils.extractEncStrFromCursor(c, SNIPPET, SNIPPET_CS); 12747a063cb79843369325645ce059ec0f4676e0ca83Tom Taylor Log.d(TAG, "dumpThreadsTable threadId: " + c.getLong(ID) + 1275b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor " " + ThreadsColumns.DATE + " : " + c.getLong(DATE) + 1276b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor " " + ThreadsColumns.MESSAGE_COUNT + " : " + c.getInt(MESSAGE_COUNT) + 1277b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor " " + ThreadsColumns.SNIPPET + " : " + snippet + 1278b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor " " + ThreadsColumns.READ + " : " + c.getInt(READ) + 1279b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor " " + ThreadsColumns.ERROR + " : " + c.getInt(ERROR) + 1280b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor " " + ThreadsColumns.HAS_ATTACHMENT + " : " + c.getInt(HAS_ATTACHMENT) + 1281b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor " " + ThreadsColumns.RECIPIENT_IDS + " : " + c.getString(RECIPIENT_IDS)); 1282b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor 1283b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor ContactList recipients = ContactList.getByIds(c.getString(RECIPIENT_IDS), false); 12847a063cb79843369325645ce059ec0f4676e0ca83Tom Taylor Log.d(TAG, "----recipients: " + recipients.serialize()); 1285b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor } 1286b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor } finally { 1287b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor c.close(); 1288b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor } 1289b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor } 1290b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor 1291b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor static final String[] SMS_PROJECTION = new String[] { 1292b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor BaseColumns._ID, 1293b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor // For SMS 1294b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor Sms.THREAD_ID, 1295b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor Sms.ADDRESS, 1296b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor Sms.BODY, 1297b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor Sms.DATE, 1298b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor Sms.READ, 1299b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor Sms.TYPE, 1300b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor Sms.STATUS, 1301b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor Sms.LOCKED, 1302b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor Sms.ERROR_CODE, 1303b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor }; 1304b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor 1305b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor // The indexes of the default columns which must be consistent 1306b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor // with above PROJECTION. 1307b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor static final int COLUMN_ID = 0; 1308b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor static final int COLUMN_THREAD_ID = 1; 1309b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor static final int COLUMN_SMS_ADDRESS = 2; 1310b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor static final int COLUMN_SMS_BODY = 3; 1311b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor static final int COLUMN_SMS_DATE = 4; 1312b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor static final int COLUMN_SMS_READ = 5; 1313b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor static final int COLUMN_SMS_TYPE = 6; 1314b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor static final int COLUMN_SMS_STATUS = 7; 1315b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor static final int COLUMN_SMS_LOCKED = 8; 1316b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor static final int COLUMN_SMS_ERROR_CODE = 9; 1317b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor 1318b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor public static void dumpSmsTable(Context context) { 1319b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor LogTag.debug("**** Dump of sms table ****"); 1320b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor Cursor c = context.getContentResolver().query(Sms.CONTENT_URI, 1321d4e997a8f0e03688143d1a50b381c6f363a204caTom Taylor SMS_PROJECTION, null, null, "_id DESC"); 1322b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor try { 1323d4e997a8f0e03688143d1a50b381c6f363a204caTom Taylor // Only dump the latest 20 messages 1324b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor c.moveToPosition(-1); 1325d4e997a8f0e03688143d1a50b381c6f363a204caTom Taylor while (c.moveToNext() && c.getPosition() < 20) { 1326b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor String body = c.getString(COLUMN_SMS_BODY); 1327b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor LogTag.debug("dumpSmsTable " + BaseColumns._ID + ": " + c.getLong(COLUMN_ID) + 1328b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor " " + Sms.THREAD_ID + " : " + c.getLong(DATE) + 1329b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor " " + Sms.ADDRESS + " : " + c.getString(COLUMN_SMS_ADDRESS) + 1330d4e997a8f0e03688143d1a50b381c6f363a204caTom Taylor " " + Sms.BODY + " : " + body.substring(0, Math.min(body.length(), 8)) + 1331b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor " " + Sms.DATE + " : " + c.getLong(COLUMN_SMS_DATE) + 1332d4e997a8f0e03688143d1a50b381c6f363a204caTom Taylor " " + Sms.TYPE + " : " + c.getInt(COLUMN_SMS_TYPE)); 1333b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor } 1334b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor } finally { 1335b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor c.close(); 1336b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor } 1337b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor } 1338b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor 1339b736686638eca62aa89cb15184711ef38413cb3eTom Taylor /** 1340b736686638eca62aa89cb15184711ef38413cb3eTom Taylor * verifySingleRecipient takes a threadId and a string recipient [phone number or email 1341b736686638eca62aa89cb15184711ef38413cb3eTom Taylor * address]. It uses that threadId to lookup the row in the threads table and grab the 1342b736686638eca62aa89cb15184711ef38413cb3eTom Taylor * recipient ids column. The recipient ids column contains a space-separated list of 1343b736686638eca62aa89cb15184711ef38413cb3eTom Taylor * recipient ids. These ids are keys in the canonical_addresses table. The recipient is 1344b736686638eca62aa89cb15184711ef38413cb3eTom Taylor * compared against what's stored in the mmssms.db, but only if the recipient id list has 1345b736686638eca62aa89cb15184711ef38413cb3eTom Taylor * a single address. 1346b736686638eca62aa89cb15184711ef38413cb3eTom Taylor * @param context is used for getting a ContentResolver 1347b736686638eca62aa89cb15184711ef38413cb3eTom Taylor * @param threadId of the thread we're sending to 1348b736686638eca62aa89cb15184711ef38413cb3eTom Taylor * @param recipientStr is a phone number or email address 1349b736686638eca62aa89cb15184711ef38413cb3eTom Taylor * @return the verified number or email of the recipient 1350b736686638eca62aa89cb15184711ef38413cb3eTom Taylor */ 1351b736686638eca62aa89cb15184711ef38413cb3eTom Taylor public static String verifySingleRecipient(final Context context, 1352b736686638eca62aa89cb15184711ef38413cb3eTom Taylor final long threadId, final String recipientStr) { 1353b736686638eca62aa89cb15184711ef38413cb3eTom Taylor if (threadId <= 0) { 1354b736686638eca62aa89cb15184711ef38413cb3eTom Taylor LogTag.error("verifySingleRecipient threadId is ZERO, recipient: " + recipientStr); 1355b736686638eca62aa89cb15184711ef38413cb3eTom Taylor LogTag.dumpInternalTables(context); 1356b736686638eca62aa89cb15184711ef38413cb3eTom Taylor return recipientStr; 1357b736686638eca62aa89cb15184711ef38413cb3eTom Taylor } 1358b736686638eca62aa89cb15184711ef38413cb3eTom Taylor Cursor c = context.getContentResolver().query(sAllThreadsUri, ALL_THREADS_PROJECTION, 1359b736686638eca62aa89cb15184711ef38413cb3eTom Taylor "_id=" + Long.toString(threadId), null, null); 1360b736686638eca62aa89cb15184711ef38413cb3eTom Taylor if (c == null) { 1361b736686638eca62aa89cb15184711ef38413cb3eTom Taylor LogTag.error("verifySingleRecipient threadId: " + threadId + 1362b736686638eca62aa89cb15184711ef38413cb3eTom Taylor " resulted in NULL cursor , recipient: " + recipientStr); 1363b736686638eca62aa89cb15184711ef38413cb3eTom Taylor LogTag.dumpInternalTables(context); 1364b736686638eca62aa89cb15184711ef38413cb3eTom Taylor return recipientStr; 1365b736686638eca62aa89cb15184711ef38413cb3eTom Taylor } 1366b736686638eca62aa89cb15184711ef38413cb3eTom Taylor String address = recipientStr; 1367b736686638eca62aa89cb15184711ef38413cb3eTom Taylor String recipientIds; 1368b736686638eca62aa89cb15184711ef38413cb3eTom Taylor try { 1369b736686638eca62aa89cb15184711ef38413cb3eTom Taylor if (!c.moveToFirst()) { 1370b736686638eca62aa89cb15184711ef38413cb3eTom Taylor LogTag.error("verifySingleRecipient threadId: " + threadId + 1371b736686638eca62aa89cb15184711ef38413cb3eTom Taylor " can't moveToFirst , recipient: " + recipientStr); 1372b736686638eca62aa89cb15184711ef38413cb3eTom Taylor LogTag.dumpInternalTables(context); 1373b736686638eca62aa89cb15184711ef38413cb3eTom Taylor return recipientStr; 1374b736686638eca62aa89cb15184711ef38413cb3eTom Taylor } 1375b736686638eca62aa89cb15184711ef38413cb3eTom Taylor recipientIds = c.getString(RECIPIENT_IDS); 1376b736686638eca62aa89cb15184711ef38413cb3eTom Taylor } finally { 1377b736686638eca62aa89cb15184711ef38413cb3eTom Taylor c.close(); 1378b736686638eca62aa89cb15184711ef38413cb3eTom Taylor } 1379b736686638eca62aa89cb15184711ef38413cb3eTom Taylor String[] ids = recipientIds.split(" "); 1380b736686638eca62aa89cb15184711ef38413cb3eTom Taylor 1381b736686638eca62aa89cb15184711ef38413cb3eTom Taylor if (ids.length != 1) { 1382b736686638eca62aa89cb15184711ef38413cb3eTom Taylor // We're only verifying the situation where we have a single recipient input against 1383b736686638eca62aa89cb15184711ef38413cb3eTom Taylor // a thread with a single recipient. If the thread has multiple recipients, just 1384b736686638eca62aa89cb15184711ef38413cb3eTom Taylor // assume the input number is correct and return it. 1385b736686638eca62aa89cb15184711ef38413cb3eTom Taylor return recipientStr; 1386b736686638eca62aa89cb15184711ef38413cb3eTom Taylor } 1387b736686638eca62aa89cb15184711ef38413cb3eTom Taylor 1388b736686638eca62aa89cb15184711ef38413cb3eTom Taylor // Get the actual number from the canonical_addresses table for this recipientId 1389b736686638eca62aa89cb15184711ef38413cb3eTom Taylor address = RecipientIdCache.getSingleAddressFromCanonicalAddressInDb(context, ids[0]); 1390b736686638eca62aa89cb15184711ef38413cb3eTom Taylor 1391b736686638eca62aa89cb15184711ef38413cb3eTom Taylor if (TextUtils.isEmpty(address)) { 1392b736686638eca62aa89cb15184711ef38413cb3eTom Taylor LogTag.error("verifySingleRecipient threadId: " + threadId + 1393b736686638eca62aa89cb15184711ef38413cb3eTom Taylor " getSingleNumberFromCanonicalAddresses returned empty number for: " + 1394b736686638eca62aa89cb15184711ef38413cb3eTom Taylor ids[0] + " recipientIds: " + recipientIds); 1395b736686638eca62aa89cb15184711ef38413cb3eTom Taylor LogTag.dumpInternalTables(context); 1396b736686638eca62aa89cb15184711ef38413cb3eTom Taylor return recipientStr; 1397b736686638eca62aa89cb15184711ef38413cb3eTom Taylor } 1398b736686638eca62aa89cb15184711ef38413cb3eTom Taylor if (PhoneNumberUtils.compareLoosely(recipientStr, address)) { 1399b736686638eca62aa89cb15184711ef38413cb3eTom Taylor // Bingo, we've got a match. We're returning the input number because of area 1400b736686638eca62aa89cb15184711ef38413cb3eTom Taylor // codes. We could have a number in the canonical_address name of "232-1012" and 1401b736686638eca62aa89cb15184711ef38413cb3eTom Taylor // assume the user's phone's area code is 650. If the user sends a message to 1402b736686638eca62aa89cb15184711ef38413cb3eTom Taylor // "(415) 232-1012", it will loosely match "232-1202". If we returned the value 1403b736686638eca62aa89cb15184711ef38413cb3eTom Taylor // from the table (232-1012), the message would go to the wrong person (to the 1404b736686638eca62aa89cb15184711ef38413cb3eTom Taylor // person in the 650 area code rather than in the 415 area code). 1405b736686638eca62aa89cb15184711ef38413cb3eTom Taylor return recipientStr; 1406b736686638eca62aa89cb15184711ef38413cb3eTom Taylor } 1407b736686638eca62aa89cb15184711ef38413cb3eTom Taylor 1408b736686638eca62aa89cb15184711ef38413cb3eTom Taylor if (context instanceof Activity) { 1409b736686638eca62aa89cb15184711ef38413cb3eTom Taylor LogTag.warnPossibleRecipientMismatch("verifySingleRecipient for threadId: " + 1410b736686638eca62aa89cb15184711ef38413cb3eTom Taylor threadId + " original recipient: " + recipientStr + 1411b736686638eca62aa89cb15184711ef38413cb3eTom Taylor " recipient from DB: " + address, (Activity)context); 1412b736686638eca62aa89cb15184711ef38413cb3eTom Taylor } 1413b736686638eca62aa89cb15184711ef38413cb3eTom Taylor LogTag.dumpInternalTables(context); 1414b736686638eca62aa89cb15184711ef38413cb3eTom Taylor if (Log.isLoggable(LogTag.THREAD_CACHE, Log.VERBOSE)) { 1415b736686638eca62aa89cb15184711ef38413cb3eTom Taylor LogTag.debug("verifySingleRecipient for threadId: " + 1416b736686638eca62aa89cb15184711ef38413cb3eTom Taylor threadId + " original recipient: " + recipientStr + 1417b736686638eca62aa89cb15184711ef38413cb3eTom Taylor " recipient from DB: " + address); 1418b736686638eca62aa89cb15184711ef38413cb3eTom Taylor } 1419b736686638eca62aa89cb15184711ef38413cb3eTom Taylor return address; 1420b736686638eca62aa89cb15184711ef38413cb3eTom Taylor } 1421ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick} 1422