Skip to content

Commit

Permalink
Fix chip jank and other groups v2 ux issues.
Browse files Browse the repository at this point in the history
  • Loading branch information
alex-signal authored and greyson-signal committed May 29, 2020
1 parent 00996f0 commit 903c398
Show file tree
Hide file tree
Showing 10 changed files with 120 additions and 151 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,21 @@

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;

import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.ThemeUtil;

public class ClearProfileAvatarActivity extends Activity {

private static final String ARG_TITLE = "arg_title";

private final DynamicTheme theme = new DynamicNoActionBarTheme();

public static Intent createForUserProfilePhoto() {
return new Intent("org.thoughtcrime.securesms.action.CLEAR_PROFILE_PHOTO");
}
Expand All @@ -19,23 +28,32 @@ public static Intent createForGroupProfilePhoto() {
return intent;
}

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

theme.onCreate(this);
}

@Override
public void onResume() {
super.onResume();

theme.onResume(this);

int titleId = getIntent().getIntExtra(ARG_TITLE, R.string.ClearProfileActivity_remove_profile_photo);

new AlertDialog.Builder(this)
.setTitle(titleId)
.setNegativeButton(android.R.string.cancel, (dialog, which) -> finish())
.setPositiveButton(R.string.ClearProfileActivity_remove, (dialog, which) -> {
Intent result = new Intent();
result.putExtra("delete", true);
setResult(Activity.RESULT_OK, result);
finish();
})
.setOnCancelListener(dialog -> finish())
.show();
.setMessage(titleId)
.setNegativeButton(android.R.string.cancel, (dialog, which) -> finish())
.setPositiveButton(R.string.ClearProfileActivity_remove, (dialog, which) -> {
Intent result = new Intent();
result.putExtra("delete", true);
setResult(Activity.RESULT_OK, result);
finish();
})
.setOnCancelListener(dialog -> finish())
.show();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@


import android.Manifest;
import android.animation.LayoutTransition;
import android.annotation.SuppressLint;
import android.content.Context;
import android.database.Cursor;
Expand All @@ -36,13 +37,17 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.ConstraintSet;
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 androidx.transition.AutoTransition;
import androidx.transition.TransitionManager;

import com.google.android.material.chip.ChipGroup;
import com.pnikosis.materialishprogress.ProgressWheel;
Expand Down Expand Up @@ -89,13 +94,15 @@ public final class ContactSelectionListFragment extends Fragment
@SuppressWarnings("unused")
private static final String TAG = Log.tag(ContactSelectionListFragment.class);

private static final int CHIP_GROUP_EMPTY_COUNT = 1;
private static final int CHIP_GROUP_REVEAL_DURATION_MS = 150;

public static final String DISPLAY_MODE = "display_mode";
public static final String MULTI_SELECT = "multi_select";
public static final String REFRESHABLE = "refreshable";
public static final String RECENTS = "recents";

private final Debouncer scrollDebounce = new Debouncer(100);

private ConstraintLayout constraintLayout;
private TextView emptyText;
private OnContactSelectedListener onContactSelectedListener;
private SwipeRefreshLayout swipeRefresh;
Expand Down Expand Up @@ -178,13 +185,12 @@ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
showContactsProgress = view.findViewById(R.id.progress);
chipGroup = view.findViewById(R.id.chipGroup);
chipGroupScrollContainer = view.findViewById(R.id.chipGroupScrollContainer);
constraintLayout = view.findViewById(R.id.container);

recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));

swipeRefresh.setEnabled(requireActivity().getIntent().getBooleanExtra(REFRESHABLE, true));

autoScrollOnNewItem();

return view;
}

Expand Down Expand Up @@ -445,7 +451,7 @@ private void markContactSelected(@NonNull SelectedContact selectedContact, @NonN
cursorRecyclerViewAdapter.addSelectedContact(selectedContact);
listItem.setChecked(true);
if (isMulti() && FeatureFlags.newGroupUI()) {
chipGroup.addView(newChipForContact(listItem, selectedContact));
addChipForContact(listItem, selectedContact);
}
}

Expand All @@ -462,28 +468,65 @@ private void removeChipForContact(@NonNull SelectedContact contact) {
chipGroup.removeView(v);
}
}

if (chipGroup.getChildCount() == CHIP_GROUP_EMPTY_COUNT) {
setChipGroupVisibility(ConstraintSet.GONE);
}
}

private View newChipForContact(@NonNull ContactSelectionListItem contact, @NonNull SelectedContact selectedContact) {
private void addChipForContact(@NonNull ContactSelectionListItem contact, @NonNull SelectedContact selectedContact) {
final ContactChip chip = new ContactChip(requireContext());

if (chipGroup.getChildCount() == CHIP_GROUP_EMPTY_COUNT) {
setChipGroupVisibility(ConstraintSet.VISIBLE);
}

chip.setText(contact.getChipName());
chip.setContact(selectedContact);
chip.setCloseIconVisible(true);
chip.setOnCloseIconClickListener(view -> markContactUnselected(selectedContact, contact));

chipGroup.getLayoutTransition().addTransitionListener(new LayoutTransition.TransitionListener() {
@Override
public void startTransition(LayoutTransition transition, ViewGroup container, View view, int transitionType) {
}

@Override
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());
chipGroup.post(ContactSelectionListFragment.this::smoothScrollChipsToEnd);
}
}
});

LiveRecipient recipient = contact.getRecipient();
if (recipient != null) {
chip.setAvatar(glideRequests, recipient.get(), () -> chipGroup.addView(chip));
} else {
chipGroup.addView(chip);
}
}

private void registerChipRecipientObserver(@NonNull ContactChip chip, @Nullable LiveRecipient recipient) {
if (recipient != null) {
recipient.observe(getViewLifecycleOwner(), resolved -> {
chip.setAvatar(glideRequests, resolved);
if (chip.isAttachedToWindow()) {
chip.setAvatar(glideRequests, resolved, null);
chip.setText(resolved.getShortDisplayName(chip.getContext()));
}
);
});
}
}

chip.setCloseIconVisible(true);
chip.setOnCloseIconClickListener(view -> {
markContactUnselected(selectedContact, contact);
chipGroup.removeView(chip);
});
return chip;
private void setChipGroupVisibility(int visibility) {
TransitionManager.beginDelayedTransition(constraintLayout, new AutoTransition().setDuration(CHIP_GROUP_REVEAL_DURATION_MS));

ConstraintSet constraintSet = new ConstraintSet();
constraintSet.clone(constraintLayout);
constraintSet.setVisibility(R.id.chipGroupScrollContainer, visibility);
constraintSet.applyTo(constraintLayout);
}

public void setOnContactSelectedListener(OnContactSelectedListener onContactSelectedListener) {
Expand All @@ -494,14 +537,6 @@ public void setOnRefreshListener(SwipeRefreshLayout.OnRefreshListener onRefreshL
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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,17 +44,21 @@ public void setContact(@NonNull SelectedContact contact) {
return contact;
}

public void setAvatar(@NonNull GlideRequests requestManager, @Nullable Recipient recipient) {
public void setAvatar(@NonNull GlideRequests requestManager, @Nullable Recipient recipient, @Nullable Runnable onAvatarSet) {
if (recipient != null) {
requestManager.clear(this);

Drawable fallbackContactPhotoDrawable = recipient.getFallbackContactPhotoDrawable(getContext(), false);
Drawable fallbackContactPhotoDrawable = new HalfScaleDrawable(recipient.getFallbackContactPhotoDrawable(getContext(), false));
ContactPhoto contactPhoto = recipient.getContactPhoto();

if (contactPhoto == null) {
setChipIcon(new HalfScaleDrawable(fallbackContactPhotoDrawable));
setChipIcon(fallbackContactPhotoDrawable);
if (onAvatarSet != null) {
onAvatarSet.run();
}
} else {
requestManager.load(contactPhoto)
.placeholder(fallbackContactPhotoDrawable)
.fallback(fallbackContactPhotoDrawable)
.error(fallbackContactPhotoDrawable)
.diskCacheStrategy(DiskCacheStrategy.ALL)
Expand All @@ -63,6 +67,9 @@ public void setAvatar(@NonNull GlideRequests requestManager, @Nullable Recipient
@Override
public void onResourceReady(@NonNull Drawable resource, @Nullable Transition<? super Drawable> transition) {
setChipIcon(resource);
if (onAvatarSet != null) {
onAvatarSet.run();
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,7 @@ public void setBusy(boolean busy) {

public final static class NewGroupCandidate extends GroupMemberEntry {

private final DefaultValueLiveData<Boolean> isSelected = new DefaultValueLiveData<>(false);
private final Recipient member;
private final Recipient member;

public NewGroupCandidate(@NonNull Recipient member) {
this.member = member;
Expand All @@ -46,14 +45,6 @@ public NewGroupCandidate(@NonNull Recipient member) {
return member;
}

public @NonNull LiveData<Boolean> isSelected() {
return isSelected;
}

public void setSelected(boolean isSelected) {
this.isSelected.postValue(isSelected);
}

@Override
boolean sameId(@NonNull GroupMemberEntry newItem) {
if (getClass() != newItem.getClass()) return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -246,9 +246,6 @@ void bind(@NonNull GroupMemberEntry memberEntry) {
bindRecipient(newGroupCandidate.getMember());
bindRecipientClick(newGroupCandidate.getMember());

itemView.setSelected(false);
newGroupCandidate.isSelected().observe(this, itemView::setSelected);

int smsWarningVisibility = newGroupCandidate.getMember().isRegistered() ? View.GONE : View.VISIBLE;

smsContact.setVisibility(smsWarningVisibility);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProviders;
Expand Down Expand Up @@ -58,38 +59,6 @@ public class AddGroupDetailsFragment extends Fragment {
private Drawable avatarPlaceholder;
private EditText name;
private Toolbar toolbar;
private ActionMode actionMode;

private ActionMode.Callback recipientActionModeCallback = new ActionMode.Callback() {
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
mode.getMenuInflater().inflate(R.menu.add_group_details_fragment_context_menu, menu);

return true;
}

@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false;
}

@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
if (item.getItemId() == R.id.action_delete) {
viewModel.deleteSelected();
mode.finish();
return true;
}

return false;
}

@Override
public void onDestroyActionMode(ActionMode mode) {
actionMode = null;
viewModel.clearSelected();
}
};

@Override
public void onAttach(@NonNull Context context) {
Expand Down Expand Up @@ -142,7 +111,6 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat

avatar.setOnClickListener(v -> AvatarSelectionBottomSheetDialogFragment.create(viewModel.hasAvatar(), true, REQUEST_CODE_AVATAR, true)
.show(getChildFragmentManager(), "BOTTOM"));
members.setRecipientLongClickListener(this::handleRecipientLongClick);
members.setRecipientClickListener(this::handleRecipientClick);
name.addTextChangedListener(new AfterTextChanged(editable -> viewModel.setName(editable.toString())));
toolbar.setNavigationOnClickListener(unused -> callback.onNavigationButtonPressed());
Expand Down Expand Up @@ -219,29 +187,15 @@ private void handleCreateClicked() {
}

private void handleRecipientClick(@NonNull Recipient recipient) {
if (actionMode == null) {
return;
}

int size = viewModel.toggleSelected(recipient);
if (size == 0) {
actionMode.finish();
}
}

private boolean handleRecipientLongClick(@NonNull Recipient recipient) {
if (actionMode != null) {
return false;
}

actionMode = toolbar.startActionMode(recipientActionModeCallback);

if (actionMode != null) {
viewModel.toggleSelected(recipient);
return true;
}

return false;
new AlertDialog.Builder(requireContext())
.setMessage(getString(R.string.AddGroupDetailsFragment__remove_s_from_this_group, recipient.getDisplayName(requireContext())))
.setCancelable(true)
.setNegativeButton(android.R.string.cancel, (dialog, which) -> dialog.cancel())
.setPositiveButton(R.string.AddGroupDetailsFragment__remove, (dialog, which) -> {
viewModel.delete(recipient.getId());
dialog.dismiss();
})
.show();
}

private void handleGroupCreateResult(@NonNull GroupCreateResult groupCreateResult) {
Expand Down

0 comments on commit 903c398

Please sign in to comment.