Group contact chips behind feature flag.
parent
8e0fba7992
commit
0b279d1df3
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"data": [
|
||||
{
|
||||
"name": "Ottttooooooooo Ocataaaaaaaavius",
|
||||
"number": "+1 (555) 555-5555",
|
||||
"label": "Mobile"
|
||||
},
|
||||
{
|
||||
"name": "Victor Von Doom Phd",
|
||||
"number": "+1 (555) 123-4567",
|
||||
"label": "Home"
|
||||
},
|
||||
{
|
||||
"name": "Flash Thompson",
|
||||
"number": "+1 (555) 435-1261",
|
||||
"label": "Work"
|
||||
},
|
||||
{
|
||||
"name": "Dr. Curtis Connors",
|
||||
"number": "+1 (555) 992-1567",
|
||||
"label": "Mobile"
|
||||
},
|
||||
{
|
||||
"name": "Billy Russo",
|
||||
"number": "+1 (555) 234-1516",
|
||||
"label": "Mobile"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -28,6 +28,7 @@ import android.view.View;
|
|||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.HorizontalScrollView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
|
@ -35,26 +36,32 @@ import androidx.annotation.NonNull;
|
|||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.loader.app.LoaderManager;
|
||||
import androidx.loader.content.Loader;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
import com.google.android.material.chip.ChipGroup;
|
||||
import com.pnikosis.materialishprogress.ProgressWheel;
|
||||
|
||||
import org.thoughtcrime.securesms.components.RecyclerViewFastScroller;
|
||||
import org.thoughtcrime.securesms.contacts.ContactChip;
|
||||
import org.thoughtcrime.securesms.contacts.ContactSelectionListAdapter;
|
||||
import org.thoughtcrime.securesms.contacts.ContactSelectionListItem;
|
||||
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader;
|
||||
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader.DisplayMode;
|
||||
import org.thoughtcrime.securesms.contacts.SelectedContact;
|
||||
import org.thoughtcrime.securesms.contacts.sync.DirectoryHelper;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import org.thoughtcrime.securesms.permissions.Permissions;
|
||||
import org.thoughtcrime.securesms.contacts.sync.DirectoryHelper;
|
||||
import org.thoughtcrime.securesms.recipients.LiveRecipient;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.util.Debouncer;
|
||||
import org.thoughtcrime.securesms.util.FeatureFlags;
|
||||
import org.thoughtcrime.securesms.util.StickyHeaderDecoration;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
|
@ -67,9 +74,7 @@ import org.thoughtcrime.securesms.util.views.SimpleProgressDialog;
|
|||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Fragment for selecting a one or more contacts from a list.
|
||||
|
@ -88,8 +93,9 @@ public final class ContactSelectionListFragment extends Fragment
|
|||
public static final String REFRESHABLE = "refreshable";
|
||||
public static final String RECENTS = "recents";
|
||||
|
||||
private final Debouncer scrollDebounce = new Debouncer(100);
|
||||
|
||||
private TextView emptyText;
|
||||
private Set<SelectedContact> selectedContacts;
|
||||
private OnContactSelectedListener onContactSelectedListener;
|
||||
private SwipeRefreshLayout swipeRefresh;
|
||||
private View showContactsLayout;
|
||||
|
@ -100,10 +106,13 @@ public final class ContactSelectionListFragment extends Fragment
|
|||
private RecyclerView recyclerView;
|
||||
private RecyclerViewFastScroller fastScroller;
|
||||
private ContactSelectionListAdapter cursorRecyclerViewAdapter;
|
||||
private ChipGroup chipGroup;
|
||||
private HorizontalScrollView chipGroupScrollContainer;
|
||||
|
||||
@Nullable private FixedViewsAdapter headerAdapter;
|
||||
@Nullable private FixedViewsAdapter footerAdapter;
|
||||
@Nullable private ListCallback listCallback;
|
||||
private GlideRequests glideRequests;
|
||||
|
||||
@Override
|
||||
public void onAttach(@NonNull Context context) {
|
||||
|
@ -132,14 +141,16 @@ public final class ContactSelectionListFragment extends Fragment
|
|||
if (!TextSecurePreferences.hasSuccessfullyRetrievedDirectory(getActivity())) {
|
||||
handleContactPermissionGranted();
|
||||
} else {
|
||||
this.getLoaderManager().initLoader(0, null, this);
|
||||
LoaderManager.getInstance(this).initLoader(0, null, this);
|
||||
}
|
||||
})
|
||||
.onAnyDenied(() -> {
|
||||
getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
|
||||
FragmentActivity activity = requireActivity();
|
||||
|
||||
if (getActivity().getIntent().getBooleanExtra(RECENTS, false)) {
|
||||
getLoaderManager().initLoader(0, null, ContactSelectionListFragment.this);
|
||||
activity.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
|
||||
|
||||
if (activity.getIntent().getBooleanExtra(RECENTS, false)) {
|
||||
LoaderManager.getInstance(this).initLoader(0, null, ContactSelectionListFragment.this);
|
||||
} else {
|
||||
initializeNoContactsPermission();
|
||||
}
|
||||
|
@ -151,17 +162,22 @@ public final class ContactSelectionListFragment extends Fragment
|
|||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.contact_selection_list_fragment, container, false);
|
||||
|
||||
emptyText = ViewUtil.findById(view, android.R.id.empty);
|
||||
recyclerView = ViewUtil.findById(view, R.id.recycler_view);
|
||||
swipeRefresh = ViewUtil.findById(view, R.id.swipe_refresh);
|
||||
fastScroller = ViewUtil.findById(view, R.id.fast_scroller);
|
||||
showContactsLayout = view.findViewById(R.id.show_contacts_container);
|
||||
showContactsButton = view.findViewById(R.id.show_contacts_button);
|
||||
showContactsDescription = view.findViewById(R.id.show_contacts_description);
|
||||
showContactsProgress = view.findViewById(R.id.progress);
|
||||
emptyText = ViewUtil.findById(view, android.R.id.empty);
|
||||
recyclerView = ViewUtil.findById(view, R.id.recycler_view);
|
||||
swipeRefresh = ViewUtil.findById(view, R.id.swipe_refresh);
|
||||
fastScroller = ViewUtil.findById(view, R.id.fast_scroller);
|
||||
showContactsLayout = view.findViewById(R.id.show_contacts_container);
|
||||
showContactsButton = view.findViewById(R.id.show_contacts_button);
|
||||
showContactsDescription = view.findViewById(R.id.show_contacts_description);
|
||||
showContactsProgress = view.findViewById(R.id.progress);
|
||||
chipGroup = view.findViewById(R.id.chipGroup);
|
||||
chipGroupScrollContainer = view.findViewById(R.id.chipGroupScrollContainer);
|
||||
|
||||
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
|
||||
|
||||
swipeRefresh.setEnabled(getActivity().getIntent().getBooleanExtra(REFRESHABLE, true));
|
||||
swipeRefresh.setEnabled(requireActivity().getIntent().getBooleanExtra(REFRESHABLE, true));
|
||||
|
||||
autoScrollOnNewItem();
|
||||
|
||||
return view;
|
||||
}
|
||||
|
@ -171,26 +187,22 @@ public final class ContactSelectionListFragment extends Fragment
|
|||
Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults);
|
||||
}
|
||||
|
||||
public @NonNull List<SelectedContact> getSelectedContacts() {
|
||||
List<SelectedContact> selected = new LinkedList<>();
|
||||
if (selectedContacts != null) {
|
||||
selected.addAll(selectedContacts);
|
||||
}
|
||||
|
||||
return selected;
|
||||
@NonNull List<SelectedContact> getSelectedContacts() {
|
||||
return cursorRecyclerViewAdapter.getSelectedContacts();
|
||||
}
|
||||
|
||||
private boolean isMulti() {
|
||||
return getActivity().getIntent().getBooleanExtra(MULTI_SELECT, false);
|
||||
return requireActivity().getIntent().getBooleanExtra(MULTI_SELECT, false);
|
||||
}
|
||||
|
||||
private void initializeCursor() {
|
||||
glideRequests = GlideApp.with(this);
|
||||
|
||||
cursorRecyclerViewAdapter = new ContactSelectionListAdapter(requireContext(),
|
||||
GlideApp.with(this),
|
||||
glideRequests,
|
||||
null,
|
||||
new ListClickListener(),
|
||||
isMulti());
|
||||
selectedContacts = cursorRecyclerViewAdapter.getSelectedContacts();
|
||||
|
||||
RecyclerViewConcatenateAdapterStickyHeader concatenateAdapter = new RecyclerViewConcatenateAdapterStickyHeader();
|
||||
|
||||
|
@ -263,7 +275,7 @@ public final class ContactSelectionListFragment extends Fragment
|
|||
}
|
||||
|
||||
public void reset() {
|
||||
selectedContacts.clear();
|
||||
cursorRecyclerViewAdapter.clearSelectedContacts();
|
||||
|
||||
if (!isDetached() && !isRemoving() && getActivity() != null && !getActivity().isFinishing()) {
|
||||
getLoaderManager().restartLoader(0, null, this);
|
||||
|
@ -356,7 +368,7 @@ public final class ContactSelectionListFragment extends Fragment
|
|||
SelectedContact selectedContact = contact.isUsernameType() ? SelectedContact.forUsername(contact.getRecipientId().orNull(), contact.getNumber())
|
||||
: SelectedContact.forPhone(contact.getRecipientId().orNull(), contact.getNumber());
|
||||
|
||||
if (!isMulti() || !selectedContacts.contains(selectedContact)) {
|
||||
if (!isMulti() || !cursorRecyclerViewAdapter.isSelectedContact(selectedContact)) {
|
||||
if (contact.isUsernameType()) {
|
||||
AlertDialog loadingDialog = SimpleProgressDialog.show(requireContext());
|
||||
|
||||
|
@ -366,8 +378,8 @@ public final class ContactSelectionListFragment extends Fragment
|
|||
loadingDialog.dismiss();
|
||||
if (uuid.isPresent()) {
|
||||
Recipient recipient = Recipient.externalUsername(requireContext(), uuid.get(), contact.getNumber());
|
||||
selectedContacts.add(SelectedContact.forUsername(recipient.getId(), contact.getNumber()));
|
||||
contact.setChecked(true);
|
||||
SelectedContact selected = SelectedContact.forUsername(recipient.getId(), contact.getNumber());
|
||||
markContactSelected(selected, contact);
|
||||
|
||||
if (onContactSelectedListener != null) {
|
||||
onContactSelectedListener.onContactSelected(Optional.of(recipient.getId()), null);
|
||||
|
@ -381,24 +393,66 @@ public final class ContactSelectionListFragment extends Fragment
|
|||
}
|
||||
});
|
||||
} else {
|
||||
selectedContacts.add(selectedContact);
|
||||
contact.setChecked(true);
|
||||
markContactSelected(selectedContact, contact);
|
||||
|
||||
if (onContactSelectedListener != null) {
|
||||
onContactSelectedListener.onContactSelected(contact.getRecipientId(), contact.getNumber());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
selectedContacts.remove(selectedContact);
|
||||
contact.setChecked(false);
|
||||
markContactUnselected(selectedContact, contact);
|
||||
|
||||
if (onContactSelectedListener != null) {
|
||||
onContactSelectedListener.onContactDeselected(contact.getRecipientId(), contact.getNumber());
|
||||
}
|
||||
}}
|
||||
}
|
||||
|
||||
private void markContactSelected(@NonNull SelectedContact selectedContact, @NonNull ContactSelectionListItem listItem) {
|
||||
cursorRecyclerViewAdapter.addSelectedContact(selectedContact);
|
||||
listItem.setChecked(true);
|
||||
if (isMulti() && FeatureFlags.newGroupUI()) {
|
||||
chipGroup.addView(newChipForContact(listItem, selectedContact));
|
||||
}
|
||||
}
|
||||
|
||||
private void markContactUnselected(@NonNull SelectedContact selectedContact, @NonNull ContactSelectionListItem listItem) {
|
||||
cursorRecyclerViewAdapter.removeFromSelectedContacts(selectedContact);
|
||||
listItem.setChecked(false);
|
||||
removeChipForContact(selectedContact);
|
||||
}
|
||||
|
||||
private void removeChipForContact(@NonNull SelectedContact contact) {
|
||||
for (int i = chipGroup.getChildCount() - 1; i >= 0; i--) {
|
||||
View v = chipGroup.getChildAt(i);
|
||||
if (v instanceof ContactChip && contact.matches(((ContactChip) v).getContact())) {
|
||||
chipGroup.removeView(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private View newChipForContact(@NonNull ContactSelectionListItem contact, @NonNull SelectedContact selectedContact) {
|
||||
final ContactChip chip = new ContactChip(requireContext());
|
||||
chip.setText(contact.getChipName());
|
||||
chip.setContact(selectedContact);
|
||||
|
||||
LiveRecipient recipient = contact.getRecipient();
|
||||
if (recipient != null) {
|
||||
recipient.observe(getViewLifecycleOwner(), resolved -> {
|
||||
chip.setAvatar(glideRequests, resolved);
|
||||
chip.setText(resolved.getShortDisplayName(chip.getContext()));
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
chip.setCloseIconVisible(true);
|
||||
chip.setOnCloseIconClickListener(view -> {
|
||||
markContactUnselected(selectedContact, contact);
|
||||
chipGroup.removeView(chip);
|
||||
});
|
||||
return chip;
|
||||
}
|
||||
|
||||
public void setOnContactSelectedListener(OnContactSelectedListener onContactSelectedListener) {
|
||||
this.onContactSelectedListener = onContactSelectedListener;
|
||||
}
|
||||
|
@ -407,6 +461,19 @@ public final class ContactSelectionListFragment extends Fragment
|
|||
this.swipeRefresh.setOnRefreshListener(onRefreshListener);
|
||||
}
|
||||
|
||||
private void autoScrollOnNewItem() {
|
||||
chipGroup.addOnLayoutChangeListener((view1, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
|
||||
if (right > oldRight) {
|
||||
scrollDebounce.publish(this::smoothScrollChipsToEnd);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void smoothScrollChipsToEnd() {
|
||||
int x = chipGroupScrollContainer.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR ? chipGroup.getWidth() : 0;
|
||||
chipGroupScrollContainer.smoothScrollTo(x, 0);
|
||||
}
|
||||
|
||||
public interface OnContactSelectedListener {
|
||||
void onContactSelected(Optional<RecipientId> recipientId, String number);
|
||||
void onContactDeselected(Optional<RecipientId> recipientId, String number);
|
||||
|
|
|
@ -22,8 +22,6 @@ import androidx.appcompat.widget.Toolbar;
|
|||
import androidx.core.content.ContextCompat;
|
||||
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
|
||||
|
||||
import com.annimon.stream.Stream;
|
||||
|
||||
import org.thoughtcrime.securesms.components.ContactFilterToolbar;
|
||||
import org.thoughtcrime.securesms.components.ContactFilterToolbar.OnFilterChangedListener;
|
||||
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader.DisplayMode;
|
||||
|
@ -42,6 +40,7 @@ import org.thoughtcrime.securesms.util.concurrent.ListenableFuture.Listener;
|
|||
import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
public class InviteActivity extends PassphraseRequiredActionBarActivity implements ContactSelectionListFragment.OnContactSelectedListener {
|
||||
|
@ -135,14 +134,15 @@ public class InviteActivity extends PassphraseRequiredActionBarActivity implemen
|
|||
new SendSmsInvitesAsyncTask(this, inviteText.getText().toString())
|
||||
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
|
||||
contactsFragment.getSelectedContacts()
|
||||
.toArray(new SelectedContact[contactsFragment.getSelectedContacts().size()]));
|
||||
.toArray(new SelectedContact[0]));
|
||||
}
|
||||
|
||||
private void updateSmsButtonText() {
|
||||
List<SelectedContact> selectedContacts = contactsFragment.getSelectedContacts();
|
||||
smsSendButton.setText(getResources().getQuantityString(R.plurals.InviteActivity_send_sms_to_friends,
|
||||
contactsFragment.getSelectedContacts().size(),
|
||||
contactsFragment.getSelectedContacts().size()));
|
||||
smsSendButton.setEnabled(!contactsFragment.getSelectedContacts().isEmpty());
|
||||
selectedContacts.size(),
|
||||
selectedContacts.size()));
|
||||
smsSendButton.setEnabled(!selectedContacts.isEmpty());
|
||||
}
|
||||
|
||||
@Override public void onBackPressed() {
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
package org.thoughtcrime.securesms.contacts;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.ColorFilter;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||
import com.bumptech.glide.request.target.CustomTarget;
|
||||
import com.bumptech.glide.request.transition.Transition;
|
||||
import com.google.android.material.chip.Chip;
|
||||
|
||||
import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
|
||||
public final class ContactChip extends Chip {
|
||||
|
||||
@Nullable private SelectedContact contact;
|
||||
|
||||
public ContactChip(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public ContactChip(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public ContactChip(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
public void setContact(@NonNull SelectedContact contact) {
|
||||
this.contact = contact;
|
||||
}
|
||||
|
||||
public @Nullable SelectedContact getContact() {
|
||||
return contact;
|
||||
}
|
||||
|
||||
public void setAvatar(@NonNull GlideRequests requestManager, @Nullable Recipient recipient) {
|
||||
if (recipient != null) {
|
||||
requestManager.clear(this);
|
||||
|
||||
Drawable fallbackContactPhotoDrawable = recipient.getFallbackContactPhotoDrawable(getContext(), false);
|
||||
ContactPhoto contactPhoto = recipient.getContactPhoto();
|
||||
|
||||
if (contactPhoto == null) {
|
||||
setChipIcon(new HalfScaleDrawable(fallbackContactPhotoDrawable));
|
||||
} else {
|
||||
requestManager.load(contactPhoto)
|
||||
.fallback(fallbackContactPhotoDrawable)
|
||||
.error(fallbackContactPhotoDrawable)
|
||||
.diskCacheStrategy(DiskCacheStrategy.ALL)
|
||||
.circleCrop()
|
||||
.into(new CustomTarget<Drawable>() {
|
||||
@Override
|
||||
public void onResourceReady(@NonNull Drawable resource, @Nullable Transition<? super Drawable> transition) {
|
||||
setChipIcon(resource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadCleared(@Nullable Drawable placeholder) {
|
||||
setChipIcon(placeholder);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class HalfScaleDrawable extends Drawable {
|
||||
|
||||
private final Drawable fallbackContactPhotoDrawable;
|
||||
|
||||
HalfScaleDrawable(Drawable fallbackContactPhotoDrawable) {
|
||||
this.fallbackContactPhotoDrawable = fallbackContactPhotoDrawable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBounds(int left, int top, int right, int bottom) {
|
||||
super.setBounds(left, top, right, bottom);
|
||||
fallbackContactPhotoDrawable.setBounds(left, top, 2 * right - left, 2 * bottom - top);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBounds(@NonNull Rect bounds) {
|
||||
super.setBounds(bounds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(@NonNull Canvas canvas) {
|
||||
canvas.save();
|
||||
canvas.scale(0.5f, 0.5f);
|
||||
fallbackContactPhotoDrawable.draw(canvas);
|
||||
canvas.restore();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAlpha(int alpha) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColorFilter(@Nullable ColorFilter colorFilter) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOpacity() {
|
||||
return PixelFormat.OPAQUE;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -43,8 +43,8 @@ import org.thoughtcrime.securesms.recipients.RecipientId;
|
|||
import org.thoughtcrime.securesms.util.StickyHeaderDecoration.StickyHeaderAdapter;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* List adapter to display all contacts and their related information
|
||||
|
@ -70,7 +70,26 @@ public class ContactSelectionListAdapter extends CursorRecyclerViewAdapter<ViewH
|
|||
private final ItemClickListener clickListener;
|
||||
private final GlideRequests glideRequests;
|
||||
|
||||
private final Set<SelectedContact> selectedContacts = new HashSet<>();
|
||||
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 {
|
||||
|
||||
|
@ -227,8 +246,8 @@ public class ContactSelectionListAdapter extends CursorRecyclerViewAdapter<ViewH
|
|||
return getHeaderString(position);
|
||||
}
|
||||
|
||||
public Set<SelectedContact> getSelectedContacts() {
|
||||
return selectedContacts;
|
||||
public List<SelectedContact> getSelectedContacts() {
|
||||
return selectedContacts.getContacts();
|
||||
}
|
||||
|
||||
private CharSequence getSpannedHeaderString(int position) {
|
||||
|
|
|
@ -35,6 +35,7 @@ public class ContactSelectionListItem extends LinearLayout implements RecipientF
|
|||
private CheckBox checkBox;
|
||||
|
||||
private String number;
|
||||
private String chipName;
|
||||
private int contactType;
|
||||
private LiveRecipient recipient;
|
||||
private GlideRequests glideRequests;
|
||||
|
@ -128,8 +129,10 @@ public class ContactSelectionListItem extends LinearLayout implements RecipientF
|
|||
|
||||
if (recipient != null) {
|
||||
this.nameView.setText(recipient);
|
||||
chipName = recipient.getShortDisplayName(getContext());
|
||||
} else {
|
||||
this.nameView.setText(name);
|
||||
chipName = name;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -137,6 +140,14 @@ public class ContactSelectionListItem extends LinearLayout implements RecipientF
|
|||
return number;
|
||||
}
|
||||
|
||||
public String getChipName() {
|
||||
return chipName;
|
||||
}
|
||||
|
||||
public @Nullable LiveRecipient getRecipient() {
|
||||
return recipient;
|
||||
}
|
||||
|
||||
public boolean isUsernameType() {
|
||||
return contactType == ContactRepository.NEW_USERNAME_TYPE;
|
||||
}
|
||||
|
|
|
@ -8,16 +8,12 @@ import androidx.annotation.Nullable;
|
|||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Model for a contact and the various ways it could be represented. Used in situations where we
|
||||
* don't want to create Recipients for the wrapped data (like a custom-entered phone number for
|
||||
* someone you don't yet have a conversation with).
|
||||
*
|
||||
* Designed so that two instances will be equal if *any* of its properties match.
|
||||
*/
|
||||
public class SelectedContact {
|
||||
public final class SelectedContact {
|
||||
private final RecipientId recipientId;
|
||||
private final String number;
|
||||
private final String username;
|
||||
|
@ -46,19 +42,14 @@ public class SelectedContact {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
SelectedContact that = (SelectedContact) o;
|
||||
/**
|
||||
* Returns true iff any non-null property matches one on the other contact.
|
||||
*/
|
||||
public boolean matches(@Nullable SelectedContact other) {
|
||||
if (other == null) return false;
|
||||
|
||||
return Objects.equals(recipientId, that.recipientId) ||
|
||||
Objects.equals(number, that.number) ||
|
||||
Objects.equals(username, that.username);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(recipientId, number, username);
|
||||
return recipientId != null && recipientId.equals(other.recipientId) ||
|
||||
number != null && number .equals(other.number) ||
|
||||
username != null && username .equals(other.username);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
package org.thoughtcrime.securesms.contacts;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Specialised set for {@link SelectedContact} that will not allow more than one entry that
|
||||
* {@link SelectedContact#matches(SelectedContact)} any other.
|
||||
*/
|
||||
public final class SelectedContactSet {
|
||||
|
||||
private final List<SelectedContact> contacts = new LinkedList<>();
|
||||
|
||||
public boolean add(@NonNull SelectedContact contact) {
|
||||
if (contains(contact)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
contacts.add(contact);
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean contains(@NonNull SelectedContact otherContact) {
|
||||
for (SelectedContact contact : contacts) {
|
||||
if (otherContact.matches(contact)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public List<SelectedContact> getContacts() {
|
||||
return new ArrayList<>(contacts);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
contacts.clear();
|
||||
}
|
||||
|
||||
public int remove(@NonNull SelectedContact otherContact) {
|
||||
int removeCount = 0;
|
||||
Iterator<SelectedContact> iterator = contacts.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
SelectedContact next = iterator.next();
|
||||
if (next.matches(otherContact)) {
|
||||
iterator.remove();
|
||||
removeCount++;
|
||||
}
|
||||
}
|
||||
|
||||
return removeCount;
|
||||
}
|
||||
}
|
|
@ -415,6 +415,12 @@ public class Recipient {
|
|||
context.getString(R.string.Recipient_unknown));
|
||||
}
|
||||
|
||||
public @NonNull String getShortDisplayName(@NonNull Context context) {
|
||||
return Util.getFirstNonEmpty(getName(context),
|
||||
getProfileName().getGivenName(),
|
||||
getDisplayName(context));
|
||||
}
|
||||
|
||||
public @NonNull MaterialColor getColor() {
|
||||
if (isGroupInternal()) {
|
||||
return MaterialColor.GROUP;
|
||||
|
@ -610,6 +616,10 @@ public class Recipient {
|
|||
return getFallbackContactPhotoDrawable(context, inverted, DEFAULT_FALLBACK_PHOTO_PROVIDER);
|
||||
}
|
||||
|
||||
public @NonNull Drawable getSmallFallbackContactPhotoDrawable(Context context, boolean inverted) {
|
||||
return getSmallFallbackContactPhotoDrawable(context, inverted, DEFAULT_FALLBACK_PHOTO_PROVIDER);
|
||||
}
|
||||
|
||||
public @NonNull Drawable getFallbackContactPhotoDrawable(Context context, boolean inverted, @Nullable FallbackPhotoProvider fallbackPhotoProvider) {
|
||||
return getFallbackContactPhoto(Util.firstNonNull(fallbackPhotoProvider, DEFAULT_FALLBACK_PHOTO_PROVIDER)).asDrawable(context, getColor().toAvatarColor(context), inverted);
|
||||
}
|
||||
|
|
|
@ -1,27 +1,34 @@
|
|||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:orientation="vertical">
|
||||
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:id="@+id/swipe_refresh"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:id="@+id/swipe_refresh"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/chipGroupScrollContainer">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recycler_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:scrollbars="vertical" />
|
||||
android:scrollbars="vertical"
|
||||
tools:listitem="@layout/contact_selection_list_item" />
|
||||
|
||||
<TextView android:id="@android:id/empty"
|
||||
<TextView
|
||||
android:id="@android:id/empty"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center|center_vertical"
|
||||
android:layout_marginTop="15dp"
|
||||
android:gravity="center|center_vertical"
|
||||
android:text="@string/contact_selection_group_activity__finding_contacts"
|
||||
android:textSize="20sp" />
|
||||
|
||||
|
@ -30,64 +37,108 @@
|
|||
<org.thoughtcrime.securesms.components.RecyclerViewFastScroller
|
||||
android:id="@+id/fast_scroller"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="gone"
|
||||
android:layout_height="0dp"
|
||||
android:layout_gravity="end"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/chipGroupScrollContainer"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<LinearLayout android:id="@+id/show_contacts_container"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible">
|
||||
<LinearLayout
|
||||
android:id="@+id/show_contacts_container"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/chipGroupScrollContainer"
|
||||
tools:visibility="visible">
|
||||
|
||||
<Button
|
||||
android:id="@+id/show_contacts_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginTop="20dp"
|
||||
android:background="@color/core_ultramarine"
|
||||
android:padding="10dp"
|
||||
android:text="@string/contact_selection_list_fragment__show_contacts"
|
||||
android:textColor="@color/white" />
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.pnikosis.materialishprogress.ProgressWheel
|
||||
android:id="@+id/progress"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="invisible"
|
||||
app:matProg_circleRadius="145dp"
|
||||
app:matProg_barWidth="6dp"
|
||||
app:matProg_rimColor="@color/core_ultramarine"
|
||||
app:matProg_barColor="@color/core_ultramarine_dark"
|
||||
app:matProg_progressIndeterminate="true"
|
||||
tools:visibility="visible"
|
||||
/>
|
||||
|
||||
<ImageView android:layout_gravity="center"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/no_contacts"/>
|
||||
android:id="@+id/progress"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="invisible"
|
||||
app:matProg_barColor="@color/core_ultramarine_dark"
|
||||
app:matProg_barWidth="6dp"
|
||||
app:matProg_circleRadius="145dp"
|
||||
app:matProg_progressIndeterminate="true"
|
||||
app:matProg_rimColor="@color/core_ultramarine"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:importantForAccessibility="no"
|
||||
android:src="@drawable/no_contacts" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<TextView android:id="@+id/show_contacts_description"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginStart="50dp"
|
||||
android:layout_marginEnd="50dp"
|
||||
android:textSize="15sp"
|
||||
android:lineSpacingMultiplier="1.3"
|
||||
android:gravity="center"
|
||||
android:text="@string/contact_selection_list_fragment__signal_needs_access_to_your_contacts_in_order_to_display_them"/>
|
||||
<TextView
|
||||
android:id="@+id/show_contacts_description"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="50dp"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginEnd="50dp"
|
||||
android:gravity="center"
|
||||
android:lineSpacingMultiplier="1.3"
|
||||
android:text="@string/contact_selection_list_fragment__signal_needs_access_to_your_contacts_in_order_to_display_them"
|
||||
android:textSize="15sp" />
|
||||
|
||||
<Button android:id="@+id/show_contacts_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_gravity="center_horizontal"
|
||||
style="@style/Button.Primary"
|
||||
android:textColor="@color/white"
|
||||
android:padding="10dp"
|
||||
android:text="@string/contact_selection_list_fragment__show_contacts"/>
|
||||
</LinearLayout>
|
||||
|
||||
</FrameLayout>
|
||||
<HorizontalScrollView
|
||||
android:id="@+id/chipGroupScrollContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:scrollbars="none"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<com.google.android.material.chip.ChipGroup
|
||||
android:id="@+id/chipGroup"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="96dp"
|
||||
android:animateLayoutChanges="true"
|
||||
app:singleLine="true">
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
tools:text="Example"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</com.google.android.material.chip.ChipGroup>
|
||||
|
||||
</HorizontalScrollView>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -1,5 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<org.thoughtcrime.securesms.contacts.ContactSelectionListItem
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
|
@ -38,7 +37,7 @@
|
|||
android:ellipsize="marquee"
|
||||
style="@style/Signal.Text.Body"
|
||||
android:fontFamily="sans-serif-medium"
|
||||
tools:text="Ottttooooooooo Ocataaaaaaaavius" />
|
||||
tools:text="@sample/contacts.json/data/name" />
|
||||
|
||||
<LinearLayout android:id="@+id/number_container"
|
||||
android:orientation="horizontal"
|
||||
|
@ -54,7 +53,7 @@
|
|||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textSize="14sp"
|
||||
android:fontFamily="sans-serif-light"
|
||||
tools:text="+1 (555) 555-5555" />
|
||||
tools:text="@sample/contacts.json/data/number" />
|
||||
|
||||
<TextView android:id="@+id/label"
|
||||
android:layout_width="wrap_content"
|
||||
|
@ -64,12 +63,11 @@
|
|||
android:singleLine="true"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:fontFamily="sans-serif-light"
|
||||
tools:text="Mobile"
|
||||
tools:text="@sample/contacts.json/data/label"
|
||||
tools:ignore="RtlSymmetry" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<CheckBox android:id="@+id/check_box"
|
||||
|
|
|
@ -150,7 +150,7 @@
|
|||
<!-- Allows for Overrides -->
|
||||
</style>
|
||||
|
||||
<style name="TextSecure.BaseLightTheme" parent="@style/Theme.AppCompat.Light">
|
||||
<style name="TextSecure.BaseLightTheme" parent="@style/Theme.MaterialComponents.Light.Bridge">
|
||||
<item name="theme_type">light</item>
|
||||
<item name="icon_tint">@color/core_grey_75</item>
|
||||
<item name="icon_tint_dark">@color/core_grey_15</item>
|
||||
|
@ -421,6 +421,8 @@
|
|||
<item name="contact_selection_new_group_icon">@drawable/ic_new_group_circle_light</item>
|
||||
<item name="contact_selection_invite_icon">@drawable/ic_invite_circle_light</item>
|
||||
|
||||
<item name="chipStyle">@style/Signal.Chip.Action.Light</item>
|
||||
|
||||
<item name="colorControlNormal">@color/core_grey_90</item>
|
||||
</style>
|
||||
|
||||
|
@ -428,7 +430,7 @@
|
|||
<!-- leave empty to allow overriding -->
|
||||
</style>
|
||||
|
||||
<style name="TextSecure.BaseDarkTheme" parent="@style/Theme.AppCompat">
|
||||
<style name="TextSecure.BaseDarkTheme" parent="@style/Theme.MaterialComponents.Bridge">
|
||||
<item name="theme_type">dark</item>
|
||||
<item name="icon_tint">@color/core_grey_15</item>
|
||||
<item name="icon_tint_dark">?icon_tint</item>
|
||||
|
@ -699,6 +701,8 @@
|
|||
<item name="contact_selection_new_group_icon">@drawable/ic_new_group_circle_dark</item>
|
||||
<item name="contact_selection_invite_icon">@drawable/ic_invite_circle_dark</item>
|
||||
|
||||
<item name="chipStyle">@style/Signal.Chip.Action</item>
|
||||
|
||||
<item name="colorControlNormal">@color/core_white</item>
|
||||
</style>
|
||||
|
||||
|
@ -836,4 +840,24 @@
|
|||
<item name="recipient_remove_icon">@drawable/ic_leave_tinted_24</item>
|
||||
</style>
|
||||
|
||||
<style name="Signal.Chip.Action" parent="Widget.MaterialComponents.Chip.Action">
|
||||
<item name="chipIconSize">32dp</item>
|
||||
<item name="chipStartPadding">0dp</item>
|
||||
<item name="closeIcon">@drawable/ic_x</item>
|
||||
<item name="closeIconSize">12dp</item>
|
||||
<item name="textEndPadding">4dp</item>
|
||||
<!-- Negative padding gives us 4dp between icon and text, while allowing 10dp for when no icon -->
|
||||
<item name="iconEndPadding">-6dp</item>
|
||||
<item name="textStartPadding">10dp</item>
|
||||
<item name="android:textColor">@color/core_white</item>
|
||||
<item name="chipBackgroundColor">@color/core_grey_80</item>
|
||||
<item name="closeIconTint">@color/core_grey_15</item>
|
||||
</style>
|
||||
|
||||
<style name="Signal.Chip.Action.Light">
|
||||
<item name="android:textColor">@color/core_grey_90</item>
|
||||
<item name="chipBackgroundColor">@color/core_grey_05</item>
|
||||
<item name="closeIconTint">@color/core_grey_60</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
package org.thoughtcrime.securesms.contacts;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
|
||||
import static edu.emory.mathcs.backport.java.util.Collections.singletonList;
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public final class SelectedContactSetTest {
|
||||
|
||||
private final SelectedContactSet selectedContactSet = new SelectedContactSet();
|
||||
|
||||
@Test
|
||||
public void add_without_recipient_ids() {
|
||||
SelectedContact contact1 = SelectedContact.forPhone(null, "+1-555-000-0000");
|
||||
SelectedContact contact2 = SelectedContact.forUsername(null, "@alice");
|
||||
|
||||
assertTrue(selectedContactSet.add(contact1));
|
||||
assertTrue(selectedContactSet.add(contact2));
|
||||
|
||||
assertThat(selectedContactSet.getContacts(), is(asList(contact1, contact2)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void add_with_recipient_ids() {
|
||||
SelectedContact contact1 = SelectedContact.forPhone(RecipientId.from(1), "+1-555-000-0000");
|
||||
SelectedContact contact2 = SelectedContact.forUsername(RecipientId.from(2), "@alice");
|
||||
|
||||
assertTrue(selectedContactSet.add(contact1));
|
||||
assertTrue(selectedContactSet.add(contact2));
|
||||
|
||||
assertThat(selectedContactSet.getContacts(), is(asList(contact1, contact2)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void add_with_same_recipient_id() {
|
||||
SelectedContact contact1 = SelectedContact.forPhone(RecipientId.from(1), "+1-555-000-0000");
|
||||
SelectedContact contact2 = SelectedContact.forUsername(RecipientId.from(1), "@alice");
|
||||
|
||||
assertTrue(selectedContactSet.add(contact1));
|
||||
assertFalse(selectedContactSet.add(contact2));
|
||||
|
||||
assertThat(selectedContactSet.getContacts(), is(singletonList(contact1)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void remove_by_recipient_id() {
|
||||
SelectedContact contact1 = SelectedContact.forPhone(RecipientId.from(1), "+1-555-000-0000");
|
||||
SelectedContact contact2 = SelectedContact.forUsername(RecipientId.from(2), "@alice" );
|
||||
SelectedContact contact2Remove = SelectedContact.forUsername(RecipientId.from(2), "@alice2");
|
||||
|
||||
assertTrue(selectedContactSet.add(contact1));
|
||||
assertTrue(selectedContactSet.add(contact2));
|
||||
assertEquals(1, selectedContactSet.remove(contact2Remove));
|
||||
|
||||
assertThat(selectedContactSet.getContacts(), is(singletonList(contact1)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void remove_by_number() {
|
||||
SelectedContact contact1 = SelectedContact.forPhone(RecipientId.from(1), "+1-555-000-0000");
|
||||
SelectedContact contact2 = SelectedContact.forUsername(RecipientId.from(2), "@alice");
|
||||
SelectedContact contact1Remove = SelectedContact.forPhone(null, "+1-555-000-0000");
|
||||
|
||||
assertTrue(selectedContactSet.add(contact1));
|
||||
assertTrue(selectedContactSet.add(contact2));
|
||||
assertEquals(1, selectedContactSet.remove(contact1Remove));
|
||||
|
||||
assertThat(selectedContactSet.getContacts(), is(singletonList(contact2)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void remove_by_username() {
|
||||
SelectedContact contact1 = SelectedContact.forPhone(RecipientId.from(1), "+1-555-000-0000");
|
||||
SelectedContact contact2 = SelectedContact.forUsername(RecipientId.from(2), "@alice");
|
||||
SelectedContact contact2Remove = SelectedContact.forUsername(null, "@alice");
|
||||
|
||||
assertTrue(selectedContactSet.add(contact1));
|
||||
assertTrue(selectedContactSet.add(contact2));
|
||||
assertEquals(1, selectedContactSet.remove(contact2Remove));
|
||||
|
||||
assertThat(selectedContactSet.getContacts(), is(singletonList(contact1)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void remove_by_recipient_id_and_username() {
|
||||
SelectedContact contact1 = SelectedContact.forPhone(RecipientId.from(1), "+1-555-000-0000");
|
||||
SelectedContact contact2 = SelectedContact.forUsername(RecipientId.from(2), "@alice");
|
||||
SelectedContact contact3 = SelectedContact.forUsername(null, "@bob");
|
||||
SelectedContact contact2Remove = SelectedContact.forUsername(RecipientId.from(1), "@alice");
|
||||
|
||||
assertTrue(selectedContactSet.add(contact1));
|
||||
assertTrue(selectedContactSet.add(contact2));
|
||||
assertTrue(selectedContactSet.add(contact3));
|
||||
assertEquals(2, selectedContactSet.remove(contact2Remove));
|
||||
|
||||
assertThat(selectedContactSet.getContacts(), is(singletonList(contact3)));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue