From 636b11abea7a520e1c69c59f69055c5ab112e70f Mon Sep 17 00:00:00 2001 From: Moxie Marlinspike Date: Tue, 19 May 2015 14:00:54 -0700 Subject: [PATCH] Refactor ContactSelectionListAdapter and associated views. Fixes #3181 Closes #3197 // FREEBIE --- .../push_contact_selection_list_item.xml | 49 +++-- .../securesms/GroupCreateActivity.java | 19 +- .../securesms/NewConversationActivity.java | 42 +--- .../PushContactSelectionActivity.java | 17 +- .../PushContactSelectionListFragment.java | 61 ++---- .../contacts/ContactSelectionListAdapter.java | 206 ++++-------------- .../contacts/ContactSelectionListItem.java | 126 +++++++++++ .../securesms/util/BitmapWorkerRunnable.java | 97 --------- 8 files changed, 231 insertions(+), 386 deletions(-) create mode 100644 src/org/thoughtcrime/securesms/contacts/ContactSelectionListItem.java delete mode 100644 src/org/thoughtcrime/securesms/util/BitmapWorkerRunnable.java diff --git a/res/layout/push_contact_selection_list_item.xml b/res/layout/push_contact_selection_list_item.xml index 02c288c335..5bf5c09d3a 100644 --- a/res/layout/push_contact_selection_list_item.xml +++ b/res/layout/push_contact_selection_list_item.xml @@ -1,6 +1,7 @@ - @@ -16,23 +17,41 @@ android:scaleType="centerCrop" android:contentDescription="@string/SingleContactSelectionActivity_contact_photo" /> - + + + + + + + - + diff --git a/src/org/thoughtcrime/securesms/GroupCreateActivity.java b/src/org/thoughtcrime/securesms/GroupCreateActivity.java index c12219d4de..044383b4c0 100644 --- a/src/org/thoughtcrime/securesms/GroupCreateActivity.java +++ b/src/org/thoughtcrime/securesms/GroupCreateActivity.java @@ -380,17 +380,14 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity { switch (reqCode) { case PICK_CONTACT: - List selected = data.getParcelableArrayListExtra("contacts"); - for (ContactData contact : selected) { - for (ContactAccessor.NumberData numberData : contact.numbers) { - Recipient recipient = RecipientFactory.getRecipientsFromString(this, numberData.number, false) - .getPrimaryRecipient(); - - if (!selectedContacts.contains(recipient) && - (existingContacts == null || !existingContacts.contains(recipient)) && - recipient != null) { - addSelectedContact(recipient); - } + List selected = data.getStringArrayListExtra("contacts"); + for (String contact : selected) { + Recipient recipient = RecipientFactory.getRecipientsFromString(this, contact, false).getPrimaryRecipient(); + + if (!selectedContacts.contains(recipient) && + (existingContacts == null || !existingContacts.contains(recipient)) && + recipient != null) { + addSelectedContact(recipient); } } syncAdapterWithSelectedContacts(); diff --git a/src/org/thoughtcrime/securesms/NewConversationActivity.java b/src/org/thoughtcrime/securesms/NewConversationActivity.java index 9b7e707f17..fa65e1a2ca 100644 --- a/src/org/thoughtcrime/securesms/NewConversationActivity.java +++ b/src/org/thoughtcrime/securesms/NewConversationActivity.java @@ -24,25 +24,15 @@ import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; -import org.thoughtcrime.securesms.contacts.ContactAccessor; +import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.ThreadDatabase; -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.util.DirectoryHelper; import org.thoughtcrime.securesms.util.DynamicLanguage; import org.thoughtcrime.securesms.util.DynamicTheme; -import org.thoughtcrime.securesms.util.NumberUtil; import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.crypto.MasterSecret; - -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; - -import static org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData; /** * Activity container for selecting a list of contacts. @@ -67,7 +57,6 @@ public class NewConversationActivity extends PassphraseRequiredActionBarActivity @Override protected void onCreate(Bundle icicle, @NonNull MasterSecret masterSecret) { getSupportActionBar().setDisplayHomeAsUpEnabled(true); - setContentView(R.layout.new_conversation_activity); initializeResources(); } @@ -94,7 +83,6 @@ public class NewConversationActivity extends PassphraseRequiredActionBarActivity super.onOptionsItemSelected(item); switch (item.getItemId()) { case R.id.menu_refresh_directory: handleDirectoryRefresh(); return true; - case R.id.menu_selection_finished: handleSelectionFinished(); return true; case android.R.id.home: finish(); return true; } return false; @@ -104,24 +92,14 @@ public class NewConversationActivity extends PassphraseRequiredActionBarActivity contactsFragment = (PushContactSelectionListFragment) getSupportFragmentManager().findFragmentById(R.id.contact_selection_list_fragment); contactsFragment.setOnContactSelectedListener(new PushContactSelectionListFragment.OnContactSelectedListener() { @Override - public void onContactSelected(ContactData contactData) { + public void onContactSelected(String number) { Log.i(TAG, "Choosing contact from list."); - Recipients recipients = contactDataToRecipients(contactData); + Recipients recipients = RecipientFactory.getRecipientsFromString(NewConversationActivity.this, number, true); openNewConversation(recipients); } }); } - private void handleSelectionFinished() { - final Intent resultIntent = getIntent(); - final List selectedContacts = contactsFragment.getSelectedContacts(); - if (selectedContacts != null) { - resultIntent.putParcelableArrayListExtra("contacts", new ArrayList<>(contactsFragment.getSelectedContacts())); - } - setResult(RESULT_OK, resultIntent); - finish(); - } - private void handleDirectoryRefresh() { DirectoryHelper.refreshDirectoryWithProgressDialog(this, new DirectoryHelper.DirectoryUpdateFinishedListener() { @Override @@ -131,20 +109,6 @@ public class NewConversationActivity extends PassphraseRequiredActionBarActivity }); } - private Recipients contactDataToRecipients(ContactData contactData) { - if (contactData == null || contactData.numbers == null) return null; - Recipients recipients = new Recipients(new LinkedList()); - for (ContactAccessor.NumberData numberData : contactData.numbers) { - if (NumberUtil.isValidSmsOrEmailOrGroup(numberData.number)) { - Recipients recipientsForNumber = RecipientFactory.getRecipientsFromString(NewConversationActivity.this, - numberData.number, - false); - recipients.getRecipientsList().addAll(recipientsForNumber.getRecipientsList()); - } - } - return recipients; - } - private void openNewConversation(Recipients recipients) { if (recipients != null) { Intent intent = new Intent(this, ConversationActivity.class); diff --git a/src/org/thoughtcrime/securesms/PushContactSelectionActivity.java b/src/org/thoughtcrime/securesms/PushContactSelectionActivity.java index 8a580a16c9..a06b46a2b5 100644 --- a/src/org/thoughtcrime/securesms/PushContactSelectionActivity.java +++ b/src/org/thoughtcrime/securesms/PushContactSelectionActivity.java @@ -19,7 +19,6 @@ package org.thoughtcrime.securesms; import android.content.Intent; import android.os.Bundle; import android.support.annotation.NonNull; -import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; @@ -30,12 +29,9 @@ import org.thoughtcrime.securesms.util.DynamicLanguage; import org.thoughtcrime.securesms.util.DynamicTheme; import org.thoughtcrime.securesms.util.TextSecurePreferences; - import java.util.ArrayList; import java.util.List; -import static org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData; - /** * Activity container for selecting a list of contacts. * @@ -98,21 +94,16 @@ public class PushContactSelectionActivity extends PassphraseRequiredActionBarAct private void initializeResources() { contactsFragment = (PushContactSelectionListFragment) getSupportFragmentManager().findFragmentById(R.id.contact_selection_list_fragment); contactsFragment.setMultiSelect(true); - contactsFragment.setOnContactSelectedListener(new PushContactSelectionListFragment.OnContactSelectedListener() { - @Override - public void onContactSelected(ContactData contactData) { - Log.i(TAG, "Choosing contact from list."); - } - }); } private void handleSelectionFinished() { + Intent resultIntent = getIntent(); + List selectedContacts = contactsFragment.getSelectedContacts(); - final Intent resultIntent = getIntent(); - final List selectedContacts = contactsFragment.getSelectedContacts(); if (selectedContacts != null) { - resultIntent.putParcelableArrayListExtra("contacts", new ArrayList(contactsFragment.getSelectedContacts())); + resultIntent.putStringArrayListExtra("contacts", new ArrayList<>(selectedContacts)); } + setResult(RESULT_OK, resultIntent); finish(); } diff --git a/src/org/thoughtcrime/securesms/PushContactSelectionListFragment.java b/src/org/thoughtcrime/securesms/PushContactSelectionListFragment.java index b5b76c18ef..16df52c1e8 100644 --- a/src/org/thoughtcrime/securesms/PushContactSelectionListFragment.java +++ b/src/org/thoughtcrime/securesms/PushContactSelectionListFragment.java @@ -19,14 +19,12 @@ package org.thoughtcrime.securesms; import android.database.Cursor; import android.os.Bundle; -import android.provider.ContactsContract; import android.support.v4.app.Fragment; import android.support.v4.app.LoaderManager; import android.support.v4.content.Loader; import android.support.v4.widget.CursorAdapter; import android.text.Editable; import android.text.TextWatcher; -import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -35,11 +33,8 @@ import android.widget.EditText; import android.widget.TextView; import org.thoughtcrime.securesms.contacts.ContactAccessor; -import org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData; import org.thoughtcrime.securesms.contacts.ContactSelectionListAdapter; -import org.thoughtcrime.securesms.contacts.ContactSelectionListAdapter.ViewHolder; -import org.thoughtcrime.securesms.contacts.ContactSelectionListAdapter.DataHolder; -import org.thoughtcrime.securesms.contacts.ContactsDatabase; +import org.thoughtcrime.securesms.contacts.ContactSelectionListItem; import java.util.LinkedList; import java.util.List; @@ -61,13 +56,12 @@ public class PushContactSelectionListFragment extends Fragment private TextView emptyText; - private Map selectedContacts; + private Map selectedContacts; private OnContactSelectedListener onContactSelectedListener; - private boolean multi = false; private StickyListHeadersListView listView; - private EditText filterEditText; private String cursorFilter; + private boolean multi = false; @Override public void onActivityCreated(Bundle icicle) { @@ -91,10 +85,10 @@ public class PushContactSelectionListFragment extends Fragment return inflater.inflate(R.layout.push_contact_selection_list_activity, container, false); } - public List getSelectedContacts() { + public List getSelectedContacts() { if (selectedContacts == null) return null; - List selected = new LinkedList(); + List selected = new LinkedList<>(); selected.addAll(selectedContacts.values()); return selected; @@ -104,23 +98,6 @@ public class PushContactSelectionListFragment extends Fragment this.multi = multi; } - private void addContact(DataHolder data) { - final ContactData contactData = new ContactData(data.id, data.name); - final CharSequence label = ContactsContract.CommonDataKinds.Phone.getTypeLabel(getResources(), - data.numberType, ""); - contactData.numbers.add(new ContactAccessor.NumberData(label.toString(), data.number)); - if (multi) { - selectedContacts.put(contactData.id, contactData); - } - if (onContactSelectedListener != null) { - onContactSelectedListener.onContactSelected(contactData); - } - } - - private void removeContact(DataHolder contactData) { - selectedContacts.remove(contactData.id); - } - private void initializeCursor() { ContactSelectionListAdapter adapter = new ContactSelectionListAdapter(getActivity(), null, multi); selectedContacts = adapter.getSelectedContacts(); @@ -135,7 +112,8 @@ public class PushContactSelectionListFragment extends Fragment listView.setFastScrollEnabled(true); listView.setDrawingListUnderStickyHeader(false); listView.setOnItemClickListener(new ListClickListener()); - filterEditText = (EditText) getView().findViewById(R.id.filter); + + EditText filterEditText = (EditText) getView().findViewById(R.id.filter); filterEditText.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) { @@ -185,20 +163,15 @@ public class PushContactSelectionListFragment extends Fragment private class ListClickListener implements AdapterView.OnItemClickListener { @Override public void onItemClick(AdapterView l, View v, int position, long id) { - final DataHolder contactData = (DataHolder) v.getTag(R.id.contact_info_tag); - final ViewHolder holder = (ViewHolder) v.getTag(R.id.holder_tag); - - if (holder == null) { - Log.w(TAG, "ViewHolder was null, can't proceed with click logic."); - return; - } - - if (multi) holder.checkBox.toggle(); - - if (!multi || holder.checkBox.isChecked()) { - addContact(contactData); - } else if (multi) { - removeContact(contactData); + ContactSelectionListItem contact = (ContactSelectionListItem)v; + + if (!multi || !selectedContacts.containsKey(contact.getContactId())) { + selectedContacts.put(contact.getContactId(), contact.getNumber()); + contact.setChecked(true); + if (onContactSelectedListener != null) onContactSelectedListener.onContactSelected(contact.getNumber()); + } else { + selectedContacts.remove(contact.getContactId()); + contact.setChecked(false); } } } @@ -208,6 +181,6 @@ public class PushContactSelectionListFragment extends Fragment } public interface OnContactSelectedListener { - public void onContactSelected(ContactData contactData); + public void onContactSelected(String number); } } diff --git a/src/org/thoughtcrime/securesms/contacts/ContactSelectionListAdapter.java b/src/org/thoughtcrime/securesms/contacts/ContactSelectionListAdapter.java index fe14322be4..ad1b8d3151 100644 --- a/src/org/thoughtcrime/securesms/contacts/ContactSelectionListAdapter.java +++ b/src/org/thoughtcrime/securesms/contacts/ContactSelectionListAdapter.java @@ -21,27 +21,15 @@ import android.content.res.TypedArray; import android.database.Cursor; import android.provider.ContactsContract; import android.support.v4.widget.CursorAdapter; -import android.text.Spannable; -import android.text.SpannableString; -import android.text.style.ForegroundColorSpan; -import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.CheckBox; -import android.widget.ImageView; import android.widget.TextView; import org.thoughtcrime.securesms.R; -import org.thoughtcrime.securesms.util.BitmapWorkerRunnable; -import org.thoughtcrime.securesms.util.BitmapWorkerRunnable.AsyncDrawable; -import org.thoughtcrime.securesms.util.TaggedFutureTask; -import org.thoughtcrime.securesms.util.Util; import java.util.HashMap; import java.util.Map; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.FutureTask; import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter; @@ -53,53 +41,22 @@ import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter; public class ContactSelectionListAdapter extends CursorAdapter implements StickyListHeadersAdapter { - private final static String TAG = "ContactListAdapter"; - - private final static ExecutorService photoResolver = Util.newSingleThreadedLifoExecutor(); + private final static String TAG = ContactSelectionListAdapter.class.getSimpleName(); private final static int STYLE_ATTRIBUTES[] = new int[]{R.attr.contact_selection_push_user, - R.attr.contact_selection_lay_user, - R.attr.contact_selection_label_text}; - - private int TYPE_COLUMN = -1; - private int NAME_COLUMN = -1; - private int NUMBER_COLUMN = -1; - private int NUMBER_TYPE_COLUMN = -1; - private int LABEL_COLUMN = -1; - private int ID_COLUMN = -1; + R.attr.contact_selection_lay_user}; - private final Context context; private final boolean multiSelect; private final LayoutInflater li; private final TypedArray drawables; - private final int scaledPhotoSize; - private final HashMap selectedContacts = new HashMap<>(); + private final HashMap selectedContacts = new HashMap<>(); public ContactSelectionListAdapter(Context context, Cursor cursor, boolean multiSelect) { super(context, cursor, 0); - this.context = context; - this.li = LayoutInflater.from(context); - this.drawables = context.obtainStyledAttributes(STYLE_ATTRIBUTES); - this.multiSelect = multiSelect; - this.scaledPhotoSize = context.getResources().getDimensionPixelSize(R.dimen.contact_selection_photo_size); - } - - public static class ViewHolder { - public CheckBox checkBox; - public TextView name; - public TextView number; - public ImageView contactPhoto; - public int position; - } - - public static class DataHolder { - public int type; - public String name; - public String number; - public int numberType; - public String label; - public long id; + this.li = LayoutInflater.from(context); + this.drawables = context.obtainStyledAttributes(STYLE_ATTRIBUTES); + this.multiSelect = multiSelect; } public static class HeaderViewHolder { @@ -108,148 +65,63 @@ public class ContactSelectionListAdapter extends CursorAdapter @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { - final View v = li.inflate(R.layout.push_contact_selection_list_item, parent, false); - final ViewHolder holder = new ViewHolder(); - - if (v != null) { - holder.name = (TextView) v.findViewById(R.id.name); - holder.number = (TextView) v.findViewById(R.id.number); - holder.checkBox = (CheckBox) v.findViewById(R.id.check_box); - holder.contactPhoto = (ImageView) v.findViewById(R.id.contact_photo_image); - - if (!multiSelect) holder.checkBox.setVisibility(View.GONE); - - v.setTag(R.id.holder_tag, holder); - v.setTag(R.id.contact_info_tag, new DataHolder()); - } - return v; + return li.inflate(R.layout.push_contact_selection_list_item, parent, false); } @Override public void bindView(View view, Context context, Cursor cursor) { - final DataHolder contactData = (DataHolder) view.getTag(R.id.contact_info_tag); - final ViewHolder holder = (ViewHolder) view.getTag(R.id.holder_tag); - if (holder == null) { - Log.w(TAG, "ViewHolder was null. This should not happen."); - return; - } - if (contactData == null) { - Log.w(TAG, "DataHolder was null. This should not happen."); - return; - } - if (ID_COLUMN < 0) { - populateColumnIndices(cursor); - } - - contactData.type = cursor.getInt(TYPE_COLUMN); - contactData.name = cursor.getString(NAME_COLUMN); - contactData.number = cursor.getString(NUMBER_COLUMN); - contactData.numberType = cursor.getInt(NUMBER_TYPE_COLUMN); - contactData.label = cursor.getString(LABEL_COLUMN); - contactData.id = cursor.getLong(ID_COLUMN); - - if (contactData.type != ContactsDatabase.PUSH_TYPE) { - holder.name.setTextColor(drawables.getColor(1, 0xff000000)); - holder.number.setTextColor(drawables.getColor(1, 0xff000000)); - } else { - holder.name.setTextColor(drawables.getColor(0, 0xa0000000)); - holder.number.setTextColor(drawables.getColor(0, 0xa0000000)); - } - - if (selectedContacts.containsKey(contactData.id)) { - holder.checkBox.setChecked(true); - } else { - holder.checkBox.setChecked(false); - } - - holder.name.setText(contactData.name); - - if (contactData.number == null || contactData.number.isEmpty()) { - holder.name.setEnabled(false); - holder.number.setText(""); - } else if (contactData.type == ContactsDatabase.PUSH_TYPE) { - holder.number.setText(contactData.number); - } else { - final CharSequence label = ContactsContract.CommonDataKinds.Phone.getTypeLabel(context.getResources(), - contactData.numberType, contactData.label); - final CharSequence numberWithLabel = contactData.number + " " + label; - final Spannable numberLabelSpan = new SpannableString(numberWithLabel); - numberLabelSpan.setSpan(new ForegroundColorSpan(drawables.getColor(2, 0xff444444)), contactData.number.length(), numberWithLabel.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE); - holder.number.setText(numberLabelSpan); - } - holder.contactPhoto.setImageDrawable(ContactPhotoFactory.getLoadingPhoto(context)); - if (contactData.id > -1) loadBitmap(contactData.number, holder.contactPhoto); + long id = cursor.getLong(cursor.getColumnIndexOrThrow(ContactsDatabase.ID_COLUMN)); + int type = cursor.getInt(cursor.getColumnIndexOrThrow(ContactsDatabase.TYPE_COLUMN)); + String name = cursor.getString(cursor.getColumnIndexOrThrow(ContactsDatabase.NAME_COLUMN)); + String number = cursor.getString(cursor.getColumnIndexOrThrow(ContactsDatabase.NUMBER_COLUMN)); + int numberType = cursor.getInt(cursor.getColumnIndexOrThrow(ContactsDatabase.NUMBER_TYPE_COLUMN)); + String label = cursor.getString(cursor.getColumnIndexOrThrow(ContactsDatabase.LABEL_COLUMN)); + String labelText = ContactsContract.CommonDataKinds.Phone.getTypeLabel(context.getResources(), + numberType, label).toString(); + + int color = (type == ContactsDatabase.PUSH_TYPE) ? drawables.getColor(0, 0xa0000000) : + drawables.getColor(1, 0xff000000); + + + ((ContactSelectionListItem)view).unbind(); + ((ContactSelectionListItem)view).set(id, type, name, number, labelText, color, multiSelect); + ((ContactSelectionListItem)view).setChecked(selectedContacts.containsKey(id)); } @Override public View getHeaderView(int i, View convertView, ViewGroup viewGroup) { - final Cursor c = getCursor(); - final HeaderViewHolder holder; + Cursor cursor = getCursor(); + + HeaderViewHolder holder; + if (convertView == null) { - holder = new HeaderViewHolder(); + holder = new HeaderViewHolder(); convertView = li.inflate(R.layout.push_contact_selection_list_header, viewGroup, false); holder.text = (TextView) convertView.findViewById(R.id.text); convertView.setTag(holder); } else { holder = (HeaderViewHolder) convertView.getTag(); } - c.moveToPosition(i); - final int type = c.getInt(c.getColumnIndexOrThrow(ContactsDatabase.TYPE_COLUMN)); - final int headerTextRes; - switch (type) { - case 1: headerTextRes = R.string.contact_selection_list__header_textsecure_users; break; - default: headerTextRes = R.string.contact_selection_list__header_other; break; - } - holder.text.setText(headerTextRes); - return convertView; - } + cursor.moveToPosition(i); - @Override - public long getHeaderId(int i) { - final Cursor c = getCursor(); - c.moveToPosition(i); - return c.getInt(c.getColumnIndexOrThrow(ContactsDatabase.TYPE_COLUMN)); - } + int type = cursor.getInt(cursor.getColumnIndexOrThrow(ContactsDatabase.TYPE_COLUMN)); - public boolean cancelPotentialWork(String number, ImageView imageView) { - final TaggedFutureTask bitmapWorkerTask = AsyncDrawable.getBitmapWorkerTask(imageView); + if (type == ContactsDatabase.PUSH_TYPE) holder.text.setText(R.string.contact_selection_list__header_textsecure_users); + else holder.text.setText(R.string.contact_selection_list__header_other); - if (bitmapWorkerTask != null) { - final Object tag = bitmapWorkerTask.getTag(); - if (tag != null && !tag.equals(number)) { - bitmapWorkerTask.cancel(true); - } else { - return false; - } - } - return true; + return convertView; } - // FIXME -- It should be unnecessary to duplicate the existing asynchronous resolution - // infrastructure we've built for Recipient objects here. - - public void loadBitmap(String number, ImageView imageView) { - if (cancelPotentialWork(number, imageView)) { - final BitmapWorkerRunnable runnable = new BitmapWorkerRunnable(context, imageView, number, scaledPhotoSize); - final TaggedFutureTask task = new TaggedFutureTask(runnable, null, number); - final AsyncDrawable asyncDrawable = new AsyncDrawable(task); + @Override + public long getHeaderId(int i) { + Cursor cursor = getCursor(); + cursor.moveToPosition(i); - imageView.setImageDrawable(asyncDrawable); - if (!task.isCancelled()) photoResolver.execute(new FutureTask(task, null)); - } + return cursor.getInt(cursor.getColumnIndexOrThrow(ContactsDatabase.TYPE_COLUMN)); } - public Map getSelectedContacts() { + public Map getSelectedContacts() { return selectedContacts; } - - private void populateColumnIndices(final Cursor cursor) { - this.TYPE_COLUMN = cursor.getColumnIndexOrThrow(ContactsDatabase.TYPE_COLUMN); - this.NAME_COLUMN = cursor.getColumnIndexOrThrow(ContactsDatabase.NAME_COLUMN); - this.NUMBER_COLUMN = cursor.getColumnIndexOrThrow(ContactsDatabase.NUMBER_COLUMN); - this.NUMBER_TYPE_COLUMN = cursor.getColumnIndexOrThrow(ContactsDatabase.NUMBER_TYPE_COLUMN); - this.LABEL_COLUMN = cursor.getColumnIndexOrThrow(ContactsDatabase.LABEL_COLUMN); - this.ID_COLUMN = cursor.getColumnIndexOrThrow(ContactsDatabase.ID_COLUMN); - } } diff --git a/src/org/thoughtcrime/securesms/contacts/ContactSelectionListItem.java b/src/org/thoughtcrime/securesms/contacts/ContactSelectionListItem.java new file mode 100644 index 0000000000..5e979b73f9 --- /dev/null +++ b/src/org/thoughtcrime/securesms/contacts/ContactSelectionListItem.java @@ -0,0 +1,126 @@ +package org.thoughtcrime.securesms.contacts; + +import android.content.Context; +import android.support.annotation.Nullable; +import android.util.AttributeSet; +import android.view.View; +import android.widget.CheckBox; +import android.widget.ImageView; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import org.thoughtcrime.securesms.R; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.recipients.RecipientFactory; + +public class ContactSelectionListItem extends RelativeLayout implements Recipient.RecipientModifiedListener { + + private ImageView contactPhotoImage; + private TextView numberView; + private TextView nameView; + private TextView labelView; + private CheckBox checkBox; + + private long id; + private String number; + private Recipient recipient; + + public ContactSelectionListItem(Context context) { + super(context); + } + + public ContactSelectionListItem(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public ContactSelectionListItem(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + @Override + protected void onFinishInflate() { + this.contactPhotoImage = (ImageView) findViewById(R.id.contact_photo_image); + this.numberView = (TextView) findViewById(R.id.number); + this.labelView = (TextView) findViewById(R.id.label); + this.nameView = (TextView) findViewById(R.id.name); + this.checkBox = (CheckBox) findViewById(R.id.check_box); + } + + public void set(long id, int type, String name, String number, String label, int color, boolean multiSelect) { + this.id = id; + this.number = number; + + if (number != null) { + this.recipient = RecipientFactory.getRecipientsFromString(getContext(), number, true) + .getPrimaryRecipient(); + } + + this.nameView.setTextColor(color); + this.numberView.setTextColor(color); + + setText(type, name, number, label); + setContactPhotoImage(recipient); + + if (multiSelect) this.checkBox.setVisibility(View.VISIBLE); + else this.checkBox.setVisibility(View.GONE); + } + + public void setChecked(boolean selected) { + this.checkBox.setChecked(selected); + } + + public void unbind() { + if (recipient != null) { + recipient.removeListener(this); + recipient = null; + } + } + + private void setText(int type, String name, String number, String label) { + if (number == null || number.isEmpty()) { + this.nameView.setEnabled(false); + this.numberView.setText(""); + this.labelView.setVisibility(View.GONE); + } else if (type == ContactsDatabase.PUSH_TYPE) { + this.numberView.setText(number); + this.nameView.setEnabled(true); + this.labelView.setVisibility(View.GONE); + } else { + this.numberView.setText(number); + this.nameView.setEnabled(true); + this.labelView.setText(label); + this.labelView.setVisibility(View.VISIBLE); + } + + this.nameView.setText(name); + } + + private void setContactPhotoImage(@Nullable Recipient recipient) { + if (recipient!= null) { + contactPhotoImage.setImageDrawable(recipient.getContactPhoto()); + recipient.addListener(this); + } else { + contactPhotoImage.setImageDrawable(null); + } + } + + @Override + public void onModified(final Recipient recipient) { + if (this.recipient == recipient) { + this.contactPhotoImage.post(new Runnable() { + @Override + public void run() { + contactPhotoImage.setImageDrawable(recipient.getContactPhoto()); + } + }); + } + } + + public long getContactId() { + return id; + } + + public String getNumber() { + return number; + } +} diff --git a/src/org/thoughtcrime/securesms/util/BitmapWorkerRunnable.java b/src/org/thoughtcrime/securesms/util/BitmapWorkerRunnable.java deleted file mode 100644 index 1018050a73..0000000000 --- a/src/org/thoughtcrime/securesms/util/BitmapWorkerRunnable.java +++ /dev/null @@ -1,97 +0,0 @@ -/** - * Copyright (C) 2014 Open 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 - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * 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.util; - -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.DrawableContainer; -import android.widget.ImageView; - -import com.makeramen.RoundedDrawable; - -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.recipients.RecipientFactory; - -import java.lang.ref.WeakReference; - -/** - * Runnable to load contact photos if they have them - * - * @author Jake McGinty - */ -public class BitmapWorkerRunnable implements Runnable { - private final static String TAG = BitmapWorkerRunnable.class.getSimpleName(); - - private final WeakReference imageViewReference; - private final Context context; - private final int size; - public final String number; - - public BitmapWorkerRunnable(Context context, ImageView imageView, String number, int size) { - this.imageViewReference = new WeakReference<>(imageView); - this.context = context; - this.size = size; - this.number = number; - } - - @Override - public void run() { - final Recipient recipient = RecipientFactory.getRecipientsFromString(context, number, false).getPrimaryRecipient(); - final Drawable contactPhoto = recipient.getContactPhoto(); - - if (contactPhoto != null) { - final ImageView imageView = imageViewReference.get(); - final TaggedFutureTask bitmapWorkerTask = AsyncDrawable.getBitmapWorkerTask(imageView); - - if (bitmapWorkerTask.getTag().equals(number) && imageView != null) { - imageView.post(new Runnable() { - @Override - public void run() { - imageView.setImageDrawable(contactPhoto); - } - }); - } - } - } - - public static class AsyncDrawable extends BitmapDrawable { - private final WeakReference> bitmapWorkerTaskReference; - - public AsyncDrawable(TaggedFutureTask bitmapWorkerTask) { - bitmapWorkerTaskReference = - new WeakReference>(bitmapWorkerTask); - } - - public TaggedFutureTask getBitmapWorkerTask() { - return bitmapWorkerTaskReference.get(); - } - - public static TaggedFutureTask getBitmapWorkerTask(ImageView imageView) { - if (imageView != null) { - final Drawable drawable = imageView.getDrawable(); - if (drawable instanceof AsyncDrawable) { - final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable; - return asyncDrawable.getBitmapWorkerTask(); - } - } - return null; - } - } -}