300 lines
11 KiB
Java
300 lines
11 KiB
Java
/**
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
package org.thoughtcrime.securesms.contacts;
|
|
|
|
import android.content.Context;
|
|
import android.content.res.TypedArray;
|
|
import android.database.Cursor;
|
|
import android.provider.ContactsContract;
|
|
import android.text.SpannableString;
|
|
import android.text.Spanned;
|
|
import android.text.style.ForegroundColorSpan;
|
|
import android.view.LayoutInflater;
|
|
import android.view.View;
|
|
import android.view.ViewGroup;
|
|
import android.widget.TextView;
|
|
|
|
import androidx.annotation.NonNull;
|
|
import androidx.annotation.Nullable;
|
|
import androidx.recyclerview.widget.RecyclerView;
|
|
|
|
import org.thoughtcrime.securesms.R;
|
|
import org.thoughtcrime.securesms.components.RecyclerViewFastScroller.FastScrollAdapter;
|
|
import org.thoughtcrime.securesms.contacts.ContactSelectionListAdapter.HeaderViewHolder;
|
|
import org.thoughtcrime.securesms.contacts.ContactSelectionListAdapter.ViewHolder;
|
|
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter;
|
|
import org.thoughtcrime.securesms.logging.Log;
|
|
import org.thoughtcrime.securesms.mms.GlideRequests;
|
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
|
import org.thoughtcrime.securesms.util.StickyHeaderDecoration.StickyHeaderAdapter;
|
|
import org.thoughtcrime.securesms.util.Util;
|
|
|
|
import java.util.List;
|
|
import java.util.Locale;
|
|
|
|
/**
|
|
* List adapter to display all contacts and their related information
|
|
*
|
|
* @author Jake McGinty
|
|
*/
|
|
public class ContactSelectionListAdapter extends CursorRecyclerViewAdapter<ViewHolder>
|
|
implements FastScrollAdapter,
|
|
StickyHeaderAdapter<HeaderViewHolder>
|
|
{
|
|
@SuppressWarnings("unused")
|
|
private final static String TAG = Log.tag(ContactSelectionListAdapter.class);
|
|
|
|
private static final int VIEW_TYPE_CONTACT = 0;
|
|
private static final int VIEW_TYPE_DIVIDER = 1;
|
|
|
|
private final static int STYLE_ATTRIBUTES[] = new int[]{R.attr.contact_selection_push_user,
|
|
R.attr.contact_selection_lay_user};
|
|
|
|
private final boolean multiSelect;
|
|
private final LayoutInflater li;
|
|
private final TypedArray drawables;
|
|
private final ItemClickListener clickListener;
|
|
private final GlideRequests glideRequests;
|
|
|
|
private final SelectedContactSet selectedContacts = new SelectedContactSet();
|
|
|
|
public void clearSelectedContacts() {
|
|
selectedContacts.clear();
|
|
}
|
|
|
|
public boolean isSelectedContact(@NonNull SelectedContact contact) {
|
|
return selectedContacts.contains(contact);
|
|
}
|
|
|
|
public void addSelectedContact(@NonNull SelectedContact contact) {
|
|
if (!selectedContacts.add(contact)) {
|
|
Log.i(TAG, "Contact was already selected, possibly by another identifier");
|
|
}
|
|
}
|
|
|
|
public void removeFromSelectedContacts(@NonNull SelectedContact selectedContact) {
|
|
int removed = selectedContacts.remove(selectedContact);
|
|
Log.i(TAG, String.format(Locale.US, "Removed %d selected contacts that matched", removed));
|
|
}
|
|
|
|
public abstract static class ViewHolder extends RecyclerView.ViewHolder {
|
|
|
|
public ViewHolder(View itemView) {
|
|
super(itemView);
|
|
}
|
|
|
|
public abstract void bind(@NonNull GlideRequests glideRequests, @Nullable RecipientId recipientId, int type, String name, String number, String label, int color, boolean multiSelect);
|
|
public abstract void unbind(@NonNull GlideRequests glideRequests);
|
|
public abstract void setChecked(boolean checked);
|
|
}
|
|
|
|
public static class ContactViewHolder extends ViewHolder {
|
|
ContactViewHolder(@NonNull final View itemView,
|
|
@Nullable final ItemClickListener clickListener)
|
|
{
|
|
super(itemView);
|
|
itemView.setOnClickListener(v -> {
|
|
if (clickListener != null) clickListener.onItemClick(getView());
|
|
});
|
|
}
|
|
|
|
public ContactSelectionListItem getView() {
|
|
return (ContactSelectionListItem) itemView;
|
|
}
|
|
|
|
public void bind(@NonNull GlideRequests glideRequests, @Nullable RecipientId recipientId, int type, String name, String number, String label, int color, boolean multiSelect) {
|
|
getView().set(glideRequests, recipientId, type, name, number, label, color, multiSelect);
|
|
}
|
|
|
|
@Override
|
|
public void unbind(@NonNull GlideRequests glideRequests) {
|
|
getView().unbind(glideRequests);
|
|
}
|
|
|
|
@Override
|
|
public void setChecked(boolean checked) {
|
|
getView().setChecked(checked);
|
|
}
|
|
}
|
|
|
|
public static class DividerViewHolder extends ViewHolder {
|
|
|
|
private final TextView label;
|
|
|
|
DividerViewHolder(View itemView) {
|
|
super(itemView);
|
|
this.label = itemView.findViewById(R.id.label);
|
|
}
|
|
|
|
@Override
|
|
public void bind(@NonNull GlideRequests glideRequests, @Nullable RecipientId recipientId, int type, String name, String number, String label, int color, boolean multiSelect) {
|
|
this.label.setText(name);
|
|
}
|
|
|
|
@Override
|
|
public void unbind(@NonNull GlideRequests glideRequests) {}
|
|
|
|
@Override
|
|
public void setChecked(boolean checked) {}
|
|
}
|
|
|
|
static class HeaderViewHolder extends RecyclerView.ViewHolder {
|
|
HeaderViewHolder(View itemView) {
|
|
super(itemView);
|
|
}
|
|
}
|
|
|
|
public ContactSelectionListAdapter(@NonNull Context context,
|
|
@NonNull GlideRequests glideRequests,
|
|
@Nullable Cursor cursor,
|
|
@Nullable ItemClickListener clickListener,
|
|
boolean multiSelect)
|
|
{
|
|
super(context, cursor);
|
|
this.li = LayoutInflater.from(context);
|
|
this.glideRequests = glideRequests;
|
|
this.drawables = context.obtainStyledAttributes(STYLE_ATTRIBUTES);
|
|
this.multiSelect = multiSelect;
|
|
this.clickListener = clickListener;
|
|
}
|
|
|
|
@Override
|
|
public long getHeaderId(int i) {
|
|
if (!isActiveCursor()) return -1;
|
|
|
|
int contactType = getContactType(i);
|
|
|
|
if (contactType == ContactRepository.DIVIDER_TYPE) return -1;
|
|
return Util.hashCode(getHeaderString(i), getContactType(i));
|
|
}
|
|
|
|
@Override
|
|
public ViewHolder onCreateItemViewHolder(ViewGroup parent, int viewType) {
|
|
if (viewType == VIEW_TYPE_CONTACT) {
|
|
return new ContactViewHolder(li.inflate(R.layout.contact_selection_list_item, parent, false), clickListener);
|
|
} else {
|
|
return new DividerViewHolder(li.inflate(R.layout.contact_selection_list_divider, parent, false));
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onBindItemViewHolder(ViewHolder viewHolder, @NonNull Cursor cursor) {
|
|
String rawId = cursor.getString(cursor.getColumnIndexOrThrow(ContactRepository.ID_COLUMN));
|
|
RecipientId id = rawId != null ? RecipientId.from(rawId) : null;
|
|
int contactType = cursor.getInt(cursor.getColumnIndexOrThrow(ContactRepository.CONTACT_TYPE_COLUMN));
|
|
String name = cursor.getString(cursor.getColumnIndexOrThrow(ContactRepository.NAME_COLUMN ));
|
|
String number = cursor.getString(cursor.getColumnIndexOrThrow(ContactRepository.NUMBER_COLUMN));
|
|
int numberType = cursor.getInt(cursor.getColumnIndexOrThrow(ContactRepository.NUMBER_TYPE_COLUMN ));
|
|
String label = cursor.getString(cursor.getColumnIndexOrThrow(ContactRepository.LABEL_COLUMN ));
|
|
String labelText = ContactsContract.CommonDataKinds.Phone.getTypeLabel(getContext().getResources(),
|
|
numberType, label).toString();
|
|
|
|
int color = (contactType == ContactRepository.PUSH_TYPE) ? drawables.getColor(0, 0xa0000000) :
|
|
drawables.getColor(1, 0xff000000);
|
|
|
|
viewHolder.unbind(glideRequests);
|
|
viewHolder.bind(glideRequests, id, contactType, name, number, labelText, color, multiSelect);
|
|
|
|
if (numberType == ContactRepository.NEW_USERNAME_TYPE) {
|
|
viewHolder.setChecked(selectedContacts.contains(SelectedContact.forUsername(id, number)));
|
|
} else {
|
|
viewHolder.setChecked(selectedContacts.contains(SelectedContact.forPhone(id, number)));
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public int getItemViewType(@NonNull Cursor cursor) {
|
|
if (cursor.getInt(cursor.getColumnIndexOrThrow(ContactRepository.CONTACT_TYPE_COLUMN)) == ContactRepository.DIVIDER_TYPE) {
|
|
return VIEW_TYPE_DIVIDER;
|
|
} else {
|
|
return VIEW_TYPE_CONTACT;
|
|
}
|
|
}
|
|
|
|
|
|
@Override
|
|
public HeaderViewHolder onCreateHeaderViewHolder(ViewGroup parent, int position) {
|
|
return new HeaderViewHolder(LayoutInflater.from(getContext()).inflate(R.layout.contact_selection_recyclerview_header, parent, false));
|
|
}
|
|
|
|
@Override
|
|
public void onBindHeaderViewHolder(HeaderViewHolder viewHolder, int position) {
|
|
((TextView)viewHolder.itemView).setText(getSpannedHeaderString(position));
|
|
}
|
|
|
|
@Override
|
|
public void onItemViewRecycled(ViewHolder holder) {
|
|
holder.unbind(glideRequests);
|
|
}
|
|
|
|
@Override
|
|
public CharSequence getBubbleText(int position) {
|
|
return getHeaderString(position);
|
|
}
|
|
|
|
public List<SelectedContact> getSelectedContacts() {
|
|
return selectedContacts.getContacts();
|
|
}
|
|
|
|
private CharSequence getSpannedHeaderString(int position) {
|
|
final String headerString = getHeaderString(position);
|
|
if (isPush(position)) {
|
|
SpannableString spannable = new SpannableString(headerString);
|
|
spannable.setSpan(new ForegroundColorSpan(getContext().getResources().getColor(R.color.core_ultramarine)), 0, headerString.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
|
return spannable;
|
|
} else {
|
|
return headerString;
|
|
}
|
|
}
|
|
|
|
private @NonNull String getHeaderString(int position) {
|
|
int contactType = getContactType(position);
|
|
|
|
if (contactType == ContactRepository.RECENT_TYPE || contactType == ContactRepository.DIVIDER_TYPE) {
|
|
return " ";
|
|
}
|
|
|
|
Cursor cursor = getCursorAtPositionOrThrow(position);
|
|
String letter = cursor.getString(cursor.getColumnIndexOrThrow(ContactRepository.NAME_COLUMN));
|
|
|
|
if (letter != null) {
|
|
letter = letter.trim();
|
|
if (letter.length() > 0) {
|
|
char firstChar = letter.charAt(0);
|
|
if (Character.isLetterOrDigit(firstChar)) {
|
|
return String.valueOf(Character.toUpperCase(firstChar));
|
|
}
|
|
}
|
|
}
|
|
|
|
return "#";
|
|
}
|
|
|
|
private int getContactType(int position) {
|
|
final Cursor cursor = getCursorAtPositionOrThrow(position);
|
|
return cursor.getInt(cursor.getColumnIndexOrThrow(ContactRepository.CONTACT_TYPE_COLUMN));
|
|
}
|
|
|
|
private boolean isPush(int position) {
|
|
return getContactType(position) == ContactRepository.PUSH_TYPE;
|
|
}
|
|
|
|
public interface ItemClickListener {
|
|
void onItemClick(ContactSelectionListItem item);
|
|
}
|
|
}
|