diff --git a/src/org/thoughtcrime/securesms/ContactSelectionGroupsFragment.java b/src/org/thoughtcrime/securesms/ContactSelectionGroupsFragment.java index 94be129ac9..88421ba0f3 100644 --- a/src/org/thoughtcrime/securesms/ContactSelectionGroupsFragment.java +++ b/src/org/thoughtcrime/securesms/ContactSelectionGroupsFragment.java @@ -95,7 +95,7 @@ public class ContactSelectionGroupsFragment extends SherlockListFragment for (ContactData contactData : contactDataList) { for (NumberData numberData : contactData.numbers) { - recipientList.add(new Recipient(contactData.name, numberData.number, null)); + recipientList.add(new Recipient(contactData.name, numberData.number, null, null)); } } } diff --git a/src/org/thoughtcrime/securesms/ContactSelectionListFragment.java b/src/org/thoughtcrime/securesms/ContactSelectionListFragment.java index 2c27a1652b..a999df00cb 100644 --- a/src/org/thoughtcrime/securesms/ContactSelectionListFragment.java +++ b/src/org/thoughtcrime/securesms/ContactSelectionListFragment.java @@ -101,7 +101,7 @@ public class ContactSelectionListFragment extends SherlockListFragment for (ContactData contactData : selectedContacts.values()) { for (NumberData numberData : contactData.numbers) { - recipientList.add(new Recipient(contactData.name, numberData.number, null)); + recipientList.add(new Recipient(contactData.name, numberData.number, null, null)); } } diff --git a/src/org/thoughtcrime/securesms/ContactSelectionRecentFragment.java b/src/org/thoughtcrime/securesms/ContactSelectionRecentFragment.java index b6df83200e..bd4efc0eae 100644 --- a/src/org/thoughtcrime/securesms/ContactSelectionRecentFragment.java +++ b/src/org/thoughtcrime/securesms/ContactSelectionRecentFragment.java @@ -89,7 +89,7 @@ public class ContactSelectionRecentFragment extends SherlockListFragment for (ContactData contactData : selectedContacts.values()) { for (NumberData numberData : contactData.numbers) { - recipientList.add(new Recipient(contactData.name, numberData.number, null)); + recipientList.add(new Recipient(contactData.name, numberData.number, null, null)); } } diff --git a/src/org/thoughtcrime/securesms/ConversationAdapter.java b/src/org/thoughtcrime/securesms/ConversationAdapter.java index b322c4ce16..e0025ddd22 100644 --- a/src/org/thoughtcrime/securesms/ConversationAdapter.java +++ b/src/org/thoughtcrime/securesms/ConversationAdapter.java @@ -266,10 +266,10 @@ public class ConversationAdapter extends CursorAdapter { try { if (address == null) recipient = recipients.getPrimaryRecipient(); - else recipient = RecipientFactory.getRecipientsFromString(context, address).getPrimaryRecipient(); + else recipient = RecipientFactory.getRecipientsFromString(context, address, false).getPrimaryRecipient(); } catch (RecipientFormattingException e) { Log.w("ConversationAdapter", e); - recipient = new Recipient("Unknown", "Unknown", null); + recipient = new Recipient("Unknown", "Unknown", null, null); } return recipient; diff --git a/src/org/thoughtcrime/securesms/ConversationItem.java b/src/org/thoughtcrime/securesms/ConversationItem.java index 2fb1f22430..266940b019 100644 --- a/src/org/thoughtcrime/securesms/ConversationItem.java +++ b/src/org/thoughtcrime/securesms/ConversationItem.java @@ -51,8 +51,8 @@ import org.thoughtcrime.securesms.database.model.NotificationMmsMessageRecord; import org.thoughtcrime.securesms.mms.Slide; import org.thoughtcrime.securesms.mms.SlideDeck; import org.thoughtcrime.securesms.protocol.Tag; +import org.thoughtcrime.securesms.recipients.ContactPhotoFactory; import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.service.SendReceiveService; import java.io.File; @@ -283,20 +283,9 @@ public class ConversationItem extends LinearLayout { } } - private void setContactPhotoForUserIdentity() { - Uri selfIdentityContact = ContactIdentityManager.getInstance(context).getSelfIdentityUri(); - - if (selfIdentityContact!= null) { - Recipient recipient = RecipientFactory.getRecipientForUri(context, selfIdentityContact); - if (recipient != null) { - contactPhoto.setImageBitmap(recipient.getContactPhoto()); - return; - } - } else { - contactPhoto.setImageDrawable(context.getResources().getDrawable(R.drawable.ic_contact_picture)); - } - + Uri uri = ContactIdentityManager.getInstance(context).getSelfIdentityUri(); + contactPhoto.setImageBitmap(ContactPhotoFactory.getLocalUserContactPhoto(context, uri)); contactPhoto.setVisibility(View.VISIBLE); } diff --git a/src/org/thoughtcrime/securesms/ConversationListActivity.java b/src/org/thoughtcrime/securesms/ConversationListActivity.java index 39d4eeb6e2..1c76c00846 100644 --- a/src/org/thoughtcrime/securesms/ConversationListActivity.java +++ b/src/org/thoughtcrime/securesms/ConversationListActivity.java @@ -329,7 +329,7 @@ public class ConversationListActivity extends SherlockFragmentActivity if (intent.getAction() != null && intent.getAction().equals("android.intent.action.SENDTO")) { Log.w("ConversationListActivity", "Intent has sendto action..."); try { - recipients = RecipientFactory.getRecipientsFromString(this, intent.getData().getSchemeSpecificPart()); + recipients = RecipientFactory.getRecipientsFromString(this, intent.getData().getSchemeSpecificPart(), false); thread = DatabaseFactory.getThreadDatabase(this).getThreadIdIfExistsFor(recipients); } catch (RecipientFormattingException rfe) { recipients = null; diff --git a/src/org/thoughtcrime/securesms/ConversationListAdapter.java b/src/org/thoughtcrime/securesms/ConversationListAdapter.java index adc881a1f6..ca61004ad5 100644 --- a/src/org/thoughtcrime/securesms/ConversationListAdapter.java +++ b/src/org/thoughtcrime/securesms/ConversationListAdapter.java @@ -18,6 +18,7 @@ package org.thoughtcrime.securesms; import android.content.Context; import android.database.Cursor; +import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.CursorAdapter; @@ -37,9 +38,10 @@ import java.util.Set; * * @author Moxie Marlinspike */ -public class ConversationListAdapter extends CursorAdapter { +public class ConversationListAdapter extends CursorAdapter { private final Context context; + private final LayoutInflater inflater; private final Set batchSet = Collections.synchronizedSet(new HashSet()); private boolean batchMode = false; @@ -47,21 +49,19 @@ public class ConversationListAdapter extends CursorAdapter { public ConversationListAdapter(Context context, Cursor cursor) { super(context, cursor); this.context = context; + this.inflater = LayoutInflater.from(context); } @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { - ConversationListItem view = new ConversationListItem(context, batchSet); - bindView(view, context, cursor); - - return view; + return inflater.inflate(R.layout.conversation_list_item_view, parent, false); } @Override public void bindView(View view, Context context, Cursor cursor) { long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.ID)); String recipientId = cursor.getString(cursor.getColumnIndexOrThrow(ThreadDatabase.RECIPIENT_IDS)); - Recipients recipients = RecipientFactory.getRecipientsForIds(context, recipientId); + Recipients recipients = RecipientFactory.getRecipientsForIds(context, recipientId, true); long date = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.DATE)); long count = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.MESSAGE_COUNT)); @@ -70,7 +70,7 @@ public class ConversationListAdapter extends CursorAdapter { ThreadRecord thread = new ThreadRecord(context, recipients, date, count, read == 1, threadId); setBody(cursor, thread); - ((ConversationListItem)view).set(thread, batchMode); + ((ConversationListItem)view).set(thread, batchSet, batchMode); } protected void filterBody(ThreadRecord thread, String body) { diff --git a/src/org/thoughtcrime/securesms/ConversationListItem.java b/src/org/thoughtcrime/securesms/ConversationListItem.java index 4d51688481..692422de1a 100644 --- a/src/org/thoughtcrime/securesms/ConversationListItem.java +++ b/src/org/thoughtcrime/securesms/ConversationListItem.java @@ -22,6 +22,7 @@ import android.graphics.Color; import android.graphics.Typeface; import android.net.Uri; import android.os.Build; +import android.os.Handler; import android.provider.Contacts.Intents; import android.provider.ContactsContract.QuickContact; import android.text.Spannable; @@ -30,7 +31,6 @@ import android.text.format.DateUtils; import android.text.style.ForegroundColorSpan; import android.text.style.StyleSpan; import android.util.AttributeSet; -import android.view.LayoutInflater; import android.view.View; import android.widget.CheckBox; import android.widget.CompoundButton; @@ -52,7 +52,9 @@ import java.util.Set; * @author Moxie Marlinspike */ -public class ConversationListItem extends RelativeLayout { +public class ConversationListItem extends RelativeLayout + implements Recipient.RecipientModifiedListener +{ private Context context; private Set selectedThreads; @@ -62,18 +64,26 @@ public class ConversationListItem extends RelativeLayout { private TextView fromView; private TextView dateView; private CheckBox checkbox; + private long count; + private boolean read; private ImageView contactPhotoImage; private QuickContactBadge contactPhotoBadge; - public ConversationListItem(Context context, Set selectedThreads) { + private final Handler handler = new Handler(); + + public ConversationListItem(Context context) { super(context); + this.context = context; + } - LayoutInflater li = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - li.inflate(R.layout.conversation_list_item_view, this, true); + public ConversationListItem(Context context, AttributeSet attrs) { + super(context, attrs); + this.context = context; + } - this.context = context; - this.selectedThreads = selectedThreads; + @Override + protected void onFinishInflate() { this.subjectView = (TextView)findViewById(R.id.subject); this.fromView = (TextView)findViewById(R.id.from); this.dateView = (TextView)findViewById(R.id.date); @@ -86,14 +96,14 @@ public class ConversationListItem extends RelativeLayout { intializeListeners(); } - public ConversationListItem(Context context, AttributeSet attrs) { - super(context, attrs); - } + public void set(ThreadRecord thread, Set selectedThreads, boolean batchMode) { + this.selectedThreads = selectedThreads; + this.recipients = thread.getRecipients(); + this.threadId = thread.getThreadId(); + this.count = thread.getCount(); + this.read = thread.isRead(); - public void set(ThreadRecord thread, boolean batchMode) { - this.recipients = thread.getRecipients(); - this.threadId = thread.getThreadId(); - this.fromView.setText(formatFrom(recipients, thread.getCount(), thread.isRead())); + this.fromView.setText(formatFrom(recipients, count, read)); if (thread.isKeyExchange()) this.subjectView.setText(R.string.ConversationListItem_key_exchange_message, @@ -114,6 +124,7 @@ public class ConversationListItem extends RelativeLayout { else checkbox.setVisibility(View.GONE); setContactPhoto(this.recipients.getPrimaryRecipient()); + this.recipients.setListener(this); } private void intializeListeners() { @@ -188,4 +199,15 @@ public class ConversationListItem extends RelativeLayout { else selectedThreads.remove(threadId); } } + + @Override + public void onModified(Recipient recipient) { + handler.post(new Runnable() { + @Override + public void run() { + ConversationListItem.this.fromView.setText(formatFrom(recipients, count, read)); + setContactPhoto(ConversationListItem.this.recipients.getPrimaryRecipient()); + } + }); + } } diff --git a/src/org/thoughtcrime/securesms/components/RecipientsPanel.java b/src/org/thoughtcrime/securesms/components/RecipientsPanel.java index a9f1c49f82..8e7770be6b 100644 --- a/src/org/thoughtcrime/securesms/components/RecipientsPanel.java +++ b/src/org/thoughtcrime/securesms/components/RecipientsPanel.java @@ -81,7 +81,7 @@ public class RecipientsPanel extends RelativeLayout { public Recipients getRecipients() throws RecipientFormattingException { String rawText = recipientsText.getText().toString(); - Recipients recipients = RecipientFactory.getRecipientsFromString(getContext(), rawText); + Recipients recipients = RecipientFactory.getRecipientsFromString(getContext(), rawText, false); if (recipients.isEmpty()) throw new RecipientFormattingException("Recipient List Is Empty!"); diff --git a/src/org/thoughtcrime/securesms/contacts/RecipientsEditor.java b/src/org/thoughtcrime/securesms/contacts/RecipientsEditor.java index 3f4d9bc9ab..721db41ef5 100644 --- a/src/org/thoughtcrime/securesms/contacts/RecipientsEditor.java +++ b/src/org/thoughtcrime/securesms/contacts/RecipientsEditor.java @@ -17,15 +17,6 @@ package org.thoughtcrime.securesms.contacts; -import java.util.ArrayList; -import java.util.List; - -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.recipients.RecipientFactory; -import org.thoughtcrime.securesms.recipients.RecipientFormattingException; -import org.thoughtcrime.securesms.recipients.Recipients; -import org.thoughtcrime.securesms.recipients.RecipientsFormatter; - import android.content.Context; import android.telephony.PhoneNumberUtils; import android.text.Annotation; @@ -39,11 +30,20 @@ import android.text.TextUtils; import android.text.TextWatcher; import android.util.AttributeSet; import android.util.Log; -import android.view.MotionEvent; import android.view.ContextMenu.ContextMenuInfo; +import android.view.MotionEvent; import android.view.inputmethod.EditorInfo; import android.widget.MultiAutoCompleteTextView; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.recipients.RecipientFactory; +import org.thoughtcrime.securesms.recipients.RecipientFormattingException; +import org.thoughtcrime.securesms.recipients.Recipients; +import org.thoughtcrime.securesms.recipients.RecipientsFormatter; + +import java.util.ArrayList; +import java.util.List; + /** * Provide UI for editing the recipients of multi-media messages. */ @@ -52,7 +52,7 @@ public class RecipientsEditor extends MultiAutoCompleteTextView { private final RecipientsEditorTokenizer mTokenizer; private char mLastSeparator = ','; private Context mContext; - + public RecipientsEditor(Context context, AttributeSet attrs) { super(context, attrs, android.R.attr.autoCompleteTextViewStyle); mContext = context; @@ -131,7 +131,7 @@ public class RecipientsEditor extends MultiAutoCompleteTextView { public Recipients constructContactsFromInput() { Recipients r = null; try { - r = RecipientFactory.getRecipientsFromString(mContext, mTokenizer.getRawString() ); + r = RecipientFactory.getRecipientsFromString(mContext, mTokenizer.getRawString(), false); } catch (RecipientFormattingException e) { Log.w( "RecipientsEditor", e); } diff --git a/src/org/thoughtcrime/securesms/crypto/DecryptingQueue.java b/src/org/thoughtcrime/securesms/crypto/DecryptingQueue.java index cfd7487bad..e06c761f77 100644 --- a/src/org/thoughtcrime/securesms/crypto/DecryptingQueue.java +++ b/src/org/thoughtcrime/securesms/crypto/DecryptingQueue.java @@ -1,6 +1,6 @@ -/** +/** * Copyright (C) 2011 Whisper Systems - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or @@ -10,15 +10,15 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.thoughtcrime.securesms.crypto; -import java.io.IOException; -import java.util.LinkedList; -import java.util.List; +import android.content.Context; +import android.database.Cursor; +import android.util.Log; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.EncryptingMmsDatabase; @@ -38,13 +38,14 @@ import ws.com.google.android.mms.ContentType; import ws.com.google.android.mms.MmsException; import ws.com.google.android.mms.pdu.MultimediaMessagePdu; import ws.com.google.android.mms.pdu.PduParser; -import android.content.Context; -import android.database.Cursor; -import android.util.Log; + +import java.io.IOException; +import java.util.LinkedList; +import java.util.List; /** * A work queue for processing a number of encryption operations. - * + * * @author Moxie Marlinspike */ @@ -52,12 +53,12 @@ public class DecryptingQueue { private static List workQueue = new LinkedList(); private static Thread workerThread; - + static { workerThread = new WorkerThread(workQueue, "Async Decryption Thread"); workerThread.start(); - } - + } + public static void scheduleDecryption(Context context, MasterSecret masterSecret, long messageId, long threadId, MultimediaMessagePdu mms) { MmsDecryptionItem runnable = new MmsDecryptionItem(context, masterSecret, messageId, threadId, mms); synchronized (workQueue) { @@ -65,7 +66,7 @@ public class DecryptingQueue { workQueue.notifyAll(); } } - + public static void scheduleDecryption(Context context, MasterSecret masterSecret, long messageId, String originator, String body) { DecryptionWorkItem runnable = new DecryptionWorkItem(context, masterSecret, messageId, body, originator); synchronized (workQueue) { @@ -73,16 +74,16 @@ public class DecryptingQueue { workQueue.notifyAll(); } } - + public static void schedulePendingDecrypts(Context context, MasterSecret masterSecret) { Cursor cursor = null; Log.w("DecryptingQueue", "Processing pending decrypts..."); - + try { cursor = DatabaseFactory.getSmsDatabase(context).getDecryptInProgressMessages(); if (cursor == null || cursor.getCount() == 0 || !cursor.moveToFirst()) return; - + do { scheduleDecryptFromCursor(context, masterSecret, cursor); } while (cursor.moveToNext()); @@ -94,12 +95,12 @@ public class DecryptingQueue { public static void scheduleRogueMessages(Context context, MasterSecret masterSecret, Recipient recipient) { Cursor cursor = null; - + try { cursor = DatabaseFactory.getSmsDatabase(context).getEncryptedRogueMessages(recipient); if (cursor == null || cursor.getCount() == 0 || !cursor.moveToFirst()) return; - + do { DatabaseFactory.getSmsDatabase(context).markAsDecrypting(cursor.getColumnIndexOrThrow(SmsDatabase.ID)); scheduleDecryptFromCursor(context, masterSecret, cursor); @@ -107,9 +108,9 @@ public class DecryptingQueue { } finally { if (cursor != null) cursor.close(); - } + } } - + private static void scheduleDecryptFromCursor(Context context, MasterSecret masterSecret, Cursor cursor) { long id = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.ID)); String originator = cursor.getString(cursor.getColumnIndexOrThrow(SmsDatabase.ADDRESS)); @@ -117,14 +118,14 @@ public class DecryptingQueue { scheduleDecryption(context, masterSecret, id, originator, body); } - + private static class MmsDecryptionItem implements Runnable { private long messageId; private long threadId; private Context context; private MasterSecret masterSecret; private MultimediaMessagePdu pdu; - + public MmsDecryptionItem(Context context, MasterSecret masterSecret, long messageId, long threadId, MultimediaMessagePdu pdu) { this.context = context; this.masterSecret = masterSecret; @@ -132,7 +133,7 @@ public class DecryptingQueue { this.threadId = threadId; this.pdu = pdu; } - + private byte[] getEncryptedData() { for (int i=0;i addressCache = new HashMap(); - private final Map idCache = Collections.synchronizedMap(new HashMap()); + + private final Map addressCache = Collections.synchronizedMap(new HashMap()); + private final Map idCache = Collections.synchronizedMap(new HashMap()); public static CanonicalAddressDatabase getInstance(Context context) { synchronized (lock) { @@ -58,18 +60,45 @@ public class CanonicalAddressDatabase { private CanonicalAddressDatabase(Context context) { databaseHelper = new DatabaseHelper(context, DATABASE_NAME, null, DATABASE_VERSION); + fillCache(); + } + + private void fillCache() { + Cursor cursor = null; + + try { + SQLiteDatabase db = databaseHelper.getReadableDatabase(); + cursor = db.query(TABLE, null, null, null, null, null, null); + + while (cursor != null && cursor.moveToNext()) { + long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID_COLUMN)); + String address = cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS_COLUMN)); + + if (address == null || address.trim().length() == 0) + address = "Anonymous"; + + idCache.put(id+"", address); + addressCache.put(address, id); + } + } finally { + if (cursor != null) + cursor.close(); + } } public String getAddressFromId(String id) { if (id == null || id.trim().equals("")) return "Anonymous"; String cachedAddress = idCache.get(id); + if (cachedAddress != null) return cachedAddress; Cursor cursor = null; try { + Log.w("CanonicalAddressDatabase", "Hitting DB on query [ID]."); + SQLiteDatabase db = databaseHelper.getReadableDatabase(); cursor = db.query(TABLE, null, ID_COLUMN + " = ?", new String[] {id+""}, null, null, null); @@ -127,7 +156,7 @@ public class CanonicalAddressDatabase { private long getCanonicalAddressFromDatabase(String address) { Cursor cursor = null; try { - SQLiteDatabase db = databaseHelper.getReadableDatabase(); + SQLiteDatabase db = databaseHelper.getWritableDatabase(); String[] selectionArguments = new String[] {address}; cursor = db.query(TABLE, ID_PROJECTION, SELECTION, selectionArguments, null, null, null); diff --git a/src/org/thoughtcrime/securesms/database/MmsDatabase.java b/src/org/thoughtcrime/securesms/database/MmsDatabase.java index dfefaababc..0bbedc9c59 100644 --- a/src/org/thoughtcrime/securesms/database/MmsDatabase.java +++ b/src/org/thoughtcrime/securesms/database/MmsDatabase.java @@ -140,7 +140,7 @@ public class MmsDatabase extends Database { try { EncodedStringValue encodedString = headers.getEncodedStringValue(PduHeaders.FROM); String fromString = new String(encodedString.getTextString(), CharacterSets.MIMENAME_ISO_8859_1); - Recipients recipients = RecipientFactory.getRecipientsFromString(context, fromString); + Recipients recipients = RecipientFactory.getRecipientsFromString(context, fromString, false); return DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients); } catch (UnsupportedEncodingException e) { throw new AssertionError(e); diff --git a/src/org/thoughtcrime/securesms/database/SmsDatabase.java b/src/org/thoughtcrime/securesms/database/SmsDatabase.java index 64cbf9a8c1..16944b7d4e 100644 --- a/src/org/thoughtcrime/securesms/database/SmsDatabase.java +++ b/src/org/thoughtcrime/securesms/database/SmsDatabase.java @@ -84,7 +84,7 @@ public class SmsDatabase extends Database { private long insertMessageReceived(SmsMessage message, String body, long type) { List recipientList = new ArrayList(1); - recipientList.add(new Recipient(null, message.getDisplayOriginatingAddress(), null)); + recipientList.add(new Recipient(null, message.getDisplayOriginatingAddress(), null, null)); Recipients recipients = new Recipients(recipientList); long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients); diff --git a/src/org/thoughtcrime/securesms/database/SmsMigrator.java b/src/org/thoughtcrime/securesms/database/SmsMigrator.java index cbaa47b45e..51e6536bde 100644 --- a/src/org/thoughtcrime/securesms/database/SmsMigrator.java +++ b/src/org/thoughtcrime/securesms/database/SmsMigrator.java @@ -128,7 +128,7 @@ public class SmsMigrator { try { if (sb.length() == 0) return null; - else return RecipientFactory.getRecipientsFromString(context, sb.toString()); + else return RecipientFactory.getRecipientsFromString(context, sb.toString(), true); } catch (RecipientFormattingException rfe) { Log.w("SmsMigrator", rfe); return null; diff --git a/src/org/thoughtcrime/securesms/recipients/ContactPhotoFactory.java b/src/org/thoughtcrime/securesms/recipients/ContactPhotoFactory.java new file mode 100644 index 0000000000..d579e63af8 --- /dev/null +++ b/src/org/thoughtcrime/securesms/recipients/ContactPhotoFactory.java @@ -0,0 +1,69 @@ +package org.thoughtcrime.securesms.recipients; + +import android.content.Context; +import android.database.Cursor; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.net.Uri; +import android.provider.ContactsContract; +import android.provider.ContactsContract.Contacts; + +import org.thoughtcrime.securesms.R; + +import java.io.InputStream; + +public class ContactPhotoFactory { + + private static final Object defaultPhotoLock = new Object(); + private static final Object localUserLock = new Object(); + + private static Bitmap defaultContactPhoto; + private static Bitmap localUserContactPhoto; + + private static final String[] CONTENT_URI_PROJECTION = new String[] { + ContactsContract.Contacts._ID, + ContactsContract.Contacts.DISPLAY_NAME, + ContactsContract.Contacts.LOOKUP_KEY + }; + + public static Bitmap getDefaultContactPhoto(Context context) { + synchronized (defaultPhotoLock) { + if (defaultContactPhoto == null) + defaultContactPhoto = BitmapFactory.decodeResource(context.getResources(), + R.drawable.ic_contact_picture); + } + + return defaultContactPhoto; + } + + public static Bitmap getLocalUserContactPhoto(Context context, Uri uri) { + synchronized (localUserLock) { + if (localUserContactPhoto == null) { + Cursor cursor = context.getContentResolver().query(uri, CONTENT_URI_PROJECTION, + null, null, null); + + if (cursor != null && cursor.moveToFirst()) { + localUserContactPhoto = getContactPhoto(context, Uri.withAppendedPath(Contacts.CONTENT_URI, + cursor.getLong(0) + "")); + } else { + localUserContactPhoto = getDefaultContactPhoto(context); + } + } + } + + return localUserContactPhoto; + } + + public static void clearCache() { + synchronized (localUserLock) { + localUserContactPhoto = null; + } + } + + private static Bitmap getContactPhoto(Context context, Uri uri) { + InputStream inputStream = ContactsContract.Contacts.openContactPhotoInputStream(context.getContentResolver(), uri); + + if (inputStream == null) return ContactPhotoFactory.getDefaultContactPhoto(context); + else return BitmapFactory.decodeStream(inputStream); + } +} diff --git a/src/org/thoughtcrime/securesms/recipients/Recipient.java b/src/org/thoughtcrime/securesms/recipients/Recipient.java index 63fecf6b5a..ba4f7a53bf 100644 --- a/src/org/thoughtcrime/securesms/recipients/Recipient.java +++ b/src/org/thoughtcrime/securesms/recipients/Recipient.java @@ -21,6 +21,10 @@ import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; +import org.thoughtcrime.securesms.recipients.RecipientProvider.RecipientDetails; + +import java.util.concurrent.atomic.AtomicReference; + public class Recipient implements Parcelable { public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { @@ -33,35 +37,69 @@ public class Recipient implements Parcelable { } }; - private final String name; + private final AtomicReference name = new AtomicReference(null); + private final AtomicReference contactPhoto = new AtomicReference(null); + private final AtomicReference contactUri = new AtomicReference(null); + private final String number; - private Uri contactUri; - private Bitmap contactPhoto; - public Recipient(String name, String number, Uri contactUri, Bitmap contactPhoto) { - this(name, number, contactPhoto); - this.contactUri = contactUri; - } + private RecipientModifiedListener listener; + private boolean asynchronousUpdateComplete = false; + +// public Recipient(String name, String number, Uri contactUri, Bitmap contactPhoto) { +// this(name, number, contactPhoto); +// this.contactUri = contactUri; +// } + +// public Recipient(String number, Bitmap contactPhoto, +// ListenableFutureTask future) +// { +// this.number = number; +// this.contactUri = null; +// this.contactPhoto.set(contactPhoto); +// +// future.setListener(new FutureTaskListener() { +// @Override +// public void onSuccess(RecipientDetails result) { +// if (result != null) { +// Recipient.this.name.set(result.name); +// Recipient.this.contactPhoto.set(result.avatar); +// +// synchronized(this) { +// if (listener == null) asynchronousUpdateComplete = true; +// else listener.onModified(Recipient.this); +// } +// } +// } +// +// @Override +// public void onFailure(Throwable error) { +// Log.w("Recipient", error); +// } +// }); +// } - public Recipient(String name, String number, Bitmap contactPhoto) { - this.name = name; - this.number = number; - this.contactPhoto = contactPhoto; + public Recipient(String name, String number, Uri contactUri, Bitmap contactPhoto) { + this.number = number; + this.contactUri.set(contactUri); + this.name.set(name); + this.contactPhoto.set(contactPhoto); } public Recipient(Parcel in) { - this.name = in.readString(); - this.number = in.readString(); - this.contactUri = in.readParcelable(null); - this.contactPhoto = in.readParcelable(null); + this.number = in.readString(); + + this.name.set(in.readString()); + this.contactUri.set((Uri)in.readParcelable(null)); + this.contactPhoto.set((Bitmap)in.readParcelable(null)); } public Uri getContactUri() { - return this.contactUri; + return this.contactUri.get(); } public String getName() { - return name; + return name.get(); } public String getNumber() { @@ -72,20 +110,44 @@ public class Recipient implements Parcelable { return 0; } + public void updateAsynchronousContent(RecipientDetails result) { + if (result != null) { + Recipient.this.name.set(result.name); + Recipient.this.contactUri.set(result.contactUri); + Recipient.this.contactPhoto.set(result.avatar); + + synchronized(this) { + if (listener == null) asynchronousUpdateComplete = true; + else listener.onModified(Recipient.this); + } + } + } + + public synchronized void setListener(RecipientModifiedListener listener) { + this.listener = listener; + if (asynchronousUpdateComplete) { + if (listener != null) + listener.onModified(this); + asynchronousUpdateComplete = false; + } + } + public void writeToParcel(Parcel dest, int flags) { - dest.writeString(name); dest.writeString(number); - dest.writeParcelable(contactUri, 0); - dest.writeParcelable(contactPhoto, 0); + dest.writeString(name.get()); + dest.writeParcelable(contactUri.get(), 0); + dest.writeParcelable(contactPhoto.get(), 0); } public String toShortString() { - return (name == null ? number : name); + return (name.get() == null ? number : name.get()); } public Bitmap getContactPhoto() { - return contactPhoto; + return contactPhoto.get(); } - + public static interface RecipientModifiedListener { + public void onModified(Recipient recipient); + } } diff --git a/src/org/thoughtcrime/securesms/recipients/RecipientFactory.java b/src/org/thoughtcrime/securesms/recipients/RecipientFactory.java index 573ec1b9bf..732ff4c60d 100644 --- a/src/org/thoughtcrime/securesms/recipients/RecipientFactory.java +++ b/src/org/thoughtcrime/securesms/recipients/RecipientFactory.java @@ -17,44 +17,19 @@ package org.thoughtcrime.securesms.recipients; import android.content.Context; -import android.graphics.BitmapFactory; -import android.net.Uri; -import android.util.Log; -import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.util.NumberUtil; -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; -import java.util.Map; import java.util.StringTokenizer; public class RecipientFactory { - private static final Map recipientCache = Collections.synchronizedMap(new LRUHashMap()); - private static final Map recipientIdCache = Collections.synchronizedMap(new LRUHashMap()); - private static final Map recipientUriCache = Collections.synchronizedMap(new HashMap()); - private static final RecipientProvider provider = new RecipientProvider(); - public static RecipientProvider getRecipientProvider() { - return provider; - } - - public static Recipient getRecipientForUri(Context context, Uri uri) { - Recipient recipient = recipientUriCache.get(uri); - - if (recipient == null) - recipient = getRecipientFromProvider(context, uri); - - return recipient; - } - - public static Recipients getRecipientsForIds(Context context, String recipientIds) { + public static Recipients getRecipientsForIds(Context context, String recipientIds, boolean asynchronous) { if (recipientIds == null || recipientIds.trim().length() == 0) return new Recipients(new LinkedList()); @@ -63,14 +38,7 @@ public class RecipientFactory { while (tokenizer.hasMoreTokens()) { String recipientId = tokenizer.nextToken(); - - Recipient recipient = recipientIdCache.get(recipientId); - - if (recipient == null) - recipient = getRecipientFromProviderId(context, recipientId); - - if (recipient == null) - recipient = getNullIdRecipient(context, recipientId); + Recipient recipient = getRecipientFromProviderId(context, recipientId, asynchronous); results.add(recipient); } @@ -78,24 +46,18 @@ public class RecipientFactory { return new Recipients(results); } - private static Recipient getRecipientForNumber(Context context, String number) { - Recipient recipient = recipientCache.get(number); - - if (recipient == null) - recipient = getRecipientFromProvider(context, number); - - if (recipient == null) - recipient = getNullRecipient(context, number); - - return recipient; + private static Recipient getRecipientForNumber(Context context, String number, boolean asynchronous) { + return provider.getRecipient(context, number, asynchronous); } - public static Recipients getRecipientsFromString(Context context, String rawText) throws RecipientFormattingException { + public static Recipients getRecipientsFromString(Context context, String rawText, boolean asynchronous) + throws RecipientFormattingException + { List results = new LinkedList(); StringTokenizer tokenizer = new StringTokenizer(rawText, ","); while (tokenizer.hasMoreTokens()) { - Recipient recipient = parseRecipient(context, tokenizer.nextToken()); + Recipient recipient = parseRecipient(context, tokenizer.nextToken(), asynchronous); if( recipient != null ) results.add(recipient); } @@ -103,45 +65,9 @@ public class RecipientFactory { return new Recipients(results); } - private static Recipient getNullIdRecipient(Context context, String recipientId) { - String address = DatabaseFactory.getAddressDatabase(context).getAddressFromId(recipientId); - Recipient recipient = getNullRecipient(context, address); - recipientIdCache.put(recipientId, recipient); - return recipient; - } - - private static Recipient getNullRecipient(Context context, String number) { - Recipient nullRecipient = new Recipient(null, number, BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_contact_picture)); - recipientCache.put(number, nullRecipient); - return nullRecipient; - } - - private static Recipient getRecipientFromProviderId(Context context, String recipientId) { - Log.w("RecipientFactory", "Hitting recipient provider [ID]."); - - String address = DatabaseFactory.getAddressDatabase(context).getAddressFromId(recipientId); - Recipient recipient = getRecipientFromProvider(context, address); - - recipientIdCache.put(recipientId, recipient); - return recipient; - } - - private static Recipient getRecipientFromProvider(Context context, Uri uri) { - Recipient recipient = provider.getRecipient(context, uri); - - if (recipient != null) - recipientUriCache.put(uri, recipient); - - return recipient; - } - - private static Recipient getRecipientFromProvider(Context context, String number) { - Recipient recipient = provider.getRecipient(context, number); - - if (recipient != null) - recipientCache.put(number, recipient); - - return recipient; + private static Recipient getRecipientFromProviderId(Context context, String recipientId, boolean asynchronous) { + String number = DatabaseFactory.getAddressDatabase(context).getAddressFromId(recipientId); + return getRecipientForNumber(context, number, asynchronous); } private static boolean hasBracketedNumber(String recipient) { @@ -151,7 +77,9 @@ public class RecipientFactory { (recipient.indexOf('>', openBracketIndex) != -1); } - private static String parseBracketedNumber(String recipient) throws RecipientFormattingException { + private static String parseBracketedNumber(String recipient) + throws RecipientFormattingException + { int begin = recipient.indexOf('<'); int end = recipient.indexOf('>', begin); String value = recipient.substring(begin + 1, end); @@ -162,32 +90,26 @@ public class RecipientFactory { throw new RecipientFormattingException("Bracketed value: " + value + " is not valid."); } - private static Recipient parseRecipient(Context context, String recipient) throws RecipientFormattingException { + private static Recipient parseRecipient(Context context, String recipient, boolean asynchronous) + throws RecipientFormattingException + { recipient = recipient.trim(); if( recipient.length() == 0 ) return null; if (hasBracketedNumber(recipient)) - return getRecipientForNumber(context, parseBracketedNumber(recipient)); + return getRecipientForNumber(context, parseBracketedNumber(recipient), asynchronous); if (NumberUtil.isValidSmsOrEmail(recipient)) - return getRecipientForNumber(context, recipient); + return getRecipientForNumber(context, recipient, asynchronous); throw new RecipientFormattingException("Recipient: " + recipient + " is badly formatted."); } public static void clearCache() { - recipientCache.clear(); - recipientIdCache.clear(); - recipientUriCache.clear(); + ContactPhotoFactory.clearCache(); + provider.clearCache(); } - private static class LRUHashMap extends LinkedHashMap { - private static final int MAX_SIZE = 1000; - @Override - protected boolean removeEldestEntry (Map.Entry eldest) { - return size() > MAX_SIZE; - } - } } diff --git a/src/org/thoughtcrime/securesms/recipients/RecipientProvider.java b/src/org/thoughtcrime/securesms/recipients/RecipientProvider.java index 0d3d735b31..92cab83cde 100644 --- a/src/org/thoughtcrime/securesms/recipients/RecipientProvider.java +++ b/src/org/thoughtcrime/securesms/recipients/RecipientProvider.java @@ -21,17 +21,22 @@ import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; +import android.os.AsyncTask; import android.provider.ContactsContract; import android.provider.ContactsContract.Contacts; import android.provider.ContactsContract.PhoneLookup; +import android.util.Log; -import org.thoughtcrime.securesms.R; +import org.thoughtcrime.securesms.util.LRUCache; import java.io.InputStream; +import java.util.Collections; +import java.util.Map; public class RecipientProvider { - private static Bitmap defaultContactPhoto; + private static final Map recipientCache = Collections.synchronizedMap(new LRUCache(1000)); +// private static final ExecutorService asyncRecipientResolver = Executors.newSingleThreadExecutor(); private static final String[] CALLER_ID_PROJECTION = new String[] { PhoneLookup.DISPLAY_NAME, @@ -39,41 +44,107 @@ public class RecipientProvider { PhoneLookup._ID, }; - private static final String[] CONTENT_URI_PROJECTION = new String[] { - ContactsContract.Contacts._ID, - ContactsContract.Contacts.DISPLAY_NAME, - ContactsContract.Contacts.LOOKUP_KEY - }; + public Recipient getRecipient(Context context, String number, boolean asynchronous) { + Recipient cachedRecipient = recipientCache.get(number); - public Recipient getRecipient(Context context, Uri uri) { - Cursor cursor = context.getContentResolver().query(uri, CONTENT_URI_PROJECTION, null, null, null); + if (cachedRecipient != null) return cachedRecipient; + else if (asynchronous) return getAsynchronousRecipient(context, number); + else return getSynchronousRecipient(context, number); + } - try { - if (cursor.moveToFirst()) { - long rowId = cursor.getLong(0); - Uri contactUri = Contacts.getLookupUri(rowId, cursor.getString(2)); - Bitmap contactPhoto = getContactPhoto(context, Uri.withAppendedPath(Contacts.CONTENT_URI, - rowId+"")); - String displayName = cursor.getString(1); - cursor.close(); + private Recipient getSynchronousRecipient(Context context, String number) { + Log.w("RecipientProvider", "Cache miss [SYNC]!"); + RecipientDetails details = getRecipientDetails(context, number); + Recipient recipient; - cursor = context.getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, new String[] {ContactsContract.CommonDataKinds.Phone.NUMBER}, ContactsContract.CommonDataKinds.Phone.CONTACT_ID +" = ?", new String[] {rowId+""}, null); + if (details != null) { + recipient = new Recipient(details.name, number, details.contactUri, details.avatar); + } else { + recipient = new Recipient(null, number, null, ContactPhotoFactory.getDefaultContactPhoto(context)); + } - if (cursor.moveToFirst()) - return new Recipient(displayName, cursor.getString(0), contactUri, contactPhoto); - else - return new Recipient(displayName, null, contactUri, contactPhoto); + recipientCache.put(number, recipient); + return recipient; + } + + private Recipient getAsynchronousRecipient(final Context context, final String number) { + Log.w("RecipientProvider", "Cache miss [ASYNC]!"); + + Recipient recipient = new Recipient(null, number, null, ContactPhotoFactory.getDefaultContactPhoto(context)); + recipientCache.put(number, recipient); + + new AsyncTask() { + private Recipient recipient; + + @Override + protected RecipientDetails doInBackground(Recipient... recipient) { + this.recipient = recipient[0]; + return getRecipientDetails(context, number); } - } finally { - cursor.close(); - } - return null; + @Override + protected void onPostExecute(RecipientDetails result) { + recipient.updateAsynchronousContent(result); + } + }.execute(recipient); + + return recipient; + +// ListenableFutureTask future = +// new ListenableFutureTask(new Callable() { +// @Override +// public RecipientDetails call() throws Exception { +// return getRecipientDetails(context, number); +//// RecipientDetails recipientDetails = getRecipientDetails(); +//// +//// if (recipientDeta) +//// +//// Recipient cachedRecipient = recipientCache.get(number); +//// +//// if (cachedRecipient != null) { +//// return new RecipientDetails(cachedRecipient.getName(), cachedRecipient.getContactPhoto()); +//// } +//// +//// Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number)); +//// Cursor cursor = context.getContentResolver().query(uri, CALLER_ID_PROJECTION, +//// null, null, null); +//// +//// try { +//// if (cursor != null && cursor.moveToFirst()) { +//// Uri contactUri = Contacts.getLookupUri(cursor.getLong(2), cursor.getString(1)); +//// Bitmap contactPhoto = getContactPhoto(context, Uri.withAppendedPath(Contacts.CONTENT_URI, +//// cursor.getLong(2)+"")); +//// +//// recipientCache.put(number, new Recipient(cursor.getString(0), number, contactPhoto)); +//// return new RecipientDetails(cursor.getString(0), contactPhoto); +//// } else { +//// recipientCache.put(number, new Recipient(null, number, ContactPhotoFactory.getDefaultContactPhoto(context))); +//// } +//// } finally { +//// if (cursor != null) +//// cursor.close(); +//// } +//// +//// return null; +// } +// }, null); +// +// asyncRecipientResolver.submit(future); +// Recipient recipient = new Recipient(number, ContactPhotoFactory.getDefaultContactPhoto(context), future); +// recipientCache.put(number, recipient); +// +// return recipient; +//// return new Recipient(null, number, ContactPhotoFactory.getDefaultContactPhoto(context)); + } + + public void clearCache() { + recipientCache.clear(); } - public Recipient getRecipient(Context context, String number) { + private RecipientDetails getRecipientDetails(Context context, String number) { Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number)); - Cursor cursor = context.getContentResolver().query(uri, CALLER_ID_PROJECTION, null, null, null); + Cursor cursor = context.getContentResolver().query(uri, CALLER_ID_PROJECTION, + null, null, null); try { if (cursor != null && cursor.moveToFirst()) { @@ -81,8 +152,7 @@ public class RecipientProvider { Bitmap contactPhoto = getContactPhoto(context, Uri.withAppendedPath(Contacts.CONTENT_URI, cursor.getLong(2)+"")); - Recipient recipient = new Recipient(cursor.getString(0), number, contactUri, contactPhoto); - return recipient; + return new RecipientDetails(cursor.getString(0), contactUri, contactPhoto); } } finally { if (cursor != null) @@ -92,22 +162,25 @@ public class RecipientProvider { return null; } - public Bitmap getDefaultContactPhoto(Context context) { - synchronized (this) { - if (defaultContactPhoto == null) - defaultContactPhoto = BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_contact_picture); - } - - return defaultContactPhoto; - } - private Bitmap getContactPhoto(Context context, Uri uri) { InputStream inputStream = ContactsContract.Contacts.openContactPhotoInputStream(context.getContentResolver(), uri); if (inputStream == null) - return getDefaultContactPhoto(context); + return ContactPhotoFactory.getDefaultContactPhoto(context); else return BitmapFactory.decodeStream(inputStream); } + public static class RecipientDetails { + public final String name; + public final Bitmap avatar; + public final Uri contactUri; + + public RecipientDetails(String name, Uri contactUri, Bitmap avatar) { + this.name = name; + this.avatar = avatar; + this.contactUri = contactUri; + } + } + } \ No newline at end of file diff --git a/src/org/thoughtcrime/securesms/recipients/Recipients.java b/src/org/thoughtcrime/securesms/recipients/Recipients.java index 30a1d166b8..52b24e7d4d 100644 --- a/src/org/thoughtcrime/securesms/recipients/Recipients.java +++ b/src/org/thoughtcrime/securesms/recipients/Recipients.java @@ -21,6 +21,7 @@ import android.os.Parcel; import android.os.Parcelable; import org.thoughtcrime.securesms.crypto.KeyUtil; +import org.thoughtcrime.securesms.recipients.Recipient.RecipientModifiedListener; import org.thoughtcrime.securesms.util.NumberUtil; import java.util.ArrayList; @@ -68,6 +69,12 @@ public class Recipients implements Parcelable { return this; } + public void setListener(RecipientModifiedListener listener) { + for (Recipient recipient : recipients) { + recipient.setListener(listener); + } + } + public boolean isEmailRecipient() { for (Recipient recipient : recipients) { if (NumberUtil.isValidEmail(recipient.getNumber())) diff --git a/src/org/thoughtcrime/securesms/service/MessageNotifier.java b/src/org/thoughtcrime/securesms/service/MessageNotifier.java index ca072b4061..27789f4c3c 100644 --- a/src/org/thoughtcrime/securesms/service/MessageNotifier.java +++ b/src/org/thoughtcrime/securesms/service/MessageNotifier.java @@ -94,13 +94,13 @@ public class MessageNotifier { private static Recipients getSmsRecipient(Context context, Cursor c) throws RecipientFormattingException { String address = c.getString(c.getColumnIndexOrThrow(SmsDatabase.ADDRESS)); - return RecipientFactory.getRecipientsFromString(context, address); + return RecipientFactory.getRecipientsFromString(context, address, false); } private static Recipients getMmsRecipient(Context context, Cursor c) throws RecipientFormattingException { long messageId = c.getLong(c.getColumnIndexOrThrow(MmsDatabase.ID)); String address = DatabaseFactory.getMmsDatabase(context).getMessageRecipient(messageId); - return RecipientFactory.getRecipientsFromString(context, address); + return RecipientFactory.getRecipientsFromString(context, address, false); } private static Recipients getMostRecentRecipients(Context context, Cursor c) { diff --git a/src/org/thoughtcrime/securesms/service/MmsSender.java b/src/org/thoughtcrime/securesms/service/MmsSender.java index 1046984c7e..0701bd54c7 100644 --- a/src/org/thoughtcrime/securesms/service/MmsSender.java +++ b/src/org/thoughtcrime/securesms/service/MmsSender.java @@ -108,7 +108,7 @@ public class MmsSender extends MmscProcessor { private byte[] getEncryptedPdu(MasterSecret masterSecret, String recipient, byte[] pduBytes) { synchronized (SessionCipher.CIPHER_LOCK) { - SessionCipher cipher = new SessionCipher(context, masterSecret, new Recipient(null, recipient, null), new TextTransport()); + SessionCipher cipher = new SessionCipher(context, masterSecret, new Recipient(null, recipient, null, null), new TextTransport()); return cipher.encryptMessage(pduBytes); } } diff --git a/src/org/thoughtcrime/securesms/service/SmsReceiver.java b/src/org/thoughtcrime/securesms/service/SmsReceiver.java index 6c830fa539..fa42881fb6 100644 --- a/src/org/thoughtcrime/securesms/service/SmsReceiver.java +++ b/src/org/thoughtcrime/securesms/service/SmsReceiver.java @@ -102,7 +102,7 @@ public class SmsReceiver { private void storeKeyExchangeMessage(MasterSecret masterSecret, SmsMessage message, String messageBody) { if (PreferenceManager.getDefaultSharedPreferences(context).getBoolean(ApplicationPreferencesActivity.AUTO_KEY_EXCHANGE_PREF, true)) { try { - Recipient recipient = new Recipient(null, message.getDisplayOriginatingAddress(), null); + Recipient recipient = new Recipient(null, message.getDisplayOriginatingAddress(), null, null); KeyExchangeMessage keyExchangeMessage = new KeyExchangeMessage(messageBody); KeyExchangeProcessor processor = new KeyExchangeProcessor(context, masterSecret, recipient); diff --git a/src/org/thoughtcrime/securesms/service/SmsSender.java b/src/org/thoughtcrime/securesms/service/SmsSender.java index 3ad3e91ed2..e5a7390bcd 100644 --- a/src/org/thoughtcrime/securesms/service/SmsSender.java +++ b/src/org/thoughtcrime/securesms/service/SmsSender.java @@ -175,7 +175,7 @@ public class SmsSender { private String getAsymmetricEncrypt(MasterSecret masterSecret, String body, String address) { synchronized (SessionCipher.CIPHER_LOCK) { - SessionCipher cipher = new SessionCipher(context, masterSecret, new Recipient(null, address, null), new SmsTransportDetails()); + SessionCipher cipher = new SessionCipher(context, masterSecret, new Recipient(null, address, null, null), new SmsTransportDetails()); return new String(cipher.encryptMessage(body.getBytes())); } } diff --git a/src/org/thoughtcrime/securesms/util/FutureTaskListener.java b/src/org/thoughtcrime/securesms/util/FutureTaskListener.java new file mode 100644 index 0000000000..7477fdbc41 --- /dev/null +++ b/src/org/thoughtcrime/securesms/util/FutureTaskListener.java @@ -0,0 +1,6 @@ +package org.thoughtcrime.securesms.util; + +public interface FutureTaskListener { + public void onSuccess(V result); + public void onFailure(Throwable error); +} diff --git a/src/org/thoughtcrime/securesms/util/LRUCache.java b/src/org/thoughtcrime/securesms/util/LRUCache.java new file mode 100644 index 0000000000..b89308918d --- /dev/null +++ b/src/org/thoughtcrime/securesms/util/LRUCache.java @@ -0,0 +1,18 @@ +package org.thoughtcrime.securesms.util; + +import java.util.LinkedHashMap; +import java.util.Map; + +public class LRUCache extends LinkedHashMap { + + private final int maxSize; + + public LRUCache(int maxSize) { + this.maxSize = maxSize; + } + + @Override + protected boolean removeEldestEntry (Map.Entry eldest) { + return size() > maxSize; + } +} diff --git a/src/org/thoughtcrime/securesms/util/ListenableFutureTask.java b/src/org/thoughtcrime/securesms/util/ListenableFutureTask.java new file mode 100644 index 0000000000..362421b126 --- /dev/null +++ b/src/org/thoughtcrime/securesms/util/ListenableFutureTask.java @@ -0,0 +1,39 @@ +package org.thoughtcrime.securesms.util; + +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.FutureTask; + +public class ListenableFutureTask extends FutureTask { + + private FutureTaskListener listener; + + public ListenableFutureTask(Callable callable, FutureTaskListener listener) { + super(callable); + this.listener = listener; + } + + public synchronized void setListener(FutureTaskListener listener) { + this.listener = listener; + if (this.isDone()) { + callback(); + } + } + + @Override + protected synchronized void done() { + callback(); + } + + private void callback() { + if (this.listener != null) { + try { + this.listener.onSuccess(get()); + } catch (ExecutionException ee) { + this.listener.onFailure(ee); + } catch (InterruptedException e) { + throw new AssertionError(e); + } + } + } +}