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.
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();
}

}
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
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
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
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
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.