Fix chip jank and other groups v2 ux issues.
This commit is contained in:
parent
00996f0d7a
commit
903c3989b9
10 changed files with 120 additions and 151 deletions
|
@ -3,12 +3,21 @@ package org.thoughtcrime.securesms;
|
|||
|
||||
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");
|
||||
}
|
||||
|
@ -19,14 +28,23 @@ public class ClearProfileAvatarActivity extends Activity {
|
|||
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)
|
||||
.setMessage(titleId)
|
||||
.setNegativeButton(android.R.string.cancel, (dialog, which) -> finish())
|
||||
.setPositiveButton(R.string.ClearProfileActivity_remove, (dialog, which) -> {
|
||||
Intent result = new Intent();
|
||||
|
|
|
@ -18,6 +18,7 @@ package org.thoughtcrime.securesms;
|
|||
|
||||
|
||||
import android.Manifest;
|
||||
import android.animation.LayoutTransition;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
|
@ -36,6 +37,8 @@ import android.widget.Toast;
|
|||
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;
|
||||
|
@ -43,6 +46,8 @@ 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;
|
||||
|
@ -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;
|
||||
|
@ -178,13 +185,12 @@ public final class ContactSelectionListFragment extends Fragment
|
|||
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;
|
||||
}
|
||||
|
||||
|
@ -445,7 +451,7 @@ public final class ContactSelectionListFragment extends Fragment
|
|||
cursorRecyclerViewAdapter.addSelectedContact(selectedContact);
|
||||
listItem.setChecked(true);
|
||||
if (isMulti() && FeatureFlags.newGroupUI()) {
|
||||
chipGroup.addView(newChipForContact(listItem, selectedContact));
|
||||
addChipForContact(listItem, selectedContact);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -462,28 +468,65 @@ public final class ContactSelectionListFragment extends Fragment
|
|||
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) {
|
||||
recipient.observe(getViewLifecycleOwner(), resolved -> {
|
||||
chip.setAvatar(glideRequests, resolved);
|
||||
chip.setText(resolved.getShortDisplayName(chip.getContext()));
|
||||
chip.setAvatar(glideRequests, recipient.get(), () -> chipGroup.addView(chip));
|
||||
} else {
|
||||
chipGroup.addView(chip);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
chip.setCloseIconVisible(true);
|
||||
chip.setOnCloseIconClickListener(view -> {
|
||||
markContactUnselected(selectedContact, contact);
|
||||
chipGroup.removeView(chip);
|
||||
private void registerChipRecipientObserver(@NonNull ContactChip chip, @Nullable LiveRecipient recipient) {
|
||||
if (recipient != null) {
|
||||
recipient.observe(getViewLifecycleOwner(), resolved -> {
|
||||
if (chip.isAttachedToWindow()) {
|
||||
chip.setAvatar(glideRequests, resolved, null);
|
||||
chip.setText(resolved.getShortDisplayName(chip.getContext()));
|
||||
}
|
||||
});
|
||||
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) {
|
||||
|
@ -494,14 +537,6 @@ 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);
|
||||
|
|
|
@ -44,17 +44,21 @@ public final class ContactChip extends Chip {
|
|||
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)
|
||||
|
@ -63,6 +67,9 @@ public final class ContactChip extends Chip {
|
|||
@Override
|
||||
public void onResourceReady(@NonNull Drawable resource, @Nullable Transition<? super Drawable> transition) {
|
||||
setChipIcon(resource);
|
||||
if (onAvatarSet != null) {
|
||||
onAvatarSet.run();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -35,7 +35,6 @@ public abstract class GroupMemberEntry {
|
|||
|
||||
public final static class NewGroupCandidate extends GroupMemberEntry {
|
||||
|
||||
private final DefaultValueLiveData<Boolean> isSelected = new DefaultValueLiveData<>(false);
|
||||
private final Recipient member;
|
||||
|
||||
public NewGroupCandidate(@NonNull Recipient member) {
|
||||
|
@ -46,14 +45,6 @@ public abstract class GroupMemberEntry {
|
|||
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;
|
||||
|
|
|
@ -246,9 +246,6 @@ final class GroupMemberListAdapter extends LifecycleRecyclerAdapter<GroupMemberL
|
|||
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);
|
||||
|
|
|
@ -20,6 +20,7 @@ import android.widget.Toast;
|
|||
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;
|
||||
|
@ -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) {
|
||||
|
@ -142,7 +111,6 @@ public class AddGroupDetailsFragment extends Fragment {
|
|||
|
||||
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());
|
||||
|
@ -219,29 +187,15 @@ public class AddGroupDetailsFragment extends Fragment {
|
|||
}
|
||||
|
||||
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) {
|
||||
|
|
|
@ -18,6 +18,7 @@ import org.thoughtcrime.securesms.recipients.Recipient;
|
|||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.util.DefaultValueLiveData;
|
||||
import org.thoughtcrime.securesms.util.SingleLiveEvent;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.thoughtcrime.securesms.util.livedata.LiveDataUtil;
|
||||
|
||||
import java.util.HashSet;
|
||||
|
@ -28,7 +29,6 @@ import java.util.Set;
|
|||
public final class AddGroupDetailsViewModel extends ViewModel {
|
||||
|
||||
private final LiveData<List<GroupMemberEntry.NewGroupCandidate>> members;
|
||||
private final DefaultValueLiveData<Set<RecipientId>> selected = new DefaultValueLiveData<>(new HashSet<>());
|
||||
private final DefaultValueLiveData<Set<RecipientId>> deleted = new DefaultValueLiveData<>(new HashSet<>());
|
||||
private final MutableLiveData<String> name = new MutableLiveData<>("");
|
||||
private final MutableLiveData<byte[]> avatar = new MutableLiveData<>();
|
||||
|
@ -37,17 +37,14 @@ public final class AddGroupDetailsViewModel extends ViewModel {
|
|||
private final LiveData<Boolean> canSubmitForm = Transformations.map(name, name -> !TextUtils.isEmpty(name));
|
||||
private final AddGroupDetailsRepository repository;
|
||||
|
||||
AddGroupDetailsViewModel(@NonNull RecipientId[] recipientIds,
|
||||
private AddGroupDetailsViewModel(@NonNull RecipientId[] recipientIds,
|
||||
@NonNull AddGroupDetailsRepository repository)
|
||||
{
|
||||
this.repository = repository;
|
||||
|
||||
MutableLiveData<List<GroupMemberEntry.NewGroupCandidate>> initialMembers = new MutableLiveData<>();
|
||||
LiveData<List<GroupMemberEntry.NewGroupCandidate>> membersWithoutDeleted = LiveDataUtil.combineLatest(initialMembers,
|
||||
deleted,
|
||||
AddGroupDetailsViewModel::filterDeletedMembers);
|
||||
|
||||
members = LiveDataUtil.combineLatest(membersWithoutDeleted, selected, AddGroupDetailsViewModel::updateSelectedMembers);
|
||||
members = LiveDataUtil.combineLatest(initialMembers, deleted, AddGroupDetailsViewModel::filterDeletedMembers);
|
||||
isMms = Transformations.map(members, this::isAnyForcedSms);
|
||||
|
||||
repository.resolveMembers(recipientIds, initialMembers::postValue);
|
||||
|
@ -85,27 +82,10 @@ public final class AddGroupDetailsViewModel extends ViewModel {
|
|||
this.name.setValue(name);
|
||||
}
|
||||
|
||||
int toggleSelected(@NonNull Recipient recipient) {
|
||||
Set<RecipientId> selected = this.selected.getValue();
|
||||
|
||||
if (!selected.add(recipient.getId())) {
|
||||
selected.remove(recipient.getId());
|
||||
}
|
||||
|
||||
this.selected.setValue(selected);
|
||||
|
||||
return selected.size();
|
||||
}
|
||||
|
||||
void clearSelected() {
|
||||
this.selected.setValue(new HashSet<>());
|
||||
}
|
||||
|
||||
void deleteSelected() {
|
||||
Set<RecipientId> selected = this.selected.getValue();
|
||||
void delete(@NonNull RecipientId recipientId) {
|
||||
Set<RecipientId> deleted = this.deleted.getValue();
|
||||
|
||||
deleted.addAll(selected);
|
||||
deleted.add(recipientId);
|
||||
this.deleted.setValue(deleted);
|
||||
}
|
||||
|
||||
|
@ -139,14 +119,6 @@ public final class AddGroupDetailsViewModel extends ViewModel {
|
|||
.toList();
|
||||
}
|
||||
|
||||
private static @NonNull List<GroupMemberEntry.NewGroupCandidate> updateSelectedMembers(@NonNull List<GroupMemberEntry.NewGroupCandidate> members, @NonNull Set<RecipientId> selected) {
|
||||
for (GroupMemberEntry.NewGroupCandidate member : members) {
|
||||
member.setSelected(selected.contains(member.getMember().getId()));
|
||||
}
|
||||
|
||||
return members;
|
||||
}
|
||||
|
||||
private boolean isAnyForcedSms(@NonNull List<GroupMemberEntry.NewGroupCandidate> members) {
|
||||
return Stream.of(members)
|
||||
.anyMatch(member -> !member.getMember().isRegistered());
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
<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:id="@+id/container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
|
@ -112,20 +113,22 @@
|
|||
<HorizontalScrollView
|
||||
android:id="@+id/chipGroupScrollContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="56dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:scrollbars="none"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:visibility="visible">
|
||||
|
||||
<com.google.android.material.chip.ChipGroup
|
||||
android:id="@+id/chipGroup"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="56dp"
|
||||
android:layout_marginEnd="96dp"
|
||||
android:animateLayoutChanges="true"
|
||||
app:singleLine="true">
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item
|
||||
android:id="@+id/action_delete"
|
||||
android:icon="?attr/menu_trash_icon"
|
||||
android:title="@string/AddGroupDetailsFragment__remove"
|
||||
app:showAsAction="always" />
|
||||
</menu>
|
|
@ -500,6 +500,7 @@
|
|||
<string name="AddGroupDetailsFragment__youve_selected_a_contact_that_doesnt">You\'ve selected a contact that doesn\'t support Signal groups, so this group will be MMS.</string>
|
||||
<string name="AddGroupDetailsFragment__remove">Remove</string>
|
||||
<string name="AddGroupDetailsFragment__sms_contact">SMS contact</string>
|
||||
<string name="AddGroupDetailsFragment__remove_s_from_this_group">Remove %1$s from this group?</string>
|
||||
|
||||
<!-- ManageGroupActivity -->
|
||||
<string name="ManageGroupActivity_disappearing_messages">Disappearing messages</string>
|
||||
|
|
Loading…
Add table
Reference in a new issue