diff --git a/src/com/android/mms/data/Contact.java b/src/com/android/mms/data/Contact.java index 774c6c980..65a02d5b1 100644 --- a/src/com/android/mms/data/Contact.java +++ b/src/com/android/mms/data/Contact.java @@ -54,6 +54,9 @@ public void onChange(boolean selfUpdate) { private String mNumber; private String mName; private String mNameAndNumber; // for display, e.g. Fred Flintstone <670-782-1123> + private boolean mNumberIsModified; // true if the number is modified + + private long mRecipientId; // used to find the Recipient cache entry private String mLabel; private long mPersonId; private int mPresenceResId; // TODO: make this a state instead of a res ID @@ -72,9 +75,9 @@ public interface UpdateListener { } private Contact(String number) { - mNumber = number; mName = ""; - updateNameAndNumber(); + setNumber(number); + mNumberIsModified = false; mLabel = ""; mPersonId = 0; mPresenceResId = 0; @@ -273,6 +276,15 @@ public synchronized String getNumber() { public synchronized void setNumber(String number) { mNumber = number; updateNameAndNumber(); + mNumberIsModified = true; + } + + public boolean isNumberModified() { + return mNumberIsModified; + } + + public void setIsNumberModified(boolean flag) { + mNumberIsModified = flag; } public synchronized String getName() { @@ -291,6 +303,14 @@ private void updateNameAndNumber() { mNameAndNumber = formatNameAndNumber(mName, mNumber); } + public synchronized long getRecipientId() { + return mRecipientId; + } + + public synchronized void setRecipientId(long id) { + mRecipientId = id; + } + public synchronized String getLabel() { return mLabel; } diff --git a/src/com/android/mms/data/ContactList.java b/src/com/android/mms/data/ContactList.java index aaab6dd7a..fd02b864a 100644 --- a/src/com/android/mms/data/ContactList.java +++ b/src/com/android/mms/data/ContactList.java @@ -39,11 +39,17 @@ public static ContactList getByNumbers(String semiSepNumbers, return list; } + /** + * Returns a ContactList for the corresponding recipient ids passed in. This method will + * create the contact if it doesn't exist, and would inject the recipient id into the contact. + */ public static ContactList getByIds(String spaceSepIds, boolean canBlock) { ContactList list = new ContactList(); - for (String number : RecipientIdCache.getNumbers(spaceSepIds)) { - if (!TextUtils.isEmpty(number)) { - list.add(Contact.get(number, canBlock)); + for (RecipientIdCache.Entry entry : RecipientIdCache.getAddresses(spaceSepIds)) { + if (entry != null && !TextUtils.isEmpty(entry.number)) { + Contact contact = Contact.get(entry.number, canBlock); + contact.setRecipientId(entry.id); + list.add(contact); } } return list; diff --git a/src/com/android/mms/data/RecipientIdCache.java b/src/com/android/mms/data/RecipientIdCache.java index b8cfe8a7e..4547a5dc0 100644 --- a/src/com/android/mms/data/RecipientIdCache.java +++ b/src/com/android/mms/data/RecipientIdCache.java @@ -6,22 +6,41 @@ import java.util.Map; import android.content.Context; +import android.content.ContentValues; +import android.content.ContentUris; import android.database.Cursor; import android.net.Uri; import android.text.TextUtils; import android.util.Log; +import android.provider.Telephony; import com.google.android.mms.util.SqliteWrapper; +import com.android.mms.LogTag; public class RecipientIdCache { - private static final String TAG = "RecipientIdCache"; + private static final String TAG = "Mms/cache"; + + private static Uri sAllCanonical = + Uri.parse("content://mms-sms/canonical-addresses"); + + private static Uri sSingleCanonicalAddressUri = + Uri.parse("content://mms-sms/canonical-address"); - private static Uri sAllCanonical = Uri.parse("content://mms-sms/canonical-addresses"); private static RecipientIdCache sInstance; static RecipientIdCache getInstance() { return sInstance; } - private final Map mCache; + private final Map mCache; private final Context mContext; + public static class Entry { + public long id; + public String number; + + public Entry(long id, String number) { + this.id = id; + this.number = number; + } + }; + static void init(Context context) { sInstance = new RecipientIdCache(context); new Thread(new Runnable() { @@ -32,7 +51,7 @@ public void run() { } RecipientIdCache(Context context) { - mCache = new HashMap(); + mCache = new HashMap(); mContext = context; } @@ -40,13 +59,15 @@ public static void fill() { Context context = sInstance.mContext; Cursor c = SqliteWrapper.query(context, context.getContentResolver(), sAllCanonical, null, null, null, null); + try { synchronized (sInstance) { // Technically we don't have to clear this because the stupid // canonical_addresses table is never GC'ed. sInstance.mCache.clear(); while (c.moveToNext()) { - String id = c.getString(0); + // TODO: don't hardcode the column indices + long id = c.getLong(0); String number = c.getString(1); sInstance.mCache.put(id, number); } @@ -56,32 +77,92 @@ public static void fill() { } } - public static List getNumbers(String spaceSepIds) { + public static List getAddresses(String spaceSepIds) { synchronized (sInstance) { - List numbers = new ArrayList(); + List numbers = new ArrayList(); String[] ids = spaceSepIds.split(" "); for (String id : ids) { - String number = sInstance.mCache.get(id); + long longId; + + try { + longId = Long.parseLong(id); + } catch (NumberFormatException ex) { + // skip this id + continue; + } + + String number = sInstance.mCache.get(longId); + if (number == null) { Log.w(TAG, "Recipient ID " + id + " not in DB!"); dump(); fill(); number = sInstance.mCache.get(id); - } + } + if (TextUtils.isEmpty(number)) { Log.w(TAG, "Recipient ID " + id + " has empty number!"); } else { - numbers.add(number); + numbers.add(new Entry(longId, number)); } } return numbers; } } + public static void updateNumbers(long threadId, ContactList contacts) { + long recipientId = 0; + + for (Contact contact : contacts) { + if (contact.isNumberModified()) { + contact.setIsNumberModified(false); + } else { + // if the contact's number wasn't modified, don't bother. + continue; + } + + recipientId = contact.getRecipientId(); + if (recipientId == 0) { + continue; + } + + String number1 = contact.getNumber(); + String number2 = sInstance.mCache.get(recipientId); + + if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) { + Log.d(TAG, "[RecipientIdCache] updateNumbers: comparing " + number1 + + " with " + number2); + } + + // if the numbers don't match, let's update the RecipientIdCache's number + // with the new number in the contact. + if (!number1.equalsIgnoreCase(number2)) { + sInstance.mCache.put(recipientId, number1); + sInstance.updateCanonicalAddressInDb(recipientId, number1); + } + } + } + + private void updateCanonicalAddressInDb(long id, String number) { + if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) { + Log.d(TAG, "[RecipientIdCache] updateCanonicalAddressInDb: id=" + id + + ", number=" + number); + } + + ContentValues values = new ContentValues(); + values.put(Telephony.CanonicalAddressesColumns.ADDRESS, number); + + StringBuilder buf = new StringBuilder(Telephony.CanonicalAddressesColumns._ID); + buf.append('=').append(id); + + Uri uri = ContentUris.withAppendedId(sSingleCanonicalAddressUri, id); + mContext.getContentResolver().update(uri, values, buf.toString(), null); + } + public static void dump() { synchronized (sInstance) { Log.d(TAG, "*** Recipient ID cache dump ***"); - for (String id : sInstance.mCache.keySet()) { + for (Long id : sInstance.mCache.keySet()) { Log.d(TAG, id + ": " + sInstance.mCache.get(id)); } } diff --git a/src/com/android/mms/data/WorkingMessage.java b/src/com/android/mms/data/WorkingMessage.java index 90740420a..8c131fc58 100644 --- a/src/com/android/mms/data/WorkingMessage.java +++ b/src/com/android/mms/data/WorkingMessage.java @@ -932,6 +932,9 @@ public void run() { }).start(); } + // update the Recipient cache with the new to address, if it's different + RecipientIdCache.updateNumbers(conv.getThreadId(), conv.getRecipients()); + // Mark the message as discarded because it is "off the market" after being sent. mDiscarded = true; }