1d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd/* 2d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Copyright (C) 2015 The Android Open Source Project 3d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 4d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Licensed under the Apache License, Version 2.0 (the "License"); 5d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * you may not use this file except in compliance with the License. 6d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * You may obtain a copy of the License at 7d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 8d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * http://www.apache.org/licenses/LICENSE-2.0 9d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 10d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Unless required by applicable law or agreed to in writing, software 11d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * distributed under the License is distributed on an "AS IS" BASIS, 12d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * See the License for the specific language governing permissions and 14d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * limitations under the License. 15d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 16d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 17d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddpackage com.android.messaging.datamodel; 18d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 19d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.content.ContentResolver; 20d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.content.ContentValues; 21d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.database.Cursor; 22d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.database.sqlite.SQLiteDoneException; 23d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.database.sqlite.SQLiteStatement; 24d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.net.Uri; 25d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.os.ParcelFileDescriptor; 26d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.support.v4.util.ArrayMap; 27d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.support.v4.util.SimpleArrayMap; 28d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.text.TextUtils; 29d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 30d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.Factory; 31d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.datamodel.DatabaseHelper.ConversationColumns; 32d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.datamodel.DatabaseHelper.ConversationParticipantsColumns; 33d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.datamodel.DatabaseHelper.MessageColumns; 34d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.datamodel.DatabaseHelper.PartColumns; 35d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.datamodel.DatabaseHelper.ParticipantColumns; 36d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.datamodel.ParticipantRefresh.ConversationParticipantsQuery; 37d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.datamodel.data.ConversationListItemData; 38d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.datamodel.data.MessageData; 39d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.datamodel.data.MessagePartData; 40d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.datamodel.data.ParticipantData; 41d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.sms.MmsUtils; 42d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.ui.UIIntents; 43d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.Assert; 44d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.Assert.DoesNotRunOnMainThread; 45d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.AvatarUriUtil; 46d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.ContentType; 47d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.LogUtil; 48d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.OsUtil; 49d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.PhoneUtils; 50d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.UriUtil; 51d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.widget.WidgetConversationProvider; 52d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.google.common.annotations.VisibleForTesting; 53d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 54d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.io.IOException; 55d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.util.ArrayList; 56d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.util.HashSet; 57d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.util.List; 58d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport javax.annotation.Nullable; 59d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 60d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 61d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd/** 62d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * This class manages updating our local database 63d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 64d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddpublic class BugleDatabaseOperations { 65d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 66d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static final String TAG = LogUtil.BUGLE_DATABASE_TAG; 67d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 68d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Global cache of phone numbers -> participant id mapping since this call is expensive. 69d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static final ArrayMap<String, String> sNormalizedPhoneNumberToParticipantIdCache = 70d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new ArrayMap<String, String>(); 71d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 72d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 73d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Convert list of recipient strings (email/phone number) into list of ConversationParticipants 74d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 75d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param recipients The recipient list 76d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param refSubId The subId used to normalize phone numbers in the recipients 77d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 78d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd static ArrayList<ParticipantData> getConversationParticipantsFromRecipients( 79d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final List<String> recipients, final int refSubId) { 80d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Generate a list of partially formed participants 81d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ArrayList<ParticipantData> participants = new 82d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ArrayList<ParticipantData>(); 83d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 84d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (recipients != null) { 85d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd for (final String recipient : recipients) { 86d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd participants.add(ParticipantData.getFromRawPhoneBySimLocale(recipient, refSubId)); 87d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 88d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 89d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return participants; 90d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 91d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 92d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 93d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Sanitize a given list of conversation participants by de-duping and stripping out self 94d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * phone number in group conversation. 95d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 96d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @DoesNotRunOnMainThread 97d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static void sanitizeConversationParticipants(final List<ParticipantData> participants) { 98d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isNotMainThread(); 99d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (participants.size() > 0) { 100d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // First remove redundant phone numbers 101d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final HashSet<String> recipients = new HashSet<String>(); 102d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd for (int i = participants.size() - 1; i >= 0; i--) { 103d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String recipient = participants.get(i).getNormalizedDestination(); 104d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (!recipients.contains(recipient)) { 105d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd recipients.add(recipient); 106d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 107d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd participants.remove(i); 108d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 109d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 110d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (participants.size() > 1) { 111d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Remove self phone number from group conversation. 112d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final HashSet<String> selfNumbers = 113d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd PhoneUtils.getDefault().getNormalizedSelfNumbers(); 114d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd int removed = 0; 115d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Do this two-pass scan to avoid unnecessary memory allocation. 116d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Prescan to count the self numbers in the list 117d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd for (final ParticipantData p : participants) { 118d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (selfNumbers.contains(p.getNormalizedDestination())) { 119d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd removed++; 120d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 121d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 122d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // If all are self numbers, maybe that's what the user wants, just leave 123d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // the participants as is. Otherwise, do another scan to remove self numbers. 124d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (removed < participants.size()) { 125d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd for (int i = participants.size() - 1; i >= 0; i--) { 126d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String recipient = participants.get(i).getNormalizedDestination(); 127d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (selfNumbers.contains(recipient)) { 128d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd participants.remove(i); 129d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 130d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 131d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 132d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 133d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 134d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 135d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 136d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 137d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Convert list of ConversationParticipants into recipient strings (email/phone number) 138d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 139d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @DoesNotRunOnMainThread 140d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static ArrayList<String> getRecipientsFromConversationParticipants( 141d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final List<ParticipantData> participants) { 142d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isNotMainThread(); 143d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // First find the thread id for this list of participants. 144d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ArrayList<String> recipients = new ArrayList<String>(); 145d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 146d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd for (final ParticipantData participant : participants) { 147d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd recipients.add(participant.getSendDestination()); 148d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 149d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return recipients; 150d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 151d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 152d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 153d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Get or create a conversation based on the message's thread id 154d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 155d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * NOTE: There are phones on which you can't get the recipients from the thread id for SMS 156d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * until you have a message, so use getOrCreateConversationFromRecipient instead. 157d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 158d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * TODO: Should this be in MMS/SMS code? 159d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 160d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param db the database 161d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param threadId The message's thread 162d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param senderBlocked Flag whether sender of message is in blocked people list 163d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param refSubId The reference subId for canonicalize phone numbers 164d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @return conversationId 165d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 166d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @DoesNotRunOnMainThread 167d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static String getOrCreateConversationFromThreadId(final DatabaseWrapper db, 168d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long threadId, final boolean senderBlocked, final int refSubId) { 169d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isNotMainThread(); 170d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final List<String> recipients = MmsUtils.getRecipientsByThread(threadId); 171d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ArrayList<ParticipantData> participants = 172d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd getConversationParticipantsFromRecipients(recipients, refSubId); 173d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 174d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return getOrCreateConversation(db, threadId, senderBlocked, participants, false, false, 175d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd null); 176d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 177d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 178d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 179d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Get or create a conversation based on provided recipient 180d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 181d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param db the database 182d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param threadId The message's thread 183d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param senderBlocked Flag whether sender of message is in blocked people list 184d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param recipient recipient for thread 185d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @return conversationId 186d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 187d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @DoesNotRunOnMainThread 188d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static String getOrCreateConversationFromRecipient(final DatabaseWrapper db, 189d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long threadId, final boolean senderBlocked, final ParticipantData recipient) { 190d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isNotMainThread(); 191d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ArrayList<ParticipantData> recipients = new ArrayList<>(1); 192d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd recipients.add(recipient); 193d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return getOrCreateConversation(db, threadId, senderBlocked, recipients, false, false, null); 194d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 195d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 196d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 197d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Get or create a conversation based on provided participants 198d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 199d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param db the database 200d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param threadId The message's thread 201d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param archived Flag whether the conversation should be created archived 202d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param participants list of conversation participants 203d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param noNotification If notification should be disabled 204d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param noVibrate If vibrate on notification should be disabled 205d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param soundUri If there is custom sound URI 206d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @return a conversation id 207d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 208d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @DoesNotRunOnMainThread 209d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static String getOrCreateConversation(final DatabaseWrapper db, final long threadId, 210d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final boolean archived, final ArrayList<ParticipantData> participants, 211d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd boolean noNotification, boolean noVibrate, String soundUri) { 212d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isNotMainThread(); 213d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 214d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Check to see if this conversation is already in out local db cache 215d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd String conversationId = BugleDatabaseOperations.getExistingConversation(db, threadId, 216d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd false); 217d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 218d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (conversationId == null) { 219d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String conversationName = ConversationListItemData.generateConversationName( 220d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd participants); 221d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 222d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Create the conversation with the default self participant which always maps to 223d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // the system default subscription. 224d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ParticipantData self = ParticipantData.getSelfParticipant( 225d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ParticipantData.DEFAULT_SELF_SUB_ID); 226d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 227d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd db.beginTransaction(); 228d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 229d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Look up the "self" participantId (creating if necessary) 230d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String selfId = 231d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd BugleDatabaseOperations.getOrCreateParticipantInTransaction(db, self); 232d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Create a new conversation 233d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd conversationId = BugleDatabaseOperations.createConversationInTransaction( 234d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd db, threadId, conversationName, selfId, participants, archived, 235d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd noNotification, noVibrate, soundUri); 236d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd db.setTransactionSuccessful(); 237d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } finally { 238d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd db.endTransaction(); 239d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 240d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 241d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 242d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return conversationId; 243d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 244d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 245d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 246d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Get a conversation from the local DB based on the message's thread id. 247d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 248d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param dbWrapper The database 249d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param threadId The message's thread in the SMS database 250d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param senderBlocked Flag whether sender of message is in blocked people list 251d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @return The existing conversation id or null 252d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 253d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @VisibleForTesting 254d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @DoesNotRunOnMainThread 255d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static String getExistingConversation(final DatabaseWrapper dbWrapper, 256d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long threadId, final boolean senderBlocked) { 257d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isNotMainThread(); 258d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd String conversationId = null; 259d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 260d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Cursor cursor = null; 261d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 262d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Look for an existing conversation in the db with this thread id 263d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor = dbWrapper.rawQuery("SELECT " + ConversationColumns._ID 264d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + " FROM " + DatabaseHelper.CONVERSATIONS_TABLE 265d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + " WHERE " + ConversationColumns.SMS_THREAD_ID + "=" + threadId, 266d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd null); 267d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 268d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor.moveToFirst()) { 269d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isTrue(cursor.getCount() == 1); 270d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd conversationId = cursor.getString(0); 271d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 272d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } finally { 273d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor != null) { 274d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor.close(); 275d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 276d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 277d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 278d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return conversationId; 279d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 280d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 281d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 282d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Get the thread id for an existing conversation from the local DB. 283d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 284d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param dbWrapper The database 285d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param conversationId The conversation to look up thread for 286d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @return The thread id. Returns -1 if the conversation was not found or if it was found 287d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * but the thread column was NULL. 288d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 289d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @DoesNotRunOnMainThread 290d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static long getThreadId(final DatabaseWrapper dbWrapper, final String conversationId) { 291d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isNotMainThread(); 292d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd long threadId = -1; 293d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 294d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Cursor cursor = null; 295d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 296d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor = dbWrapper.query(DatabaseHelper.CONVERSATIONS_TABLE, 297d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new String[] { ConversationColumns.SMS_THREAD_ID }, 298d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ConversationColumns._ID + " =?", 299d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new String[] { conversationId }, 300d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd null, null, null); 301d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 302d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor.moveToFirst()) { 303d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isTrue(cursor.getCount() == 1); 304d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (!cursor.isNull(0)) { 305d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd threadId = cursor.getLong(0); 306d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 307d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 308d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } finally { 309d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor != null) { 310d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor.close(); 311d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 312d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 313d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 314d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return threadId; 315d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 316d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 317d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @DoesNotRunOnMainThread 318d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static boolean isBlockedDestination(final DatabaseWrapper db, final String destination) { 319d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isNotMainThread(); 320d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return isBlockedParticipant(db, destination, ParticipantColumns.NORMALIZED_DESTINATION); 321d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 322d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 323d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd static boolean isBlockedParticipant(final DatabaseWrapper db, final String participantId) { 324d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return isBlockedParticipant(db, participantId, ParticipantColumns._ID); 325d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 326d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 327d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd static boolean isBlockedParticipant(final DatabaseWrapper db, final String value, 328d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String column) { 329d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Cursor cursor = null; 330d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 331d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor = db.query(DatabaseHelper.PARTICIPANTS_TABLE, 332d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new String[] { ParticipantColumns.BLOCKED }, 333d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd column + "=? AND " + ParticipantColumns.SUB_ID + "=?", 334d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new String[] { value, 335d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Integer.toString(ParticipantData.OTHER_THAN_SELF_SUB_ID) }, 336d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd null, null, null); 337d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 338d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.inRange(cursor.getCount(), 0, 1); 339d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor.moveToFirst()) { 340d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return cursor.getInt(0) == 1; 341d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 342d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } finally { 343d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor != null) { 344d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor.close(); 345d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 346d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 347d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return false; // if there's no row, it's not blocked :-) 348d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 349d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 350d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 351d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Create a conversation in the local DB based on the message's thread id. 352d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 353d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * It's up to the caller to make sure that this is all inside a transaction. It will return 354d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * null if it's not in the local DB. 355d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 356d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param dbWrapper The database 357d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param threadId The message's thread 358d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param selfId The selfId to make default for this conversation 359d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param archived Flag whether the conversation should be created archived 360d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param noNotification If notification should be disabled 361d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param noVibrate If vibrate on notification should be disabled 362d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param soundUri The customized sound 363d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @return The existing conversation id or new conversation id 364d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 365d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd static String createConversationInTransaction(final DatabaseWrapper dbWrapper, 366d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long threadId, final String conversationName, final String selfId, 367d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final List<ParticipantData> participants, final boolean archived, 368d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd boolean noNotification, boolean noVibrate, String soundUri) { 369d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // We want conversation and participant creation to be atomic 370d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isTrue(dbWrapper.getDatabase().inTransaction()); 371d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd boolean hasEmailAddress = false; 372d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd for (final ParticipantData participant : participants) { 373d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isTrue(!participant.isSelf()); 374d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (participant.isEmail()) { 375d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd hasEmailAddress = true; 376d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 377d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 378d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 379d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // TODO : Conversations state - normal vs. archived 380d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 381d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Insert a new local conversation for this thread id 382d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ContentValues values = new ContentValues(); 383d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(ConversationColumns.SMS_THREAD_ID, threadId); 384d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Start with conversation hidden - sending a message or saving a draft will change that 385d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(ConversationColumns.SORT_TIMESTAMP, 0L); 386d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(ConversationColumns.CURRENT_SELF_ID, selfId); 387d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(ConversationColumns.PARTICIPANT_COUNT, participants.size()); 388d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(ConversationColumns.INCLUDE_EMAIL_ADDRESS, (hasEmailAddress ? 1 : 0)); 389d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (archived) { 390d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(ConversationColumns.ARCHIVE_STATUS, 1); 391d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 392d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (noNotification) { 393d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(ConversationColumns.NOTIFICATION_ENABLED, 0); 394d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 395d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (noVibrate) { 396d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(ConversationColumns.NOTIFICATION_VIBRATION, 0); 397d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 398d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (!TextUtils.isEmpty(soundUri)) { 399d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(ConversationColumns.NOTIFICATION_SOUND_URI, soundUri); 400d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 401d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 402d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd fillParticipantData(values, participants); 403d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 404d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long conversationRowId = dbWrapper.insert(DatabaseHelper.CONVERSATIONS_TABLE, null, 405d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values); 406d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 407d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isTrue(conversationRowId != -1); 408d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (conversationRowId == -1) { 409d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "BugleDatabaseOperations : failed to insert conversation into table"); 410d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return null; 411d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 412d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 413d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String conversationId = Long.toString(conversationRowId); 414d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 415d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Make sure that participants are added for this conversation 416d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd for (final ParticipantData participant : participants) { 417d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // TODO: Use blocking information 418d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd addParticipantToConversation(dbWrapper, participant, conversationId); 419d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 420d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 421d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Now fully resolved participants available can update conversation name / avatar. 422d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // b/16437575: We cannot use the participants directly, but instead have to call 423d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // getParticipantsForConversation() to retrieve the actual participants. This is needed 424d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // because the call to addParticipantToConversation() won't fill up the ParticipantData 425d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // if the participant already exists in the participant table. For example, say you have 426d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // an existing conversation with John. Now if you create a new group conversation with 427d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Jeff & John with only their phone numbers, then when we try to add John's number to the 428d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // group conversation, we see that he's already in the participant table, therefore we 429d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // short-circuit any steps to actually fill out the ParticipantData for John other than 430d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // just returning his participant id. Eventually, the ParticipantData we have is still the 431d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // raw data with just the phone number. getParticipantsForConversation(), on the other 432d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // hand, will fill out all the info for each participant from the participants table. 433d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd updateConversationNameAndAvatarInTransaction(dbWrapper, conversationId, 434d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd getParticipantsForConversation(dbWrapper, conversationId)); 435d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 436d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return conversationId; 437d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 438d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 439d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static void fillParticipantData(final ContentValues values, 440d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final List<ParticipantData> participants) { 441d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (participants != null && !participants.isEmpty()) { 442d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Uri avatarUri = AvatarUriUtil.createAvatarUri(participants); 443d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(ConversationColumns.ICON, avatarUri.toString()); 444d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 445d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd long contactId; 446d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd String lookupKey; 447d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd String destination; 448d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (participants.size() == 1) { 449d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ParticipantData firstParticipant = participants.get(0); 450d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd contactId = firstParticipant.getContactId(); 451d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd lookupKey = firstParticipant.getLookupKey(); 452d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd destination = firstParticipant.getNormalizedDestination(); 453d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 454d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd contactId = 0; 455d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd lookupKey = null; 456d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd destination = null; 457d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 458d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 459d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(ConversationColumns.PARTICIPANT_CONTACT_ID, contactId); 460d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(ConversationColumns.PARTICIPANT_LOOKUP_KEY, lookupKey); 461d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(ConversationColumns.OTHER_PARTICIPANT_NORMALIZED_DESTINATION, destination); 462d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 463d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 464d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 465d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 466d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Delete conversation and associated messages/parts 467d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 468d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @DoesNotRunOnMainThread 469d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static boolean deleteConversation(final DatabaseWrapper dbWrapper, 470d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String conversationId, final long cutoffTimestamp) { 471d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isNotMainThread(); 472d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd dbWrapper.beginTransaction(); 473d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd boolean conversationDeleted = false; 474d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd boolean conversationMessagesDeleted = false; 475d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 476d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Delete existing messages 477d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cutoffTimestamp == Long.MAX_VALUE) { 478d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Delete parts and messages 479d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd dbWrapper.delete(DatabaseHelper.MESSAGES_TABLE, 480d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MessageColumns.CONVERSATION_ID + "=?", new String[] { conversationId }); 481d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd conversationMessagesDeleted = true; 482d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 483d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Delete all messages prior to the cutoff 484d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd dbWrapper.delete(DatabaseHelper.MESSAGES_TABLE, 485d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MessageColumns.CONVERSATION_ID + "=? AND " 486d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + MessageColumns.RECEIVED_TIMESTAMP + "<=?", 487d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new String[] { conversationId, Long.toString(cutoffTimestamp) }); 488d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 489d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Delete any draft message. The delete above may not always include the draft, 490d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // because under certain scenarios (e.g. sending messages in progress), the draft 491d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // timestamp can be larger than the cutoff time, which is generally the conversation 492d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // sort timestamp. Because of how the sms/mms provider works on some newer 493d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // devices, it's important that we never delete all the messages in a conversation 494d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // without also deleting the conversation itself (see b/20262204 for details). 495d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd dbWrapper.delete(DatabaseHelper.MESSAGES_TABLE, 496d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MessageColumns.STATUS + "=? AND " + MessageColumns.CONVERSATION_ID + "=?", 497d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new String[] { 498d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Integer.toString(MessageData.BUGLE_STATUS_OUTGOING_DRAFT), 499d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd conversationId 500d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd }); 501d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 502d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Check to see if there are any messages left in the conversation 503d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long count = dbWrapper.queryNumEntries(DatabaseHelper.MESSAGES_TABLE, 504d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MessageColumns.CONVERSATION_ID + "=?", new String[] { conversationId }); 505d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd conversationMessagesDeleted = (count == 0); 506d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 507d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Log detail information if there are still messages left in the conversation 508d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (!conversationMessagesDeleted) { 509d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long maxTimestamp = 510d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd getConversationMaxTimestamp(dbWrapper, conversationId); 511d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.w(TAG, "BugleDatabaseOperations:" 512d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + " cannot delete all messages in a conversation" 513d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + ", after deletion: count=" + count 514d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + ", max timestamp=" + maxTimestamp 515d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + ", cutoff timestamp=" + cutoffTimestamp); 516d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 517d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 518d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 519d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (conversationMessagesDeleted) { 520d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Delete conversation row 521d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int count = dbWrapper.delete(DatabaseHelper.CONVERSATIONS_TABLE, 522d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ConversationColumns._ID + "=?", new String[] { conversationId }); 523d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd conversationDeleted = (count > 0); 524d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 525d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd dbWrapper.setTransactionSuccessful(); 526d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } finally { 527d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd dbWrapper.endTransaction(); 528d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 529d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return conversationDeleted; 530d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 531d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 532d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static final String MAX_RECEIVED_TIMESTAMP = 533d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "MAX(" + MessageColumns.RECEIVED_TIMESTAMP + ")"; 534d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 535d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Get the max received timestamp of a conversation's messages 536d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 537d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static long getConversationMaxTimestamp(final DatabaseWrapper dbWrapper, 538d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String conversationId) { 539d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Cursor cursor = dbWrapper.query( 540d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd DatabaseHelper.MESSAGES_TABLE, 541d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new String[]{ MAX_RECEIVED_TIMESTAMP }, 542d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MessageColumns.CONVERSATION_ID + "=?", 543d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new String[]{ conversationId }, 544d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd null, null, null); 545d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor != null) { 546d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 547d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor.moveToFirst()) { 548d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return cursor.getLong(0); 549d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 550d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } finally { 551d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor.close(); 552d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 553d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 554d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return 0; 555d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 556d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 557d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @DoesNotRunOnMainThread 558d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static void updateConversationMetadataInTransaction(final DatabaseWrapper dbWrapper, 559d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String conversationId, final String messageId, final long latestTimestamp, 560d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final boolean keepArchived, final String smsServiceCenter, 561d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final boolean shouldAutoSwitchSelfId) { 562d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isNotMainThread(); 563d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isTrue(dbWrapper.getDatabase().inTransaction()); 564d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 565d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ContentValues values = new ContentValues(); 566d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(ConversationColumns.LATEST_MESSAGE_ID, messageId); 567d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(ConversationColumns.SORT_TIMESTAMP, latestTimestamp); 568d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (!TextUtils.isEmpty(smsServiceCenter)) { 569d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(ConversationColumns.SMS_SERVICE_CENTER, smsServiceCenter); 570d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 571d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 572d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // When the conversation gets updated with new messages, unarchive the conversation unless 573d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // the sender is blocked, or we have been told to keep it archived. 574d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (!keepArchived) { 575d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(ConversationColumns.ARCHIVE_STATUS, 0); 576d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 577d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 578d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final MessageData message = readMessage(dbWrapper, messageId); 579d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd addSnippetTextAndPreviewToContentValues(message, false /* showDraft */, values); 580d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 581d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (shouldAutoSwitchSelfId) { 582d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd addSelfIdAutoSwitchInfoToContentValues(dbWrapper, message, conversationId, values); 583d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 584d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 585d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Conversation always exists as this method is called from ActionService only after 586d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // reading and if necessary creating the conversation. 587d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd updateConversationRow(dbWrapper, conversationId, values); 588d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 589d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (shouldAutoSwitchSelfId && OsUtil.isAtLeastL_MR1()) { 590d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Normally, the draft message compose UI trusts its UI state for providing up-to-date 591d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // conversation self id. Therefore, notify UI through local broadcast receiver about 592d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // this external change so the change can be properly reflected. 593d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd UIIntents.get().broadcastConversationSelfIdChange(dbWrapper.getContext(), 594d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd conversationId, getConversationSelfId(dbWrapper, conversationId)); 595d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 596d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 597d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 598d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @DoesNotRunOnMainThread 599d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static void updateConversationMetadataInTransaction(final DatabaseWrapper db, 600d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String conversationId, final String messageId, final long latestTimestamp, 601d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final boolean keepArchived, final boolean shouldAutoSwitchSelfId) { 602d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isNotMainThread(); 603d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd updateConversationMetadataInTransaction( 604d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd db, conversationId, messageId, latestTimestamp, keepArchived, null, 605d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd shouldAutoSwitchSelfId); 606d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 607d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 608d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @DoesNotRunOnMainThread 609d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static void updateConversationArchiveStatusInTransaction(final DatabaseWrapper dbWrapper, 610d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String conversationId, final boolean isArchived) { 611d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isNotMainThread(); 612d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isTrue(dbWrapper.getDatabase().inTransaction()); 613d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ContentValues values = new ContentValues(); 614d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(ConversationColumns.ARCHIVE_STATUS, isArchived ? 1 : 0); 615d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd updateConversationRowIfExists(dbWrapper, conversationId, values); 616d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 617d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 618d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd static void addSnippetTextAndPreviewToContentValues(final MessageData message, 619d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final boolean showDraft, final ContentValues values) { 620d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(ConversationColumns.SHOW_DRAFT, showDraft ? 1 : 0); 621d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(ConversationColumns.SNIPPET_TEXT, message.getMessageText()); 622d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(ConversationColumns.SUBJECT_TEXT, message.getMmsSubject()); 623d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 624d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd String type = null; 625d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd String uriString = null; 626d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd for (final MessagePartData part : message.getParts()) { 627d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (part.isAttachment() && 628d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ContentType.isConversationListPreviewableType(part.getContentType())) { 629d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd uriString = part.getContentUri().toString(); 630d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd type = part.getContentType(); 631d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd break; 632d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 633d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 634d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(ConversationColumns.PREVIEW_CONTENT_TYPE, type); 635d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(ConversationColumns.PREVIEW_URI, uriString); 636d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 637d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 638d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 639d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Adds self-id auto switch info for a conversation if the last message has a different 640d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * subscription than the conversation's. 641d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @return true if self id will need to be changed, false otherwise. 642d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 643d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd static boolean addSelfIdAutoSwitchInfoToContentValues(final DatabaseWrapper dbWrapper, 644d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final MessageData message, final String conversationId, final ContentValues values) { 645d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Only auto switch conversation self for incoming messages. 646d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (!OsUtil.isAtLeastL_MR1() || !message.getIsIncoming()) { 647d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return false; 648d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 649d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 650d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String conversationSelfId = getConversationSelfId(dbWrapper, conversationId); 651d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String messageSelfId = message.getSelfId(); 652d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 653d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (conversationSelfId == null || messageSelfId == null) { 654d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return false; 655d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 656d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 657d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Get the sub IDs in effect for both the message and the conversation and compare them: 658d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // 1. If message is unbound (using default sub id), then the message was sent with 659d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // pre-MSIM support. Don't auto-switch because we don't know the subscription for the 660d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // message. 661d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // 2. If message is bound, 662d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // i. If conversation is unbound, use the system default sub id as its effective sub. 663d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // ii. If conversation is bound, use its subscription directly. 664d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Compare the message sub id with the conversation's effective sub id. If they are 665d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // different, auto-switch the conversation to the message's sub. 666d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ParticipantData conversationSelf = getExistingParticipant(dbWrapper, 667d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd conversationSelfId); 668d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ParticipantData messageSelf = getExistingParticipant(dbWrapper, messageSelfId); 669d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (!messageSelf.isActiveSubscription()) { 670d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Don't switch if the message subscription is no longer active. 671d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return false; 672d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 673d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int messageSubId = messageSelf.getSubId(); 674d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (messageSubId == ParticipantData.DEFAULT_SELF_SUB_ID) { 675d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return false; 676d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 677d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 678d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int conversationEffectiveSubId = 679d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd PhoneUtils.getDefault().getEffectiveSubId(conversationSelf.getSubId()); 680d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 681d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (conversationEffectiveSubId != messageSubId) { 682d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return addConversationSelfIdToContentValues(dbWrapper, messageSelf.getId(), values); 683d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 684d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return false; 685d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 686d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 687d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 688d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Adds conversation self id updates to ContentValues given. This performs check on the selfId 689d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * to ensure it's valid and active. 690d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @return true if self id will need to be changed, false otherwise. 691d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 692d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd static boolean addConversationSelfIdToContentValues(final DatabaseWrapper dbWrapper, 693d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String selfId, final ContentValues values) { 694d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Make sure the selfId passed in is valid and active. 695d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String selection = ParticipantColumns._ID + "=? AND " + 696d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ParticipantColumns.SIM_SLOT_ID + "<>?"; 697d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Cursor cursor = null; 698d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 699d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor = dbWrapper.query(DatabaseHelper.PARTICIPANTS_TABLE, 700d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new String[] { ParticipantColumns._ID }, selection, 701d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new String[] { selfId, String.valueOf(ParticipantData.INVALID_SLOT_ID) }, 702d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd null, null, null); 703d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 704d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor != null && cursor.getCount() > 0) { 705d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(ConversationColumns.CURRENT_SELF_ID, selfId); 706d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return true; 707d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 708d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } finally { 709d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor != null) { 710d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor.close(); 711d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 712d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 713d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return false; 714d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 715d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 716d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static void updateConversationDraftSnippetAndPreviewInTransaction( 717d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final DatabaseWrapper dbWrapper, final String conversationId, 718d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final MessageData draftMessage) { 719d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isTrue(dbWrapper.getDatabase().inTransaction()); 720d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 721d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd long sortTimestamp = 0L; 722d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Cursor cursor = null; 723d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 724d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Check to find the latest message in the conversation 725d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor = dbWrapper.query(DatabaseHelper.MESSAGES_TABLE, 726d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd REFRESH_CONVERSATION_MESSAGE_PROJECTION, 727d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MessageColumns.CONVERSATION_ID + "=?", 728d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new String[]{conversationId}, null, null, 729d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MessageColumns.RECEIVED_TIMESTAMP + " DESC", "1" /* limit */); 730d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 731d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor.moveToFirst()) { 732d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd sortTimestamp = cursor.getLong(1); 733d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 734d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } finally { 735d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor != null) { 736d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor.close(); 737d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 738d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 739d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 740d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 741d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ContentValues values = new ContentValues(); 742d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (draftMessage == null || !draftMessage.hasContent()) { 743d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(ConversationColumns.SHOW_DRAFT, 0); 744d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(ConversationColumns.DRAFT_SNIPPET_TEXT, ""); 745d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(ConversationColumns.DRAFT_SUBJECT_TEXT, ""); 746d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(ConversationColumns.DRAFT_PREVIEW_CONTENT_TYPE, ""); 747d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(ConversationColumns.DRAFT_PREVIEW_URI, ""); 748d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 749d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd sortTimestamp = Math.max(sortTimestamp, draftMessage.getReceivedTimeStamp()); 750d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(ConversationColumns.SHOW_DRAFT, 1); 751d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(ConversationColumns.DRAFT_SNIPPET_TEXT, draftMessage.getMessageText()); 752d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(ConversationColumns.DRAFT_SUBJECT_TEXT, draftMessage.getMmsSubject()); 753d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd String type = null; 754d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd String uriString = null; 755d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd for (final MessagePartData part : draftMessage.getParts()) { 756d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (part.isAttachment() && 757d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ContentType.isConversationListPreviewableType(part.getContentType())) { 758d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd uriString = part.getContentUri().toString(); 759d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd type = part.getContentType(); 760d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd break; 761d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 762d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 763d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(ConversationColumns.DRAFT_PREVIEW_CONTENT_TYPE, type); 764d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(ConversationColumns.DRAFT_PREVIEW_URI, uriString); 765d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 766d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(ConversationColumns.SORT_TIMESTAMP, sortTimestamp); 767d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Called in transaction after reading conversation row 768d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd updateConversationRow(dbWrapper, conversationId, values); 769d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 770d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 771d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @DoesNotRunOnMainThread 772d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static boolean updateConversationRowIfExists(final DatabaseWrapper dbWrapper, 773d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String conversationId, final ContentValues values) { 774d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isNotMainThread(); 775d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return updateRowIfExists(dbWrapper, DatabaseHelper.CONVERSATIONS_TABLE, 776d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ConversationColumns._ID, conversationId, values); 777d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 778d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 779d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @DoesNotRunOnMainThread 780d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static void updateConversationRow(final DatabaseWrapper dbWrapper, 781d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String conversationId, final ContentValues values) { 782d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isNotMainThread(); 783d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final boolean exists = updateConversationRowIfExists(dbWrapper, conversationId, values); 784d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isTrue(exists); 785d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 786d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 787d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @DoesNotRunOnMainThread 788d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static boolean updateMessageRowIfExists(final DatabaseWrapper dbWrapper, 789d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String messageId, final ContentValues values) { 790d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isNotMainThread(); 791d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return updateRowIfExists(dbWrapper, DatabaseHelper.MESSAGES_TABLE, MessageColumns._ID, 792d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd messageId, values); 793d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 794d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 795d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @DoesNotRunOnMainThread 796d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static void updateMessageRow(final DatabaseWrapper dbWrapper, 797d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String messageId, final ContentValues values) { 798d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isNotMainThread(); 799d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final boolean exists = updateMessageRowIfExists(dbWrapper, messageId, values); 800d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isTrue(exists); 801d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 802d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 803d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @DoesNotRunOnMainThread 804d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static boolean updatePartRowIfExists(final DatabaseWrapper dbWrapper, 805d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String partId, final ContentValues values) { 806d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isNotMainThread(); 807d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return updateRowIfExists(dbWrapper, DatabaseHelper.PARTS_TABLE, PartColumns._ID, 808d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd partId, values); 809d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 810d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 811d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 812d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Returns the default conversation name based on its participants. 813d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 814d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static String getDefaultConversationName(final List<ParticipantData> participants) { 815d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return ConversationListItemData.generateConversationName(participants); 816d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 817d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 818d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 819d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Updates a given conversation's name based on its participants. 820d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 821d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @DoesNotRunOnMainThread 822d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static void updateConversationNameAndAvatarInTransaction( 823d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final DatabaseWrapper dbWrapper, final String conversationId) { 824d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isNotMainThread(); 825d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isTrue(dbWrapper.getDatabase().inTransaction()); 826d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 827d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ArrayList<ParticipantData> participants = 828d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd getParticipantsForConversation(dbWrapper, conversationId); 829d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd updateConversationNameAndAvatarInTransaction(dbWrapper, conversationId, participants); 830d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 831d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 832d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 833d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Updates a given conversation's name based on its participants. 834d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 835d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static void updateConversationNameAndAvatarInTransaction( 836d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final DatabaseWrapper dbWrapper, final String conversationId, 837d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final List<ParticipantData> participants) { 838d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isTrue(dbWrapper.getDatabase().inTransaction()); 839d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 840d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ContentValues values = new ContentValues(); 841d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(ConversationColumns.NAME, 842d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd getDefaultConversationName(participants)); 843d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 8447ad7ac27f12be44659f2f0ff112a7f8433ebb1b5Tony Mak // Fill in IS_ENTERPRISE. 8457ad7ac27f12be44659f2f0ff112a7f8433ebb1b5Tony Mak final boolean hasAnyEnterpriseContact = 8467ad7ac27f12be44659f2f0ff112a7f8433ebb1b5Tony Mak ConversationListItemData.hasAnyEnterpriseContact(participants); 8477ad7ac27f12be44659f2f0ff112a7f8433ebb1b5Tony Mak values.put(ConversationColumns.IS_ENTERPRISE, hasAnyEnterpriseContact); 8487ad7ac27f12be44659f2f0ff112a7f8433ebb1b5Tony Mak 849d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd fillParticipantData(values, participants); 850d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 851d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Used by background thread when refreshing conversation so conversation could be deleted. 852d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd updateConversationRowIfExists(dbWrapper, conversationId, values); 853d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 854d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd WidgetConversationProvider.notifyConversationRenamed(Factory.get().getApplicationContext(), 855d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd conversationId); 856d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 857d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 858d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 859d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Updates a given conversation's self id. 860d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 861d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @DoesNotRunOnMainThread 862d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static void updateConversationSelfIdInTransaction( 863d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final DatabaseWrapper dbWrapper, final String conversationId, final String selfId) { 864d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isNotMainThread(); 865d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isTrue(dbWrapper.getDatabase().inTransaction()); 866d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ContentValues values = new ContentValues(); 867d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (addConversationSelfIdToContentValues(dbWrapper, selfId, values)) { 868d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd updateConversationRowIfExists(dbWrapper, conversationId, values); 869d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 870d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 871d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 872d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @DoesNotRunOnMainThread 873d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static String getConversationSelfId(final DatabaseWrapper dbWrapper, 874d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String conversationId) { 875d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isNotMainThread(); 876d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Cursor cursor = null; 877d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 878d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor = dbWrapper.query(DatabaseHelper.CONVERSATIONS_TABLE, 879d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new String[] { ConversationColumns.CURRENT_SELF_ID }, 880d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ConversationColumns._ID + "=?", 881d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new String[] { conversationId }, 882d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd null, null, null); 883d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.inRange(cursor.getCount(), 0, 1); 884d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor.moveToFirst()) { 885d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return cursor.getString(0); 886d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 887d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } finally { 888d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor != null) { 889d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor.close(); 890d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 891d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 892d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return null; 893d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 894d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 895d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 896d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Frees up memory associated with phone number to participant id matching. 897d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 898d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @DoesNotRunOnMainThread 899d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static void clearParticipantIdCache() { 900d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isNotMainThread(); 901d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd synchronized (sNormalizedPhoneNumberToParticipantIdCache) { 902d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd sNormalizedPhoneNumberToParticipantIdCache.clear(); 903d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 904d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 905d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 906d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @DoesNotRunOnMainThread 907d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static ArrayList<String> getRecipientsForConversation(final DatabaseWrapper dbWrapper, 908d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String conversationId) { 909d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isNotMainThread(); 910d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ArrayList<ParticipantData> participants = 911d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd getParticipantsForConversation(dbWrapper, conversationId); 912d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 913d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ArrayList<String> recipients = new ArrayList<String>(); 914d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd for (final ParticipantData participant : participants) { 915d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd recipients.add(participant.getSendDestination()); 916d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 917d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 918d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return recipients; 919d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 920d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 921d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @DoesNotRunOnMainThread 922d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static String getSmsServiceCenterForConversation(final DatabaseWrapper dbWrapper, 923d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String conversationId) { 924d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isNotMainThread(); 925d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Cursor cursor = null; 926d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 927d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor = dbWrapper.query(DatabaseHelper.CONVERSATIONS_TABLE, 928d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new String[] { ConversationColumns.SMS_SERVICE_CENTER }, 929d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ConversationColumns._ID + "=?", 930d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new String[] { conversationId }, 931d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd null, null, null); 932d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.inRange(cursor.getCount(), 0, 1); 933d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor.moveToFirst()) { 934d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return cursor.getString(0); 935d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 936d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } finally { 937d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor != null) { 938d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor.close(); 939d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 940d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 941d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return null; 942d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 943d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 944d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @DoesNotRunOnMainThread 945d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static ParticipantData getExistingParticipant(final DatabaseWrapper dbWrapper, 946d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String participantId) { 947d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isNotMainThread(); 948d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ParticipantData participant = null; 949d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Cursor cursor = null; 950d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 951d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor = dbWrapper.query(DatabaseHelper.PARTICIPANTS_TABLE, 952d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ParticipantData.ParticipantsQuery.PROJECTION, 953d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ParticipantColumns._ID + " =?", 954d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new String[] { participantId }, null, null, null); 955d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.inRange(cursor.getCount(), 0, 1); 956d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor.moveToFirst()) { 957d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd participant = ParticipantData.getFromCursor(cursor); 958d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 959d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } finally { 960d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor != null) { 961d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor.close(); 962d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 963d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 964d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 965d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return participant; 966d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 967d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 968d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd static int getSelfSubscriptionId(final DatabaseWrapper dbWrapper, 969d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String selfParticipantId) { 970d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ParticipantData selfParticipant = BugleDatabaseOperations.getExistingParticipant( 971d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd dbWrapper, selfParticipantId); 972d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (selfParticipant != null) { 973d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isTrue(selfParticipant.isSelf()); 974d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return selfParticipant.getSubId(); 975d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 976d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return ParticipantData.DEFAULT_SELF_SUB_ID; 977d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 978d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 979d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @VisibleForTesting 980d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @DoesNotRunOnMainThread 981d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static ArrayList<ParticipantData> getParticipantsForConversation( 982d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final DatabaseWrapper dbWrapper, final String conversationId) { 983d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isNotMainThread(); 984d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ArrayList<ParticipantData> participants = 985d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new ArrayList<ParticipantData>(); 986d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Cursor cursor = null; 987d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 988d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor = dbWrapper.query(DatabaseHelper.PARTICIPANTS_TABLE, 989d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ParticipantData.ParticipantsQuery.PROJECTION, 990d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ParticipantColumns._ID + " IN ( " + "SELECT " 991d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + ConversationParticipantsColumns.PARTICIPANT_ID + " AS " 992d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + ParticipantColumns._ID 993d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + " FROM " + DatabaseHelper.CONVERSATION_PARTICIPANTS_TABLE 994d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + " WHERE " + ConversationParticipantsColumns.CONVERSATION_ID + " =? )", 995d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new String[] { conversationId }, null, null, null); 996d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 997d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd while (cursor.moveToNext()) { 998d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd participants.add(ParticipantData.getFromCursor(cursor)); 999d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1000d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } finally { 1001d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor != null) { 1002d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor.close(); 1003d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1004d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1005d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1006d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return participants; 1007d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1008d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1009d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @DoesNotRunOnMainThread 1010d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static MessageData readMessage(final DatabaseWrapper dbWrapper, final String messageId) { 1011d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isNotMainThread(); 1012d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final MessageData message = readMessageData(dbWrapper, messageId); 1013d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (message != null) { 1014d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd readMessagePartsData(dbWrapper, message, false); 1015d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1016d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return message; 1017d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1018d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1019d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @VisibleForTesting 1020d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd static MessagePartData readMessagePartData(final DatabaseWrapper dbWrapper, 1021d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String partId) { 1022d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MessagePartData messagePartData = null; 1023d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Cursor cursor = null; 1024d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 1025d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor = dbWrapper.query(DatabaseHelper.PARTS_TABLE, 1026d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MessagePartData.getProjection(), PartColumns._ID + "=?", 1027d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new String[] { partId }, null, null, null); 1028d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.inRange(cursor.getCount(), 0, 1); 1029d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor.moveToFirst()) { 1030d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd messagePartData = MessagePartData.createFromCursor(cursor); 1031d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1032d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } finally { 1033d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor != null) { 1034d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor.close(); 1035d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1036d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1037d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return messagePartData; 1038d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1039d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1040d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @DoesNotRunOnMainThread 1041d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static MessageData readMessageData(final DatabaseWrapper dbWrapper, 1042d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Uri smsMessageUri) { 1043d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isNotMainThread(); 1044d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MessageData message = null; 1045d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Cursor cursor = null; 1046d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 1047d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor = dbWrapper.query(DatabaseHelper.MESSAGES_TABLE, 1048d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MessageData.getProjection(), MessageColumns.SMS_MESSAGE_URI + "=?", 1049d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new String[] { smsMessageUri.toString() }, null, null, null); 1050d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.inRange(cursor.getCount(), 0, 1); 1051d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor.moveToFirst()) { 1052d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd message = new MessageData(); 1053d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd message.bind(cursor); 1054d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1055d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } finally { 1056d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor != null) { 1057d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor.close(); 1058d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1059d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1060d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return message; 1061d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1062d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1063d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @DoesNotRunOnMainThread 1064d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static MessageData readMessageData(final DatabaseWrapper dbWrapper, 1065d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String messageId) { 1066d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isNotMainThread(); 1067d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MessageData message = null; 1068d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Cursor cursor = null; 1069d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 1070d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor = dbWrapper.query(DatabaseHelper.MESSAGES_TABLE, 1071d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MessageData.getProjection(), MessageColumns._ID + "=?", 1072d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new String[] { messageId }, null, null, null); 1073d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.inRange(cursor.getCount(), 0, 1); 1074d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor.moveToFirst()) { 1075d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd message = new MessageData(); 1076d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd message.bind(cursor); 1077d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1078d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } finally { 1079d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor != null) { 1080d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor.close(); 1081d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1082d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1083d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return message; 1084d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1085d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1086d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 1087d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Read all the parts for a message 1088d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param dbWrapper database 1089d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param message read parts for this message 1090d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param checkAttachmentFilesExist check each attachment file and only include if file exists 1091d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 1092d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static void readMessagePartsData(final DatabaseWrapper dbWrapper, 1093d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final MessageData message, final boolean checkAttachmentFilesExist) { 1094d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ContentResolver contentResolver = 1095d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Factory.get().getApplicationContext().getContentResolver(); 1096d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Cursor cursor = null; 1097d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 1098d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor = dbWrapper.query(DatabaseHelper.PARTS_TABLE, 1099d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MessagePartData.getProjection(), PartColumns.MESSAGE_ID + "=?", 1100d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new String[] { message.getMessageId() }, null, null, null); 1101d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd while (cursor.moveToNext()) { 1102d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final MessagePartData messagePartData = MessagePartData.createFromCursor(cursor); 1103d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (checkAttachmentFilesExist && messagePartData.isAttachment() && 1104d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd !UriUtil.isBugleAppResource(messagePartData.getContentUri())) { 1105d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 1106d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Test that the file exists before adding the attachment to the draft 1107d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ParcelFileDescriptor fileDescriptor = 1108d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd contentResolver.openFileDescriptor( 1109d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd messagePartData.getContentUri(), "r"); 1110d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (fileDescriptor != null) { 1111d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd fileDescriptor.close(); 1112d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd message.addPart(messagePartData); 1113d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1114d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final IOException e) { 1115d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // The attachment's temp storage no longer exists, just ignore the file 1116d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final SecurityException e) { 1117d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Likely thrown by openFileDescriptor due to an expired access grant. 1118d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (LogUtil.isLoggable(LogUtil.BUGLE_TAG, LogUtil.DEBUG)) { 1119d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.d(LogUtil.BUGLE_TAG, "uri: " + messagePartData.getContentUri()); 1120d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1121d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1122d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 1123d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd message.addPart(messagePartData); 1124d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1125d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1126d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } finally { 1127d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor != null) { 1128d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor.close(); 1129d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1130d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1131d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1132d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1133d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 1134d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Write a message part to our local database 1135d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 1136d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param dbWrapper The database 1137d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param messagePart The message part to insert 1138d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @return The row id of the newly inserted part 1139d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 1140d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd static String insertNewMessagePartInTransaction(final DatabaseWrapper dbWrapper, 1141d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final MessagePartData messagePart, final String conversationId) { 1142d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isTrue(dbWrapper.getDatabase().inTransaction()); 1143d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isTrue(!TextUtils.isEmpty(messagePart.getMessageId())); 1144d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1145d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Insert a new part row 1146d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final SQLiteStatement insert = messagePart.getInsertStatement(dbWrapper, conversationId); 1147d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long rowNumber = insert.executeInsert(); 1148d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1149d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.inRange(rowNumber, 0, Long.MAX_VALUE); 1150d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String partId = Long.toString(rowNumber); 1151d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1152d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Update the part id 1153d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd messagePart.updatePartId(partId); 1154d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1155d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return partId; 1156d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1157d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1158d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 1159d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Insert a message and its parts into the table 1160d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 1161d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @DoesNotRunOnMainThread 1162d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static void insertNewMessageInTransaction(final DatabaseWrapper dbWrapper, 1163d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final MessageData message) { 1164d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isNotMainThread(); 1165d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isTrue(dbWrapper.getDatabase().inTransaction()); 1166d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1167d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Insert message row 1168d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final SQLiteStatement insert = message.getInsertStatement(dbWrapper); 1169d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long rowNumber = insert.executeInsert(); 1170d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1171d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.inRange(rowNumber, 0, Long.MAX_VALUE); 1172d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String messageId = Long.toString(rowNumber); 1173d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd message.updateMessageId(messageId); 1174d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Insert new parts 1175d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd for (final MessagePartData messagePart : message.getParts()) { 1176d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd messagePart.updateMessageId(messageId); 1177d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd insertNewMessagePartInTransaction(dbWrapper, messagePart, message.getConversationId()); 1178d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1179d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1180d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1181d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 1182d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Update a message and add its parts into the table 1183d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 1184d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @DoesNotRunOnMainThread 1185d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static void updateMessageInTransaction(final DatabaseWrapper dbWrapper, 1186d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final MessageData message) { 1187d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isNotMainThread(); 1188d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isTrue(dbWrapper.getDatabase().inTransaction()); 1189d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String messageId = message.getMessageId(); 1190d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Check message still exists (sms sync or delete might have purged it) 1191d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final MessageData current = BugleDatabaseOperations.readMessage(dbWrapper, messageId); 1192d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (current != null) { 1193d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Delete existing message parts) 1194d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd deletePartsForMessage(dbWrapper, message.getMessageId()); 1195d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Insert new parts 1196d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd for (final MessagePartData messagePart : message.getParts()) { 1197d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd messagePart.updatePartId(null); 1198d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd messagePart.updateMessageId(message.getMessageId()); 1199d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd insertNewMessagePartInTransaction(dbWrapper, messagePart, 1200d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd message.getConversationId()); 1201d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1202d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Update message row 1203d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ContentValues values = new ContentValues(); 1204d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd message.populate(values); 1205d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd updateMessageRowIfExists(dbWrapper, message.getMessageId(), values); 1206d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1207d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1208d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1209d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @DoesNotRunOnMainThread 1210d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static void updateMessageAndPartsInTransaction(final DatabaseWrapper dbWrapper, 1211d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final MessageData message, final List<MessagePartData> partsToUpdate) { 1212d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isNotMainThread(); 1213d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isTrue(dbWrapper.getDatabase().inTransaction()); 1214d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ContentValues values = new ContentValues(); 1215d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd for (final MessagePartData messagePart : partsToUpdate) { 1216d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.clear(); 1217d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd messagePart.populate(values); 1218d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd updatePartRowIfExists(dbWrapper, messagePart.getPartId(), values); 1219d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1220d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.clear(); 1221d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd message.populate(values); 1222d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd updateMessageRowIfExists(dbWrapper, message.getMessageId(), values); 1223d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1224d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1225d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 1226d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Delete all parts for a message 1227d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 1228d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd static void deletePartsForMessage(final DatabaseWrapper dbWrapper, 1229d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String messageId) { 1230d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int cnt = dbWrapper.delete(DatabaseHelper.PARTS_TABLE, 1231d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd PartColumns.MESSAGE_ID + " =?", 1232d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new String[] { messageId }); 1233d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.inRange(cnt, 0, Integer.MAX_VALUE); 1234d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1235d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1236d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 1237d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Delete one message and update the conversation (if necessary). 1238d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 1239d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @return number of rows deleted (should be 1 or 0). 1240d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 1241d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @DoesNotRunOnMainThread 1242d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static int deleteMessage(final DatabaseWrapper dbWrapper, final String messageId) { 1243d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isNotMainThread(); 1244d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd dbWrapper.beginTransaction(); 1245d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 1246d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Read message to find out which conversation it is in 1247d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final MessageData message = BugleDatabaseOperations.readMessage(dbWrapper, messageId); 1248d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1249d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd int count = 0; 1250d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (message != null) { 1251d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String conversationId = message.getConversationId(); 1252d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Delete message 1253d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd count = dbWrapper.delete(DatabaseHelper.MESSAGES_TABLE, 1254d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MessageColumns._ID + "=?", new String[] { messageId }); 1255d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1256d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (!deleteConversationIfEmptyInTransaction(dbWrapper, conversationId)) { 1257d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // TODO: Should we leave the conversation sort timestamp alone? 1258d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd refreshConversationMetadataInTransaction(dbWrapper, conversationId, 1259d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd false/* shouldAutoSwitchSelfId */, false/*archived*/); 1260d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1261d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1262d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd dbWrapper.setTransactionSuccessful(); 1263d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return count; 1264d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } finally { 1265d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd dbWrapper.endTransaction(); 1266d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1267d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1268d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1269d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 1270d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Deletes the conversation if there are zero non-draft messages left. 1271d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * <p> 1272d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * This is necessary because the telephony database has a trigger that deletes threads after 1273d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * their last message is deleted. We need to ensure that if a thread goes away, we also delete 1274d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * the conversation in Bugle. We don't store draft messages in telephony, so we ignore those 1275d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * when querying for the # of messages in the conversation. 1276d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 1277d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @return true if the conversation was deleted 1278d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 1279d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @DoesNotRunOnMainThread 1280d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static boolean deleteConversationIfEmptyInTransaction(final DatabaseWrapper dbWrapper, 1281d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String conversationId) { 1282d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isNotMainThread(); 1283d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isTrue(dbWrapper.getDatabase().inTransaction()); 1284d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Cursor cursor = null; 1285d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 1286d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // TODO: The refreshConversationMetadataInTransaction method below uses this 1287d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // same query; maybe they should share this logic? 1288d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1289d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Check to see if there are any (non-draft) messages in the conversation 1290d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor = dbWrapper.query(DatabaseHelper.MESSAGES_TABLE, 1291d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd REFRESH_CONVERSATION_MESSAGE_PROJECTION, 1292d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MessageColumns.CONVERSATION_ID + "=? AND " + 1293d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MessageColumns.STATUS + "!=" + MessageData.BUGLE_STATUS_OUTGOING_DRAFT, 1294d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new String[] { conversationId }, null, null, 1295d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MessageColumns.RECEIVED_TIMESTAMP + " DESC", "1" /* limit */); 1296d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor.getCount() == 0) { 1297d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd dbWrapper.delete(DatabaseHelper.CONVERSATIONS_TABLE, 1298d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ConversationColumns._ID + "=?", new String[] { conversationId }); 1299d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.i(TAG, 1300d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "BugleDatabaseOperations: Deleted empty conversation " + conversationId); 1301d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return true; 1302d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 1303d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return false; 1304d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1305d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } finally { 1306d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor != null) { 1307d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor.close(); 1308d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1309d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1310d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1311d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1312d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static final String[] REFRESH_CONVERSATION_MESSAGE_PROJECTION = new String[] { 1313d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MessageColumns._ID, 1314d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MessageColumns.RECEIVED_TIMESTAMP, 1315d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MessageColumns.SENDER_PARTICIPANT_ID 1316d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd }; 1317d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1318d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 1319d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Update conversation snippet, timestamp and optionally self id to match latest message in 1320d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * conversation. 1321d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 1322d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @DoesNotRunOnMainThread 1323d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static void refreshConversationMetadataInTransaction(final DatabaseWrapper dbWrapper, 1324d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String conversationId, final boolean shouldAutoSwitchSelfId, 1325d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd boolean keepArchived) { 1326d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isNotMainThread(); 1327d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isTrue(dbWrapper.getDatabase().inTransaction()); 1328d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Cursor cursor = null; 1329d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 1330d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Check to see if there are any (non-draft) messages in the conversation 1331d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor = dbWrapper.query(DatabaseHelper.MESSAGES_TABLE, 1332d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd REFRESH_CONVERSATION_MESSAGE_PROJECTION, 1333d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MessageColumns.CONVERSATION_ID + "=? AND " + 1334d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MessageColumns.STATUS + "!=" + MessageData.BUGLE_STATUS_OUTGOING_DRAFT, 1335d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new String[] { conversationId }, null, null, 1336d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MessageColumns.RECEIVED_TIMESTAMP + " DESC", "1" /* limit */); 1337d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1338d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor.moveToFirst()) { 1339d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Refresh latest message in conversation 1340d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String latestMessageId = cursor.getString(0); 1341d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long latestMessageTimestamp = cursor.getLong(1); 1342d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String senderParticipantId = cursor.getString(2); 1343d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final boolean senderBlocked = isBlockedParticipant(dbWrapper, senderParticipantId); 1344d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd updateConversationMetadataInTransaction(dbWrapper, conversationId, 1345d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd latestMessageId, latestMessageTimestamp, senderBlocked || keepArchived, 1346d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd shouldAutoSwitchSelfId); 1347d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1348d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } finally { 1349d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor != null) { 1350d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor.close(); 1351d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1352d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1353d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1354d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1355d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 1356d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * When moving/removing an existing message update conversation metadata if necessary 1357d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param dbWrapper db wrapper 1358d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param conversationId conversation to modify 1359d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param messageId message that is leaving the conversation 1360d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param shouldAutoSwitchSelfId should we try to auto-switch the conversation's self-id as a 1361d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * result of this call when we see a new latest message? 1362d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param keepArchived should we keep the conversation archived despite refresh 1363d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 1364d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @DoesNotRunOnMainThread 1365d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static void maybeRefreshConversationMetadataInTransaction( 1366d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final DatabaseWrapper dbWrapper, final String conversationId, final String messageId, 1367d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final boolean shouldAutoSwitchSelfId, final boolean keepArchived) { 1368d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isNotMainThread(); 1369d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd boolean refresh = true; 1370d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (!TextUtils.isEmpty(messageId)) { 1371d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd refresh = false; 1372d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Look for an existing conversation in the db with this conversation id 1373d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Cursor cursor = null; 1374d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 1375d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor = dbWrapper.query(DatabaseHelper.CONVERSATIONS_TABLE, 1376d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new String[] { ConversationColumns.LATEST_MESSAGE_ID }, 1377d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ConversationColumns._ID + "=?", 1378d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new String[] { conversationId }, 1379d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd null, null, null); 1380d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.inRange(cursor.getCount(), 0, 1); 1381d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor.moveToFirst()) { 1382d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd refresh = TextUtils.equals(cursor.getString(0), messageId); 1383d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1384d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } finally { 1385d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor != null) { 1386d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor.close(); 1387d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1388d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1389d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1390d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (refresh) { 1391d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // TODO: I think it is okay to delete the conversation if it is empty... 1392d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd refreshConversationMetadataInTransaction(dbWrapper, conversationId, 1393d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd shouldAutoSwitchSelfId, keepArchived); 1394d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1395d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1396d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1397d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1398d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1399d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // SQL statement to query latest message if for particular conversation 1400d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static final String QUERY_CONVERSATIONS_LATEST_MESSAGE_SQL = "SELECT " 1401d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + ConversationColumns.LATEST_MESSAGE_ID + " FROM " + DatabaseHelper.CONVERSATIONS_TABLE 1402d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + " WHERE " + ConversationColumns._ID + "=? LIMIT 1"; 1403d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1404d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 1405d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Note this is not thread safe so callers need to make sure they own the wrapper + statements 1406d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * while they call this and use the returned value. 1407d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 1408d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @DoesNotRunOnMainThread 1409d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static SQLiteStatement getQueryConversationsLatestMessageStatement( 1410d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final DatabaseWrapper db, final String conversationId) { 1411d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isNotMainThread(); 1412d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final SQLiteStatement query = db.getStatementInTransaction( 1413d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd DatabaseWrapper.INDEX_QUERY_CONVERSATIONS_LATEST_MESSAGE, 1414d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd QUERY_CONVERSATIONS_LATEST_MESSAGE_SQL); 1415d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd query.clearBindings(); 1416d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd query.bindString(1, conversationId); 1417d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return query; 1418d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1419d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1420d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // SQL statement to query latest message if for particular conversation 1421d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static final String QUERY_MESSAGES_LATEST_MESSAGE_SQL = "SELECT " 1422d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + MessageColumns._ID + " FROM " + DatabaseHelper.MESSAGES_TABLE 1423d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + " WHERE " + MessageColumns.CONVERSATION_ID + "=? ORDER BY " 1424d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + MessageColumns.RECEIVED_TIMESTAMP + " DESC LIMIT 1"; 1425d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1426d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 1427d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Note this is not thread safe so callers need to make sure they own the wrapper + statements 1428d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * while they call this and use the returned value. 1429d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 1430d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @DoesNotRunOnMainThread 1431d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static SQLiteStatement getQueryMessagesLatestMessageStatement( 1432d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final DatabaseWrapper db, final String conversationId) { 1433d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isNotMainThread(); 1434d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final SQLiteStatement query = db.getStatementInTransaction( 1435d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd DatabaseWrapper.INDEX_QUERY_MESSAGES_LATEST_MESSAGE, 1436d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd QUERY_MESSAGES_LATEST_MESSAGE_SQL); 1437d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd query.clearBindings(); 1438d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd query.bindString(1, conversationId); 1439d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return query; 1440d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1441d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1442d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 1443d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Update conversation metadata if necessary 1444d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param dbWrapper db wrapper 1445d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param conversationId conversation to modify 1446d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param shouldAutoSwitchSelfId should we try to auto-switch the conversation's self-id as a 1447d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * result of this call when we see a new latest message? 1448d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param keepArchived if the conversation should be kept archived 1449d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 1450d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @DoesNotRunOnMainThread 1451d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static void maybeRefreshConversationMetadataInTransaction( 1452d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final DatabaseWrapper dbWrapper, final String conversationId, 1453d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final boolean shouldAutoSwitchSelfId, boolean keepArchived) { 1454d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isNotMainThread(); 1455d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd String currentLatestMessageId = null; 1456d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd String latestMessageId = null; 1457d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 1458d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final SQLiteStatement currentLatestMessageIdSql = 1459d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd getQueryConversationsLatestMessageStatement(dbWrapper, conversationId); 1460d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd currentLatestMessageId = currentLatestMessageIdSql.simpleQueryForString(); 1461d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1462d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final SQLiteStatement latestMessageIdSql = 1463d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd getQueryMessagesLatestMessageStatement(dbWrapper, conversationId); 1464d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd latestMessageId = latestMessageIdSql.simpleQueryForString(); 1465d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final SQLiteDoneException e) { 1466d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "BugleDatabaseOperations: Query for latest message failed", e); 1467d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1468d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1469d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (TextUtils.isEmpty(currentLatestMessageId) || 1470d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd !TextUtils.equals(currentLatestMessageId, latestMessageId)) { 1471d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd refreshConversationMetadataInTransaction(dbWrapper, conversationId, 1472d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd shouldAutoSwitchSelfId, keepArchived); 1473d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1474d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1475d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1476d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd static boolean getConversationExists(final DatabaseWrapper dbWrapper, 1477d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String conversationId) { 1478d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Look for an existing conversation in the db with this conversation id 1479d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Cursor cursor = null; 1480d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 1481d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor = dbWrapper.query(DatabaseHelper.CONVERSATIONS_TABLE, 1482d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new String[] { /* No projection */}, 1483d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ConversationColumns._ID + "=?", 1484d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new String[] { conversationId }, 1485d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd null, null, null); 1486d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return cursor.getCount() == 1; 1487d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } finally { 1488d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor != null) { 1489d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor.close(); 1490d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1491d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1492d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1493d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1494d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** Preserve parts in message but clear the stored draft */ 1495d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static final int UPDATE_MODE_CLEAR_DRAFT = 1; 1496d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** Add the message as a draft */ 1497d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static final int UPDATE_MODE_ADD_DRAFT = 2; 1498d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1499d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 1500d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Update draft message for specified conversation 1501d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param dbWrapper local database (wrapped) 1502d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param conversationId conversation to update 1503d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param message Optional message to preserve attachments for (either as draft or for 1504d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * sending) 1505d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param updateMode either {@link #UPDATE_MODE_CLEAR_DRAFT} or 1506d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * {@link #UPDATE_MODE_ADD_DRAFT} 1507d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @return message id of newly written draft (else null) 1508d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 1509d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @DoesNotRunOnMainThread 1510d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static String updateDraftMessageData(final DatabaseWrapper dbWrapper, 1511d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String conversationId, @Nullable final MessageData message, 1512d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int updateMode) { 1513d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isNotMainThread(); 1514d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.notNull(conversationId); 1515d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.inRange(updateMode, UPDATE_MODE_CLEAR_DRAFT, UPDATE_MODE_ADD_DRAFT); 1516d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd String messageId = null; 1517d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Cursor cursor = null; 1518d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd dbWrapper.beginTransaction(); 1519d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 1520d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Find all draft parts for the current conversation 1521d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final SimpleArrayMap<Uri, MessagePartData> currentDraftParts = new SimpleArrayMap<>(); 1522d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor = dbWrapper.query(DatabaseHelper.DRAFT_PARTS_VIEW, 1523d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MessagePartData.getProjection(), 1524d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MessageColumns.CONVERSATION_ID + " =?", 1525d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new String[] { conversationId }, null, null, null); 1526d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd while (cursor.moveToNext()) { 1527d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final MessagePartData part = MessagePartData.createFromCursor(cursor); 1528d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (part.isAttachment()) { 1529d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd currentDraftParts.put(part.getContentUri(), part); 1530d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1531d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1532d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Optionally, preserve attachments for "message" 1533d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final boolean conversationExists = getConversationExists(dbWrapper, conversationId); 1534d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (message != null && conversationExists) { 1535d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd for (final MessagePartData part : message.getParts()) { 1536d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (part.isAttachment()) { 1537d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd currentDraftParts.remove(part.getContentUri()); 1538d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1539d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1540d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1541d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1542d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Delete orphan content 1543d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd for (int index = 0; index < currentDraftParts.size(); index++) { 1544d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final MessagePartData part = currentDraftParts.valueAt(index); 1545d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd part.destroySync(); 1546d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1547d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1548d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Delete existing draft (cascade deletes parts) 1549d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd dbWrapper.delete(DatabaseHelper.MESSAGES_TABLE, 1550d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MessageColumns.STATUS + "=? AND " + MessageColumns.CONVERSATION_ID + "=?", 1551d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new String[] { 1552d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Integer.toString(MessageData.BUGLE_STATUS_OUTGOING_DRAFT), 1553d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd conversationId 1554d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd }); 1555d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1556d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Write new draft 1557d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (updateMode == UPDATE_MODE_ADD_DRAFT && message != null 1558d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd && message.hasContent() && conversationExists) { 1559d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.equals(MessageData.BUGLE_STATUS_OUTGOING_DRAFT, 1560d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd message.getStatus()); 1561d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1562d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Now add draft to message table 1563d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd insertNewMessageInTransaction(dbWrapper, message); 1564d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd messageId = message.getMessageId(); 1565d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1566d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1567d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (conversationExists) { 1568d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd updateConversationDraftSnippetAndPreviewInTransaction( 1569d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd dbWrapper, conversationId, message); 1570d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1571d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (message != null && message.getSelfId() != null) { 1572d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd updateConversationSelfIdInTransaction(dbWrapper, conversationId, 1573d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd message.getSelfId()); 1574d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1575d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1576d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1577d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd dbWrapper.setTransactionSuccessful(); 1578d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } finally { 1579d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd dbWrapper.endTransaction(); 1580d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor != null) { 1581d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor.close(); 1582d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1583d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1584d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (LogUtil.isLoggable(TAG, LogUtil.VERBOSE)) { 1585d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.v(TAG, 1586d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "Updated draft message " + messageId + " for conversation " + conversationId); 1587d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1588d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return messageId; 1589d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1590d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1591d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 1592d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Read the first draft message associated with this conversation. 1593d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * If none present create an empty (sms) draft message. 1594d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 1595d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @DoesNotRunOnMainThread 1596d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static MessageData readDraftMessageData(final DatabaseWrapper dbWrapper, 1597d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String conversationId, final String conversationSelfId) { 1598d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isNotMainThread(); 1599d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MessageData message = null; 1600d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Cursor cursor = null; 1601d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd dbWrapper.beginTransaction(); 1602d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 1603d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor = dbWrapper.query(DatabaseHelper.MESSAGES_TABLE, 1604d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MessageData.getProjection(), 1605d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MessageColumns.STATUS + "=? AND " + MessageColumns.CONVERSATION_ID + "=?", 1606d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new String[] { 1607d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Integer.toString(MessageData.BUGLE_STATUS_OUTGOING_DRAFT), 1608d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd conversationId 1609d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd }, null, null, null); 1610d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.inRange(cursor.getCount(), 0, 1); 1611d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor.moveToFirst()) { 1612d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd message = new MessageData(); 1613d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd message.bindDraft(cursor, conversationSelfId); 1614d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd readMessagePartsData(dbWrapper, message, true); 1615d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Disconnect draft parts from DB 1616d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd for (final MessagePartData part : message.getParts()) { 1617d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd part.updatePartId(null); 1618d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd part.updateMessageId(null); 1619d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1620d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd message.updateMessageId(null); 1621d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1622d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd dbWrapper.setTransactionSuccessful(); 1623d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } finally { 1624d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd dbWrapper.endTransaction(); 1625d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor != null) { 1626d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor.close(); 1627d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1628d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1629d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return message; 1630d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1631d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1632d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Internal 1633d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static void addParticipantToConversation(final DatabaseWrapper dbWrapper, 1634d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ParticipantData participant, final String conversationId) { 1635d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String participantId = getOrCreateParticipantInTransaction(dbWrapper, participant); 1636d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.notNull(participantId); 1637d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1638d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Add the participant to the conversation participants table 1639d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ContentValues values = new ContentValues(); 1640d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(ConversationParticipantsColumns.CONVERSATION_ID, conversationId); 1641d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(ConversationParticipantsColumns.PARTICIPANT_ID, participantId); 1642d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd dbWrapper.insert(DatabaseHelper.CONVERSATION_PARTICIPANTS_TABLE, null, values); 1643d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1644d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1645d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 1646d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Get string used as canonical recipient for participant cache for sub id 1647d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 1648d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static String getCanonicalRecipientFromSubId(final int subId) { 1649d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return "SELF(" + subId + ")"; 1650d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1651d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1652d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 1653d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Maps from a sub id or phone number to a participant id if there is one. 1654d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 1655d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @return If the participant is available in our cache, or the DB, this returns the 1656d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * participant id for the given subid/phone number. Otherwise it returns null. 1657d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 1658d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @VisibleForTesting 1659d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static String getParticipantId(final DatabaseWrapper dbWrapper, 1660d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int subId, final String canonicalRecipient) { 1661d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // First check our memory cache for the participant Id 1662d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd String participantId; 1663d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd synchronized (sNormalizedPhoneNumberToParticipantIdCache) { 1664d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd participantId = sNormalizedPhoneNumberToParticipantIdCache.get(canonicalRecipient); 1665d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1666d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1667d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (participantId != null) { 1668d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return participantId; 1669d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1670d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1671d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // This code will only be executed for incremental additions. 1672d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Cursor cursor = null; 1673d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 1674d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (subId != ParticipantData.OTHER_THAN_SELF_SUB_ID) { 1675d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Now look for an existing participant in the db with this sub id. 1676d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor = dbWrapper.query(DatabaseHelper.PARTICIPANTS_TABLE, 1677d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new String[] {ParticipantColumns._ID}, 1678d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ParticipantColumns.SUB_ID + "=?", 1679d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new String[] { Integer.toString(subId) }, null, null, null); 1680d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 1681d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Look for existing participant with this normalized phone number and no subId. 1682d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor = dbWrapper.query(DatabaseHelper.PARTICIPANTS_TABLE, 1683d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new String[] {ParticipantColumns._ID}, 1684d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ParticipantColumns.NORMALIZED_DESTINATION + "=? AND " 1685d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + ParticipantColumns.SUB_ID + "=?", 1686d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new String[] {canonicalRecipient, Integer.toString(subId)}, 1687d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd null, null, null); 1688d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1689d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1690d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor.moveToFirst()) { 1691d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // TODO Is this assert correct for multi-sim where a new sim was put in? 1692d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isTrue(cursor.getCount() == 1); 1693d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1694d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // We found an existing participant in the database 1695d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd participantId = cursor.getString(0); 1696d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1697d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd synchronized (sNormalizedPhoneNumberToParticipantIdCache) { 1698d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Add it to the cache for next time 1699d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd sNormalizedPhoneNumberToParticipantIdCache.put(canonicalRecipient, 1700d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd participantId); 1701d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1702d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1703d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } finally { 1704d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor != null) { 1705d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor.close(); 1706d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1707d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1708d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return participantId; 1709d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1710d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1711d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @DoesNotRunOnMainThread 1712d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static ParticipantData getOrCreateSelf(final DatabaseWrapper dbWrapper, 1713d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int subId) { 1714d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isNotMainThread(); 1715d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ParticipantData participant = null; 1716d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd dbWrapper.beginTransaction(); 1717d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 1718d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ParticipantData shell = ParticipantData.getSelfParticipant(subId); 1719d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String participantId = getOrCreateParticipantInTransaction(dbWrapper, shell); 1720d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd participant = getExistingParticipant(dbWrapper, participantId); 1721d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd dbWrapper.setTransactionSuccessful(); 1722d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } finally { 1723d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd dbWrapper.endTransaction(); 1724d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1725d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return participant; 1726d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1727d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1728d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 1729d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Lookup and if necessary create a new participant 1730d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param dbWrapper Database wrapper 1731d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param participant Participant to find/create 1732d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @return participantId ParticipantId for existing or newly created participant 1733d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 1734d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @DoesNotRunOnMainThread 1735d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static String getOrCreateParticipantInTransaction(final DatabaseWrapper dbWrapper, 1736d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ParticipantData participant) { 1737d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isNotMainThread(); 1738d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isTrue(dbWrapper.getDatabase().inTransaction()); 1739d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd int subId = ParticipantData.OTHER_THAN_SELF_SUB_ID; 1740d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd String participantId = null; 1741d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd String canonicalRecipient = null; 1742d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (participant.isSelf()) { 1743d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd subId = participant.getSubId(); 1744d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd canonicalRecipient = getCanonicalRecipientFromSubId(subId); 1745d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 1746d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd canonicalRecipient = participant.getNormalizedDestination(); 1747d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1748d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.notNull(canonicalRecipient); 1749d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd participantId = getParticipantId(dbWrapper, subId, canonicalRecipient); 1750d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1751d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (participantId != null) { 1752d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return participantId; 1753d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1754d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1755d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (!participant.isContactIdResolved()) { 1756d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Refresh participant's name and avatar with matching contact in CP2. 1757d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ParticipantRefresh.refreshParticipant(dbWrapper, participant); 1758d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1759d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1760d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Insert the participant into the participants table 1761d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ContentValues values = participant.toContentValues(); 1762d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long participantRow = dbWrapper.insert(DatabaseHelper.PARTICIPANTS_TABLE, null, 1763d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values); 1764d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd participantId = Long.toString(participantRow); 1765d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.notNull(canonicalRecipient); 1766d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1767d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd synchronized (sNormalizedPhoneNumberToParticipantIdCache) { 1768d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Now that we've inserted it, add it to our cache 1769d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd sNormalizedPhoneNumberToParticipantIdCache.put(canonicalRecipient, participantId); 1770d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1771d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1772d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return participantId; 1773d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1774d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1775d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @DoesNotRunOnMainThread 1776d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static void updateDestination(final DatabaseWrapper dbWrapper, 1777d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String destination, final boolean blocked) { 1778d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isNotMainThread(); 1779d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ContentValues values = new ContentValues(); 1780d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd values.put(ParticipantColumns.BLOCKED, blocked ? 1 : 0); 1781d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd dbWrapper.update(DatabaseHelper.PARTICIPANTS_TABLE, values, 1782d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ParticipantColumns.NORMALIZED_DESTINATION + "=? AND " + 1783d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ParticipantColumns.SUB_ID + "=?", 1784d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new String[] { destination, Integer.toString( 1785d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ParticipantData.OTHER_THAN_SELF_SUB_ID) }); 1786d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1787d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1788d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @DoesNotRunOnMainThread 1789d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static String getConversationFromOtherParticipantDestination( 1790d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final DatabaseWrapper db, final String otherDestination) { 1791d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isNotMainThread(); 1792d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Cursor cursor = null; 1793d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 1794d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor = db.query(DatabaseHelper.CONVERSATIONS_TABLE, 1795d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new String[] { ConversationColumns._ID }, 1796d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ConversationColumns.OTHER_PARTICIPANT_NORMALIZED_DESTINATION + "=?", 1797d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new String[] { otherDestination }, null, null, null); 1798d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.inRange(cursor.getCount(), 0, 1); 1799d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor.moveToFirst()) { 1800d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return cursor.getString(0); 1801d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1802d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } finally { 1803d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor != null) { 1804d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor.close(); 1805d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1806d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1807d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return null; 1808d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1809d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1810d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1811d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 1812d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Get a list of conversations that contain any of participants specified. 1813d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 1814d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static HashSet<String> getConversationsForParticipants( 1815d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ArrayList<String> participantIds) { 1816d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final DatabaseWrapper db = DataModel.get().getDatabase(); 1817d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final HashSet<String> conversationIds = new HashSet<String>(); 1818d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1819d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String selection = ConversationParticipantsColumns.PARTICIPANT_ID + "=?"; 1820d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd for (final String participantId : participantIds) { 1821d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String[] selectionArgs = new String[] { participantId }; 1822d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Cursor cursor = db.query(DatabaseHelper.CONVERSATION_PARTICIPANTS_TABLE, 1823d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ConversationParticipantsQuery.PROJECTION, 1824d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd selection, selectionArgs, null, null, null); 1825d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1826d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor != null) { 1827d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 1828d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd while (cursor.moveToNext()) { 1829d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String conversationId = cursor.getString( 1830d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ConversationParticipantsQuery.INDEX_CONVERSATION_ID); 1831d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd conversationIds.add(conversationId); 1832d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1833d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } finally { 1834d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor.close(); 1835d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1836d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1837d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1838d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1839d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return conversationIds; 1840d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1841d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1842d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 1843d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Refresh conversation names/avatars based on a list of participants that are changed. 1844d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 1845d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @DoesNotRunOnMainThread 1846d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static void refreshConversationsForParticipants(final ArrayList<String> participants) { 1847d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isNotMainThread(); 1848d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final HashSet<String> conversationIds = getConversationsForParticipants(participants); 1849d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (conversationIds.size() > 0) { 1850d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd for (final String conversationId : conversationIds) { 1851d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd refreshConversation(conversationId); 1852d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1853d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1854d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MessagingContentProvider.notifyConversationListChanged(); 1855d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (LogUtil.isLoggable(TAG, LogUtil.VERBOSE)) { 1856d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.v(TAG, "Number of conversations refreshed:" + conversationIds.size()); 1857d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1858d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1859d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1860d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1861d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 1862d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Refresh conversation names/avatars based on a changed participant. 1863d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 1864d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @DoesNotRunOnMainThread 1865d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static void refreshConversationsForParticipant(final String participantId) { 1866d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isNotMainThread(); 1867d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ArrayList<String> participantList = new ArrayList<String>(1); 1868d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd participantList.add(participantId); 1869d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd refreshConversationsForParticipants(participantList); 1870d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1871d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1872d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 1873d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Refresh one conversation. 1874d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 1875d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static void refreshConversation(final String conversationId) { 1876d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final DatabaseWrapper db = DataModel.get().getDatabase(); 1877d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1878d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd db.beginTransaction(); 1879d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 1880d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd BugleDatabaseOperations.updateConversationNameAndAvatarInTransaction(db, 1881d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd conversationId); 1882d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd db.setTransactionSuccessful(); 1883d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } finally { 1884d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd db.endTransaction(); 1885d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1886d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1887d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MessagingContentProvider.notifyParticipantsChanged(conversationId); 1888d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MessagingContentProvider.notifyMessagesChanged(conversationId); 1889d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MessagingContentProvider.notifyConversationMetadataChanged(conversationId); 1890d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1891d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1892d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @DoesNotRunOnMainThread 1893d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static boolean updateRowIfExists(final DatabaseWrapper db, final String table, 1894d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String rowKey, final String rowId, final ContentValues values) { 1895d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isNotMainThread(); 1896d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final StringBuilder sb = new StringBuilder(); 1897d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ArrayList<String> whereValues = new ArrayList<String>(values.size() + 1); 1898d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd whereValues.add(rowId); 1899d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1900d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd for (final String key : values.keySet()) { 1901d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (sb.length() > 0) { 1902d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd sb.append(" OR "); 1903d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1904d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Object value = values.get(key); 1905d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd sb.append(key); 1906d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (value != null) { 1907d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd sb.append(" IS NOT ?"); 1908d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd whereValues.add(value.toString()); 1909d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 1910d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd sb.append(" IS NOT NULL"); 1911d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1912d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1913d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1914d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String whereClause = rowKey + "=?" + " AND (" + sb.toString() + ")"; 1915d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String [] whereValuesArray = whereValues.toArray(new String[whereValues.size()]); 1916d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int count = db.update(table, values, whereClause, whereValuesArray); 1917d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (count > 1) { 1918d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.w(LogUtil.BUGLE_TAG, "Updated more than 1 row " + count + "; " + table + 1919d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd " for " + rowKey + " = " + rowId + " (deleted?)"); 1920d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1921d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.inRange(count, 0, 1); 1922d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return (count >= 0); 1923d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1924d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd} 1925