Notify user during group create of members that do not support GV2.

master
Alan Evans 2020-08-05 10:26:04 -03:00 committed by Greyson Parrelli
parent d8daa83c79
commit 845f6a0a93
7 changed files with 148 additions and 5 deletions

View File

@ -31,6 +31,7 @@ import com.dd.CircularProgressButton;
import org.thoughtcrime.securesms.LoggingFragment;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.groups.ui.GroupMemberListView;
import org.thoughtcrime.securesms.groups.ui.creategroup.dialogs.NonGv2MemberDialog;
import org.thoughtcrime.securesms.mediasend.AvatarSelectionActivity;
import org.thoughtcrime.securesms.mediasend.AvatarSelectionBottomSheetDialogFragment;
import org.thoughtcrime.securesms.mediasend.Media;
@ -43,6 +44,7 @@ import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.BitmapUtil;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.text.AfterTextChanged;
import org.thoughtcrime.securesms.util.views.LearnMoreTextView;
import java.util.Arrays;
import java.util.List;
@ -90,6 +92,7 @@ public class AddGroupDetailsFragment extends LoggingFragment {
GroupMemberListView members = view.findViewById(R.id.member_list);
ImageView avatar = view.findViewById(R.id.group_avatar);
View mmsWarning = view.findViewById(R.id.mms_warning);
LearnMoreTextView gv2Warning = view.findViewById(R.id.gv2_warning);
avatarPlaceholder = VectorDrawableCompat.create(getResources(), R.drawable.ic_camera_outline_32_ultramarine, requireActivity().getTheme());
@ -118,6 +121,12 @@ public class AddGroupDetailsFragment extends LoggingFragment {
avatar.setVisibility(isMms ? View.GONE : View.VISIBLE);
toolbar.setTitle(isMms ? R.string.AddGroupDetailsFragment__create_group : R.string.AddGroupDetailsFragment__name_this_group);
});
viewModel.getNonGv2CapableMembers().observe(getViewLifecycleOwner(), nonGv2CapableMembers -> {
gv2Warning.setVisibility(nonGv2CapableMembers.isEmpty() ? View.GONE : View.VISIBLE);
gv2Warning.setText(requireContext().getResources().getQuantityString(R.plurals.AddGroupDetailsFragment__d_members_do_not_support_new_groups, nonGv2CapableMembers.size(), nonGv2CapableMembers.size()));
gv2Warning.setLearnMoreVisible(true);
gv2Warning.setOnLinkClickListener(v -> NonGv2MemberDialog.showNonGv2Members(requireContext(), nonGv2CapableMembers));
});
viewModel.getAvatar().observe(getViewLifecycleOwner(), avatarBytes -> {
if (avatarBytes == null) {
avatar.setImageDrawable(new InsetDrawable(avatarPlaceholder, ViewUtil.dpToPx(AVATAR_PLACEHOLDER_INSET_DP)));

View File

@ -4,6 +4,7 @@ import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import androidx.core.util.Consumer;
import com.annimon.stream.Stream;
@ -11,7 +12,9 @@ import com.annimon.stream.Stream;
import org.thoughtcrime.securesms.groups.GroupChangeBusyException;
import org.thoughtcrime.securesms.groups.GroupChangeException;
import org.thoughtcrime.securesms.groups.GroupManager;
import org.thoughtcrime.securesms.groups.GroupsV2CapabilityChecker;
import org.thoughtcrime.securesms.groups.ui.GroupMemberEntry;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
@ -25,6 +28,8 @@ import java.util.Set;
final class AddGroupDetailsRepository {
private static final String TAG = Log.tag(AddGroupDetailsRepository.class);
private final Context context;
AddGroupDetailsRepository(@NonNull Context context) {
@ -65,4 +70,17 @@ final class AddGroupDetailsRepository {
}
});
}
@WorkerThread
List<Recipient> checkCapabilities(@NonNull Collection<RecipientId> newPotentialMemberList) {
try {
GroupsV2CapabilityChecker.refreshCapabilitiesIfNecessary(Recipient.resolvedList(newPotentialMemberList));
} catch (IOException e) {
Log.w(TAG, "Could not get latest profiles for users, using known gv2 capability state", e);
}
return Stream.of(Recipient.resolvedList(newPotentialMemberList))
.filter(m -> m.getGroupsV2Capability() != Recipient.Capability.SUPPORTED)
.toList();
}
}

View File

@ -13,12 +13,16 @@ import androidx.lifecycle.ViewModelProvider;
import com.annimon.stream.Collectors;
import com.annimon.stream.Stream;
import org.thoughtcrime.securesms.groups.GroupsV2CapabilityChecker;
import org.thoughtcrime.securesms.groups.ui.GroupMemberEntry;
import org.thoughtcrime.securesms.logging.Log;
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.livedata.LiveDataUtil;
import java.io.IOException;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
@ -35,6 +39,7 @@ public final class AddGroupDetailsViewModel extends ViewModel {
private final LiveData<Boolean> isMms;
private final LiveData<Boolean> canSubmitForm;
private final AddGroupDetailsRepository repository;
private final LiveData<List<Recipient>> nonGv2CapableMembers;
private AddGroupDetailsViewModel(@NonNull Collection<RecipientId> recipientIds,
@NonNull AddGroupDetailsRepository repository)
@ -44,9 +49,11 @@ public final class AddGroupDetailsViewModel extends ViewModel {
MutableLiveData<List<GroupMemberEntry.NewGroupCandidate>> initialMembers = new MutableLiveData<>();
LiveData<Boolean> isValidName = Transformations.map(name, name -> !TextUtils.isEmpty(name));
members = LiveDataUtil.combineLatest(initialMembers, deleted, AddGroupDetailsViewModel::filterDeletedMembers);
isMms = Transformations.map(members, AddGroupDetailsViewModel::isAnyForcedSms);
canSubmitForm = LiveDataUtil.combineLatest(isMms, isValidName, (mms, validName) -> mms || validName);
members = LiveDataUtil.combineLatest(initialMembers, deleted, AddGroupDetailsViewModel::filterDeletedMembers);
nonGv2CapableMembers = LiveDataUtil.mapAsync(members, memberList -> repository.checkCapabilities(Stream.of(memberList).map(newGroupCandidate -> newGroupCandidate.getMember().getId()).toList()));
isMms = Transformations.map(members, AddGroupDetailsViewModel::isAnyForcedSms);
canSubmitForm = LiveDataUtil.combineLatest(isMms, isValidName, (mms, validName) -> mms || validName);
repository.resolveMembers(recipientIds, initialMembers::postValue);
}
@ -71,6 +78,10 @@ public final class AddGroupDetailsViewModel extends ViewModel {
return isMms;
}
@NonNull LiveData<List<Recipient>> getNonGv2CapableMembers() {
return nonGv2CapableMembers;
}
void setAvatar(@Nullable byte[] avatar) {
this.avatar.setValue(avatar);
}

View File

@ -0,0 +1,56 @@
package org.thoughtcrime.securesms.groups.ui.creategroup.dialogs;
import android.app.Dialog;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.groups.ui.GroupMemberEntry;
import org.thoughtcrime.securesms.groups.ui.GroupMemberListView;
import org.thoughtcrime.securesms.recipients.Recipient;
import java.util.ArrayList;
import java.util.List;
public final class NonGv2MemberDialog {
private NonGv2MemberDialog() {
}
public static @Nullable Dialog showNonGv2Members(@NonNull Context context, @NonNull List<Recipient> recipients) {
int size = recipients.size();
if (size == 0) {
return null;
}
AlertDialog.Builder builder = new AlertDialog.Builder(context)
// TODO: GV2 Need a URL for learn more
// .setNegativeButton(R.string.NonGv2MemberDialog_learn_more, (dialog, which) -> {
// })
.setPositiveButton(android.R.string.ok, null);
if (size == 1) {
builder.setMessage(context.getString(R.string.NonGv2MemberDialog_single_users_are_non_gv2_capable, recipients.get(0).getDisplayName(context)));
} else {
builder.setMessage(context.getResources().getQuantityString(R.plurals.NonGv2MemberDialog_d_users_are_non_gv2_capable, size, size))
.setView(R.layout.dialog_multiple_members_non_gv2_capable);
}
Dialog dialog = builder.show();
if (size > 1) {
GroupMemberListView nonGv2CapableMembers = dialog.findViewById(R.id.list_non_gv2_members);
List<GroupMemberEntry.NewGroupCandidate> pendingMembers = new ArrayList<>(recipients.size());
for (Recipient r : recipients) {
pendingMembers.add(new GroupMemberEntry.NewGroupCandidate(r));
}
//noinspection ConstantConditions
nonGv2CapableMembers.setMembers(pendingMembers);
}
return dialog;
}
}

View File

@ -54,10 +54,28 @@
android:textAppearance="@style/TextAppearance.Signal.Body2"
android:textColor="@color/white"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@id/member_list_header"
app:layout_constraintBottom_toTopOf="@id/gv2_warning"
app:layout_constraintTop_toBottomOf="@id/group_avatar"
tools:visibility="visible" />
<org.thoughtcrime.securesms.util.views.LearnMoreTextView
android:id="@+id/gv2_warning"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:background="?secondary_background"
android:paddingStart="16dp"
android:paddingTop="8dp"
android:paddingEnd="16dp"
android:paddingBottom="8dp"
android:textAppearance="@style/TextAppearance.Signal.Body2"
android:textColor="?title_text_color_secondary"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@id/member_list_header"
app:layout_constraintTop_toBottomOf="@id/mms_warning"
tools:text="8 members do not support New Groups, so this group will be a Legacy Group. Learn more"
tools:visibility="visible" />
<TextView
android:id="@+id/member_list_header"
android:layout_width="match_parent"
@ -68,7 +86,7 @@
android:textAlignment="viewStart"
android:textAppearance="@style/TextAppearance.Signal.Subtitle2"
android:textColor="?attr/title_text_color_secondary"
app:layout_constraintTop_toBottomOf="@id/mms_warning"
app:layout_constraintTop_toBottomOf="@id/gv2_warning"
app:layout_goneMarginTop="30dp" />
<org.thoughtcrime.securesms.groups.ui.GroupMemberListView

View File

@ -0,0 +1,20 @@
<?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"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<org.thoughtcrime.securesms.groups.ui.GroupMemberListView
android:id="@+id/list_non_gv2_members"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="16dp"
android:scrollIndicators="top|bottom"
android:scrollbars="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:maxHeight="180dp" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -525,6 +525,17 @@
<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>
<plurals name="AddGroupDetailsFragment__d_members_do_not_support_new_groups">
<item quantity="one">%d member does not support New Groups, so this will be a Legacy Group.</item>
<item quantity="other">%d members do not support New Groups, so this group will be a Legacy Group.</item>
</plurals>
<!-- NonGv2MemberDialog -->
<string name="NonGv2MemberDialog_single_users_are_non_gv2_capable">A Legacy Group will be created because “%1$s” is using an old version of Signal. You can create a New Style Group with them after they update Signal, or remove them before creating the group.</string>
<plurals name="NonGv2MemberDialog_d_users_are_non_gv2_capable">
<item quantity="one">A Legacy Group will be created because %1$d member is using an old version of Signal. You can create a New Style Group with them after they update Signal, or remove them before creating the group.</item>
<item quantity="other">A Legacy Group will be created because %1$d members are using an old version of Signal. You can create a New Style Group with them after they update Signal, or remove them before creating the group.</item>
</plurals>
<!-- ManageGroupActivity -->
<string name="ManageGroupActivity_disappearing_messages">Disappearing messages</string>