Fix contact selection behavior when searching and clear search on selection.

This commit is contained in:
Alex Hart 2020-06-02 16:27:04 -03:00 committed by GitHub
parent ae87d23003
commit 7f2439f1e9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 88 additions and 20 deletions

View File

@ -44,6 +44,7 @@ import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;
import androidx.recyclerview.widget.DefaultItemAnimator;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
@ -195,6 +196,12 @@ public final class ContactSelectionListFragment extends Fragment
constraintLayout = view.findViewById(R.id.container);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
recyclerView.setItemAnimator(new DefaultItemAnimator() {
@Override
public boolean canReuseUpdatedViewHolder(@NonNull RecyclerView.ViewHolder viewHolder) {
return true;
}
});
swipeRefresh.setEnabled(requireActivity().getIntent().getBooleanExtra(REFRESHABLE, true));
@ -335,6 +342,10 @@ public final class ContactSelectionListFragment extends Fragment
swipeRefresh.setRefreshing(false);
}
public boolean hasQueryFilter() {
return !TextUtils.isEmpty(cursorFilter);
}
public void setRefreshing(boolean refreshing) {
swipeRefresh.setRefreshing(refreshing);
}
@ -455,7 +466,8 @@ public final class ContactSelectionListFragment extends Fragment
if (uuid.isPresent()) {
Recipient recipient = Recipient.externalUsername(requireContext(), uuid.get(), contact.getNumber());
SelectedContact selected = SelectedContact.forUsername(recipient.getId(), contact.getNumber());
markContactSelected(selected, contact);
markContactSelected(selected);
cursorRecyclerViewAdapter.notifyItemChanged(recyclerView.getChildAdapterPosition(contact), ContactSelectionListAdapter.PAYLOAD_SELECTION_CHANGE);
if (onContactSelectedListener != null) {
onContactSelectedListener.onContactSelected(Optional.of(recipient.getId()), null);
@ -469,14 +481,16 @@ public final class ContactSelectionListFragment extends Fragment
}
});
} else {
markContactSelected(selectedContact, contact);
markContactSelected(selectedContact);
cursorRecyclerViewAdapter.notifyItemChanged(recyclerView.getChildAdapterPosition(contact), ContactSelectionListAdapter.PAYLOAD_SELECTION_CHANGE);
if (onContactSelectedListener != null) {
onContactSelectedListener.onContactSelected(contact.getRecipientId(), contact.getNumber());
}
}
} else {
markContactUnselected(selectedContact, contact);
markContactUnselected(selectedContact);
cursorRecyclerViewAdapter.notifyItemChanged(recyclerView.getChildAdapterPosition(contact), ContactSelectionListAdapter.PAYLOAD_SELECTION_CHANGE);
if (onContactSelectedListener != null) {
onContactSelectedListener.onContactDeselected(contact.getRecipientId(), contact.getNumber());
@ -488,17 +502,16 @@ public final class ContactSelectionListFragment extends Fragment
return getChipCount() >= selectionLimit;
}
private void markContactSelected(@NonNull SelectedContact selectedContact, @NonNull ContactSelectionListItem listItem) {
private void markContactSelected(@NonNull SelectedContact selectedContact) {
cursorRecyclerViewAdapter.addSelectedContact(selectedContact);
listItem.setChecked(true);
if (isMulti() && FeatureFlags.newGroupUI()) {
addChipForContact(listItem, selectedContact);
addChipForSelectedContact(selectedContact);
}
}
private void markContactUnselected(@NonNull SelectedContact selectedContact, @NonNull ContactSelectionListItem listItem) {
private void markContactUnselected(@NonNull SelectedContact selectedContact) {
cursorRecyclerViewAdapter.removeFromSelectedContacts(selectedContact);
listItem.setChecked(false);
cursorRecyclerViewAdapter.notifyItemRangeChanged(0, cursorRecyclerViewAdapter.getItemCount(), ContactSelectionListAdapter.PAYLOAD_SELECTION_CHANGE);
removeChipForContact(selectedContact);
}
@ -517,17 +530,23 @@ public final class ContactSelectionListFragment extends Fragment
}
}
private void addChipForContact(@NonNull ContactSelectionListItem contact, @NonNull SelectedContact selectedContact) {
private void addChipForSelectedContact(@NonNull SelectedContact selectedContact) {
SimpleTask.run(getViewLifecycleOwner().getLifecycle(),
() -> Recipient.resolved(selectedContact.getOrCreateRecipientId(requireContext())),
resolved -> addChipForRecipient(resolved, selectedContact));
}
private void addChipForRecipient(@NonNull Recipient recipient, @NonNull SelectedContact selectedContact) {
final ContactChip chip = new ContactChip(requireContext());
if (getChipCount() == 0) {
setChipGroupVisibility(ConstraintSet.VISIBLE);
}
chip.setText(contact.getChipName());
chip.setText(recipient.getShortDisplayName(requireContext()));
chip.setContact(selectedContact);
chip.setCloseIconVisible(true);
chip.setOnCloseIconClickListener(view -> markContactUnselected(selectedContact, contact));
chip.setOnCloseIconClickListener(view -> markContactUnselected(selectedContact));
chipGroup.getLayoutTransition().addTransitionListener(new LayoutTransition.TransitionListener() {
@Override
@ -538,18 +557,13 @@ public final class ContactSelectionListFragment extends Fragment
public void endTransition(LayoutTransition transition, ViewGroup container, View view, int transitionType) {
if (view == chip && transitionType == LayoutTransition.APPEARING) {
chipGroup.getLayoutTransition().removeTransitionListener(this);
registerChipRecipientObserver(chip, contact.getRecipient());
registerChipRecipientObserver(chip, recipient.live());
chipGroup.post(ContactSelectionListFragment.this::smoothScrollChipsToEnd);
}
}
});
LiveRecipient recipient = contact.getRecipient();
if (recipient != null) {
chip.setAvatar(glideRequests, recipient.get(), () -> addChip(chip));
} else {
addChip(chip);
}
chip.setAvatar(glideRequests, recipient, () -> addChip(chip));
}
private void addChip(@NonNull ContactChip chip) {

View File

@ -64,6 +64,8 @@ public class ContactSelectionListAdapter extends CursorRecyclerViewAdapter<ViewH
private final static int STYLE_ATTRIBUTES[] = new int[]{R.attr.contact_selection_push_user,
R.attr.contact_selection_lay_user};
public static final int PAYLOAD_SELECTION_CHANGE = 1;
private final boolean multiSelect;
private final LayoutInflater li;
private final TypedArray drawables;
@ -175,6 +177,7 @@ public class ContactSelectionListAdapter extends CursorRecyclerViewAdapter<ViewH
@Override
public long getHeaderId(int i) {
if (!isActiveCursor()) return -1;
else if (i == -1) return -1;
int contactType = getContactType(i);
@ -216,6 +219,24 @@ public class ContactSelectionListAdapter extends CursorRecyclerViewAdapter<ViewH
}
}
@Override
protected void onBindItemViewHolder(ViewHolder viewHolder, @NonNull Cursor cursor, @NonNull List<Object> payloads) {
if (!arePayloadsValid(payloads)) {
throw new AssertionError();
}
String rawId = cursor.getString(cursor.getColumnIndexOrThrow(ContactRepository.ID_COLUMN));
RecipientId id = rawId != null ? RecipientId.from(rawId) : null;
int numberType = cursor.getInt(cursor.getColumnIndexOrThrow(ContactRepository.NUMBER_TYPE_COLUMN));
String number = cursor.getString(cursor.getColumnIndexOrThrow(ContactRepository.NUMBER_COLUMN));
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) {
@ -225,7 +246,6 @@ public class ContactSelectionListAdapter extends CursorRecyclerViewAdapter<ViewH
}
}
@Override
public HeaderViewHolder onCreateHeaderViewHolder(ViewGroup parent, int position) {
return new HeaderViewHolder(LayoutInflater.from(getContext()).inflate(R.layout.contact_selection_recyclerview_header, parent, false));
@ -236,6 +256,11 @@ public class ContactSelectionListAdapter extends CursorRecyclerViewAdapter<ViewH
((TextView)viewHolder.itemView).setText(getSpannedHeaderString(position));
}
@Override
protected boolean arePayloadsValid(@NonNull List<Object> payloads) {
return payloads.size() == 1 && payloads.get(0).equals(PAYLOAD_SELECTION_CHANGE);
}
@Override
public void onItemViewRecycled(ViewHolder holder) {
holder.unbind(glideRequests);

View File

@ -31,6 +31,8 @@ import androidx.recyclerview.widget.RecyclerView.ViewHolder;
import org.thoughtcrime.securesms.R;
import java.util.List;
/**
* RecyclerView.Adapter that manages a Cursor, comparable to the CursorAdapter usable in ListView/GridView.
*/
@ -173,6 +175,16 @@ public abstract class CursorRecyclerViewAdapter<VH extends RecyclerView.ViewHold
public abstract VH onCreateItemViewHolder(ViewGroup parent, int viewType);
@Override
public final void onBindViewHolder(@NonNull ViewHolder viewHolder, int position, @NonNull List<Object> payloads) {
if (arePayloadsValid(payloads) && !isHeaderPosition(position) && !isFooterPosition(position)) {
if (isFastAccessPosition(position)) onBindFastAccessItemViewHolder((VH)viewHolder, position, payloads);
else onBindItemViewHolder((VH)viewHolder, getCursorAtPositionOrThrow(position), payloads);
} else {
onBindViewHolder(viewHolder, position);
}
}
@SuppressWarnings("unchecked")
@Override
public final void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
@ -188,8 +200,17 @@ public abstract class CursorRecyclerViewAdapter<VH extends RecyclerView.ViewHold
public abstract void onBindItemViewHolder(VH viewHolder, @NonNull Cursor cursor);
protected void onBindFastAccessItemViewHolder(VH viewHolder, int position) {
protected boolean arePayloadsValid(@NonNull List<Object> payloads) {
return false;
}
protected void onBindItemViewHolder(VH viewHolder, @NonNull Cursor cursor, @NonNull List<Object> payloads) {
}
protected void onBindFastAccessItemViewHolder(VH viewHolder, int position) {
}
protected void onBindFastAccessItemViewHolder(VH viewHolder, int position, @NonNull List<Object> payloads) {
}
@Override

View File

@ -82,6 +82,10 @@ public class CreateGroupActivity extends ContactSelectionActivity {
@Override
public void onContactSelected(Optional<RecipientId> recipientId, String number) {
if (contactsFragment.hasQueryFilter()) {
getToolbar().clear();
}
if (contactsFragment.getSelectedContactsCount() >= MINIMUM_GROUP_SIZE) {
enableNext();
}
@ -89,6 +93,10 @@ public class CreateGroupActivity extends ContactSelectionActivity {
@Override
public void onContactDeselected(Optional<RecipientId> recipientId, String number) {
if (contactsFragment.hasQueryFilter()) {
getToolbar().clear();
}
if (contactsFragment.getSelectedContactsCount() < MINIMUM_GROUP_SIZE) {
disableNext();
}