From b4b1e5b60538f8d5727343a892f050b3d1bbf3d9 Mon Sep 17 00:00:00 2001 From: Alan Evans Date: Thu, 22 Oct 2020 14:52:05 -0300 Subject: [PATCH] Add feature flag driven group recommended size and hard size limits. --- .../ContactSelectionListFragment.java | 78 +++++++++++++------ .../components/emoji/WarningTextView.java | 49 ++++++++++++ .../securesms/groups/SelectionLimits.java | 65 ++++++++++++++++ .../securesms/groups/ui/GroupLimitDialog.java | 28 +++++++ .../ui/addtogroup/AddToGroupsActivity.java | 1 - .../ui/creategroup/CreateGroupActivity.java | 3 +- .../ui/managegroup/ManageGroupRepository.java | 39 ++++++---- .../ui/managegroup/ManageGroupViewModel.java | 6 +- .../securesms/util/FeatureFlags.java | 12 ++- .../contact_selection_list_fragment.xml | 5 +- app/src/main/res/values/attrs.xml | 4 + app/src/main/res/values/strings.xml | 10 ++- 12 files changed, 246 insertions(+), 54 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/components/emoji/WarningTextView.java create mode 100644 app/src/main/java/org/thoughtcrime/securesms/groups/SelectionLimits.java create mode 100644 app/src/main/java/org/thoughtcrime/securesms/groups/ui/GroupLimitDialog.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/ContactSelectionListFragment.java b/app/src/main/java/org/thoughtcrime/securesms/ContactSelectionListFragment.java index 2d2ec85990..325030f703 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ContactSelectionListFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/ContactSelectionListFragment.java @@ -21,6 +21,7 @@ import android.Manifest; import android.animation.LayoutTransition; import android.annotation.SuppressLint; import android.content.Context; +import android.content.Intent; import android.database.Cursor; import android.os.AsyncTask; import android.os.Bundle; @@ -29,7 +30,6 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; -import android.view.animation.CycleInterpolator; import android.widget.Button; import android.widget.HorizontalScrollView; import android.widget.TextView; @@ -56,6 +56,7 @@ import com.google.android.material.chip.ChipGroup; import com.pnikosis.materialishprogress.ProgressWheel; import org.thoughtcrime.securesms.components.RecyclerViewFastScroller; +import org.thoughtcrime.securesms.components.emoji.WarningTextView; import org.thoughtcrime.securesms.contacts.ContactChip; import org.thoughtcrime.securesms.contacts.ContactSelectionListAdapter; import org.thoughtcrime.securesms.contacts.ContactSelectionListItem; @@ -63,6 +64,8 @@ import org.thoughtcrime.securesms.contacts.ContactsCursorLoader; import org.thoughtcrime.securesms.contacts.ContactsCursorLoader.DisplayMode; import org.thoughtcrime.securesms.contacts.SelectedContact; import org.thoughtcrime.securesms.contacts.sync.DirectoryHelper; +import org.thoughtcrime.securesms.groups.SelectionLimits; +import org.thoughtcrime.securesms.groups.ui.GroupLimitDialog; import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.mms.GlideApp; import org.thoughtcrime.securesms.mms.GlideRequests; @@ -82,7 +85,6 @@ import org.whispersystems.libsignal.util.guava.Optional; import java.io.IOException; import java.util.Collections; import java.util.List; -import java.util.Locale; import java.util.Set; /** @@ -106,7 +108,7 @@ public final class ContactSelectionListFragment extends LoggingFragment public static final String MULTI_SELECT = "multi_select"; public static final String REFRESHABLE = "refreshable"; public static final String RECENTS = "recents"; - public static final String SELECTION_LIMIT = "selection_limit"; + public static final String SELECTION_LIMITS = "selection_limits"; public static final String CURRENT_SELECTION = "current_selection"; private ConstraintLayout constraintLayout; @@ -123,15 +125,16 @@ public final class ContactSelectionListFragment extends LoggingFragment private ContactSelectionListAdapter cursorRecyclerViewAdapter; private ChipGroup chipGroup; private HorizontalScrollView chipGroupScrollContainer; - private TextView groupLimit; + private WarningTextView groupLimit; @Nullable private FixedViewsAdapter headerAdapter; @Nullable private FixedViewsAdapter footerAdapter; @Nullable private ListCallback listCallback; @Nullable private ScrollCallback scrollCallback; private GlideRequests glideRequests; - private int selectionLimit; + private SelectionLimits selectionLimit = SelectionLimits.NO_LIMITS; private Set currentSelection; + private boolean isMulti; @Override public void onAttach(@NonNull Context context) { @@ -206,9 +209,25 @@ public final class ContactSelectionListFragment extends LoggingFragment } }); - swipeRefresh.setEnabled(requireActivity().getIntent().getBooleanExtra(REFRESHABLE, true)); + Intent intent = requireActivity().getIntent(); + + swipeRefresh.setEnabled(intent.getBooleanExtra(REFRESHABLE, true)); + + selectionLimit = intent.getParcelableExtra(SELECTION_LIMITS); + isMulti = intent.getBooleanExtra(MULTI_SELECT, false); + + if (isMulti) { + if (selectionLimit == null) { + throw new AssertionError("Selection limits not supplied in args for multi-select"); + } + } else { + if (selectionLimit != null) { + throw new AssertionError("Selection limits supplied in args for a non-multi selection use"); + } else { + selectionLimit = SelectionLimits.NO_LIMITS; + } + } - selectionLimit = requireActivity().getIntent().getIntExtra(SELECTION_LIMIT, NO_LIMIT); currentSelection = getCurrentSelection(); updateGroupLimit(getChipCount()); @@ -217,12 +236,10 @@ public final class ContactSelectionListFragment extends LoggingFragment } private void updateGroupLimit(int chipCount) { - if (selectionLimit != NO_LIMIT) { - groupLimit.setText(String.format(Locale.getDefault(), "%d/%d", currentSelection.size() + chipCount, selectionLimit)); - groupLimit.setVisibility(View.VISIBLE); - } else { - groupLimit.setVisibility(View.GONE); - } + int members = currentSelection.size() + chipCount; + groupLimit.setText(getResources().getQuantityString(R.plurals.ContactSelectionListFragment_d_members, members, members)); + groupLimit.setVisibility(isMulti ? View.VISIBLE : View.GONE); + groupLimit.setWarning(selectionWarningLimitExceeded()); } @Override @@ -254,7 +271,7 @@ public final class ContactSelectionListFragment extends LoggingFragment } public boolean isMulti() { - return requireActivity().getIntent().getBooleanExtra(MULTI_SELECT, false); + return isMulti; } private void initializeCursor() { @@ -264,7 +281,7 @@ public final class ContactSelectionListFragment extends LoggingFragment glideRequests, null, new ListClickListener(), - isMulti(), + isMulti, currentSelection); RecyclerViewConcatenateAdapterStickyHeader concatenateAdapter = new RecyclerViewConcatenateAdapterStickyHeader(); @@ -450,15 +467,14 @@ public final class ContactSelectionListFragment extends LoggingFragment SelectedContact selectedContact = contact.isUsernameType() ? SelectedContact.forUsername(contact.getRecipientId().orNull(), contact.getNumber()) : SelectedContact.forPhone(contact.getRecipientId().orNull(), contact.getNumber()); - if (isMulti() && Recipient.self().getId().equals(selectedContact.getOrCreateRecipientId(requireContext()))) { + if (isMulti && Recipient.self().getId().equals(selectedContact.getOrCreateRecipientId(requireContext()))) { Toast.makeText(requireContext(), R.string.ContactSelectionListFragment_you_do_not_need_to_add_yourself_to_the_group, Toast.LENGTH_SHORT).show(); return; } - if (!isMulti() || !cursorRecyclerViewAdapter.isSelectedContact(selectedContact)) { - if (selectionLimitReached()) { - Toast.makeText(requireContext(), R.string.ContactSelectionListFragment_the_group_is_full, Toast.LENGTH_SHORT).show(); - groupLimit.animate().scaleX(1.3f).scaleY(1.3f).setInterpolator(new CycleInterpolator(0.5f)).start(); + if (!isMulti || !cursorRecyclerViewAdapter.isSelectedContact(selectedContact)) { + if (selectionHardLimitReached()) { + GroupLimitDialog.showHardLimitMessage(requireContext()); return; } @@ -486,7 +502,7 @@ public final class ContactSelectionListFragment extends LoggingFragment new AlertDialog.Builder(requireContext()) .setTitle(R.string.ContactSelectionListFragment_username_not_found) .setMessage(getString(R.string.ContactSelectionListFragment_s_is_not_a_signal_user, contact.getNumber())) - .setPositiveButton(R.string.ContactSelectionListFragment_okay, (dialog, which) -> dialog.dismiss()) + .setPositiveButton(android.R.string.ok, (dialog, which) -> dialog.dismiss()) .show(); } }); @@ -508,16 +524,25 @@ public final class ContactSelectionListFragment extends LoggingFragment if (onContactSelectedListener != null) { onContactSelectedListener.onContactDeselected(contact.getRecipientId(), contact.getNumber()); } - }} + } + } } - private boolean selectionLimitReached() { - return getChipCount() + currentSelection.size() >= selectionLimit; + private boolean selectionHardLimitReached() { + return getChipCount() + currentSelection.size() >= selectionLimit.getHardLimit(); + } + + private boolean selectionWarningLimitReachedExactly() { + return getChipCount() + currentSelection.size() == selectionLimit.getRecommendedLimit(); + } + + private boolean selectionWarningLimitExceeded() { + return getChipCount() + currentSelection.size() > selectionLimit.getRecommendedLimit(); } private void markContactSelected(@NonNull SelectedContact selectedContact) { cursorRecyclerViewAdapter.addSelectedContact(selectedContact); - if (isMulti()) { + if (isMulti) { addChipForSelectedContact(selectedContact); } } @@ -588,6 +613,9 @@ public final class ContactSelectionListFragment extends LoggingFragment private void addChip(@NonNull ContactChip chip) { chipGroup.addView(chip); updateGroupLimit(getChipCount()); + if (selectionWarningLimitReachedExactly()) { + GroupLimitDialog.showRecommendedLimitMessage(requireContext()); + } } private int getChipCount() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/emoji/WarningTextView.java b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/WarningTextView.java new file mode 100644 index 0000000000..d74e7bafa7 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/WarningTextView.java @@ -0,0 +1,49 @@ +package org.thoughtcrime.securesms.components.emoji; + +import android.content.Context; +import android.content.res.TypedArray; +import android.util.AttributeSet; + +import androidx.annotation.ColorInt; +import androidx.appcompat.widget.AppCompatTextView; + +import org.thoughtcrime.securesms.R; + +public final class WarningTextView extends AppCompatTextView { + + @ColorInt private final int originalTextColor; + @ColorInt private final int warningTextColor; + + private boolean warning; + + public WarningTextView(Context context) { + this(context, null); + } + + public WarningTextView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public WarningTextView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + + TypedArray styledAttributes = context.obtainStyledAttributes(attrs, R.styleable.WarningTextView, 0, 0); + warningTextColor = styledAttributes.getColor(R.styleable.WarningTextView_warning_text_color, 0); + + styledAttributes.recycle(); + + styledAttributes = context.obtainStyledAttributes(attrs, new int[]{ android.R.attr.textColor }); + + originalTextColor = styledAttributes.getColor(0, 0); + + styledAttributes.recycle(); + } + + public void setWarning(boolean warning) { + if (this.warning != warning) { + this.warning = warning; + setTextColor(warning ? warningTextColor : originalTextColor); + invalidate(); + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/SelectionLimits.java b/app/src/main/java/org/thoughtcrime/securesms/groups/SelectionLimits.java new file mode 100644 index 0000000000..c073bde1d5 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/SelectionLimits.java @@ -0,0 +1,65 @@ +package org.thoughtcrime.securesms.groups; + +import android.os.Parcel; +import android.os.Parcelable; + +public final class SelectionLimits implements Parcelable { + public static final int NO_LIMIT = Integer.MAX_VALUE; + + public static final SelectionLimits NO_LIMITS = new SelectionLimits(NO_LIMIT, NO_LIMIT); + + private final int recommendedLimit; + private final int hardLimit; + + public SelectionLimits(int recommendedLimit, int hardLimit) { + this.recommendedLimit = recommendedLimit; + this.hardLimit = hardLimit; + } + + public int getRecommendedLimit() { + return recommendedLimit; + } + + public int getHardLimit() { + return hardLimit; + } + + public boolean hasRecommendedLimit() { + return recommendedLimit != NO_LIMIT; + } + + public boolean hasHardLimit() { + return hardLimit != NO_LIMIT; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(recommendedLimit); + dest.writeInt(hardLimit); + } + + public static final Creator CREATOR = new Creator() { + @Override + public SelectionLimits createFromParcel(Parcel in) { + return new SelectionLimits(in.readInt(), in.readInt()); + } + + @Override + public SelectionLimits[] newArray(int size) { + return new SelectionLimits[size]; + } + }; + + public SelectionLimits excludingSelf() { + return excluding(1); + } + + public SelectionLimits excluding(int count) { + return new SelectionLimits(recommendedLimit - count, hardLimit - count); + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/GroupLimitDialog.java b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/GroupLimitDialog.java new file mode 100644 index 0000000000..35f06b5893 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/GroupLimitDialog.java @@ -0,0 +1,28 @@ +package org.thoughtcrime.securesms.groups.ui; + +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.appcompat.app.AlertDialog; + +import org.thoughtcrime.securesms.R; +import org.thoughtcrime.securesms.util.FeatureFlags; + +public final class GroupLimitDialog { + + public static void showHardLimitMessage(@NonNull Context context) { + new AlertDialog.Builder(context) + .setTitle(R.string.ContactSelectionListFragment_maximum_group_size_reached) + .setMessage(context.getString(R.string.ContactSelectionListFragment_signal_groups_can_have_a_maximum_of_d_members, FeatureFlags.groupLimits().getHardLimit())) + .setPositiveButton(android.R.string.ok, null) + .show(); + } + + public static void showRecommendedLimitMessage(@NonNull Context context) { + new AlertDialog.Builder(context) + .setTitle(R.string.ContactSelectionListFragment_recommended_member_limit_reached) + .setMessage(context.getString(R.string.ContactSelectionListFragment_signal_groups_perform_best_with_d_members_or_fewer, FeatureFlags.groupLimits().getRecommendedLimit())) + .setPositiveButton(android.R.string.ok, null) + .show(); + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/addtogroup/AddToGroupsActivity.java b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/addtogroup/AddToGroupsActivity.java index 61bcad5bec..c674f26606 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/addtogroup/AddToGroupsActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/addtogroup/AddToGroupsActivity.java @@ -51,7 +51,6 @@ public final class AddToGroupsActivity extends ContactSelectionActivity { intent.putExtra(EXTRA_RECIPIENT_ID, recipientId); intent.putExtra(ContactSelectionListFragment.DISPLAY_MODE, ContactsCursorLoader.DisplayMode.FLAG_ACTIVE_GROUPS); - intent.putExtra(ContactSelectionListFragment.SELECTION_LIMIT, ContactSelectionListFragment.NO_LIMIT); intent.putParcelableArrayListExtra(ContactSelectionListFragment.CURRENT_SELECTION, new ArrayList<>(currentGroupsMemberOf)); diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/creategroup/CreateGroupActivity.java b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/creategroup/CreateGroupActivity.java index ad0c58a6f6..f4df66568d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/creategroup/CreateGroupActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/creategroup/CreateGroupActivity.java @@ -56,8 +56,7 @@ public class CreateGroupActivity extends ContactSelectionActivity { : ContactsCursorLoader.DisplayMode.FLAG_PUSH; intent.putExtra(ContactSelectionListFragment.DISPLAY_MODE, displayMode); - intent.putExtra(ContactSelectionListFragment.SELECTION_LIMIT, SignalStore.internalValues().gv2DoNotCreateGv2Groups() ? ContactSelectionListFragment.NO_LIMIT - : FeatureFlags.gv2GroupCapacity() - 1); + intent.putExtra(ContactSelectionListFragment.SELECTION_LIMITS, FeatureFlags.groupLimits().excludingSelf()); return intent; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/managegroup/ManageGroupRepository.java b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/managegroup/ManageGroupRepository.java index 6a9eea53a4..b453e2cccf 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/managegroup/ManageGroupRepository.java +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/managegroup/ManageGroupRepository.java @@ -22,6 +22,7 @@ import org.thoughtcrime.securesms.groups.GroupProtoUtil; import org.thoughtcrime.securesms.groups.MembershipNotSuitableForV2Exception; import org.thoughtcrime.securesms.groups.ui.GroupChangeErrorCallback; import org.thoughtcrime.securesms.groups.ui.GroupChangeFailureReason; +import org.thoughtcrime.securesms.groups.SelectionLimits; import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientId; @@ -44,8 +45,8 @@ final class ManageGroupRepository { private final GroupId groupId; ManageGroupRepository(@NonNull Context context, @NonNull GroupId groupId) { - this.context = context; - this.groupId = groupId; + this.context = context; + this.groupId = groupId; } public GroupId getGroupId() { @@ -68,9 +69,9 @@ final class ManageGroupRepository { members.addAll(pendingMembers); - return new GroupCapacityResult(members, FeatureFlags.gv2GroupCapacity()); + return new GroupCapacityResult(members, FeatureFlags.groupLimits()); } else { - return new GroupCapacityResult(groupRecord.getMembers(), ContactSelectionListFragment.NO_LIMIT); + return new GroupCapacityResult(groupRecord.getMembers(), FeatureFlags.groupLimits()); } }, onGroupCapacityLoaded::accept); } @@ -186,33 +187,39 @@ final class ManageGroupRepository { static final class GroupCapacityResult { private final List members; - private final int totalCapacity; + private final SelectionLimits selectionLimits; - GroupCapacityResult(@NonNull List members, int totalCapacity) { - this.members = members; - this.totalCapacity = totalCapacity; + GroupCapacityResult(@NonNull List members, @NonNull SelectionLimits selectionLimits) { + this.members = members; + this.selectionLimits = selectionLimits; } public @NonNull List getMembers() { return members; } - public int getTotalCapacity() { - return totalCapacity; - } - public int getSelectionLimit() { - if (totalCapacity == ContactSelectionListFragment.NO_LIMIT) { - return totalCapacity; + if (!selectionLimits.hasHardLimit()) { + return ContactSelectionListFragment.NO_LIMIT; } boolean containsSelf = members.indexOf(Recipient.self().getId()) != -1; - return totalCapacity - (containsSelf ? 1 : 0); + return selectionLimits.getHardLimit() - (containsSelf ? 1 : 0); + } + + public int getSelectionWarning() { + if (!selectionLimits.hasRecommendedLimit()) { + return ContactSelectionListFragment.NO_LIMIT; + } + + boolean containsSelf = members.indexOf(Recipient.self().getId()) != -1; + + return selectionLimits.getRecommendedLimit() - (containsSelf ? 1 : 0); } public int getRemainingCapacity() { - return totalCapacity - members.size(); + return selectionLimits.getHardLimit() - members.size(); } public @NonNull ArrayList getMembersWithoutSelf() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/managegroup/ManageGroupViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/managegroup/ManageGroupViewModel.java index 8ef5f70dc6..e040549c14 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/managegroup/ManageGroupViewModel.java +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/managegroup/ManageGroupViewModel.java @@ -27,9 +27,11 @@ import org.thoughtcrime.securesms.database.loaders.MediaLoader; import org.thoughtcrime.securesms.database.loaders.ThreadMediaLoader; import org.thoughtcrime.securesms.groups.GroupAccessControl; import org.thoughtcrime.securesms.groups.GroupId; +import org.thoughtcrime.securesms.groups.SelectionLimits; import org.thoughtcrime.securesms.groups.LiveGroup; import org.thoughtcrime.securesms.groups.ui.GroupChangeFailureReason; import org.thoughtcrime.securesms.groups.ui.GroupErrors; +import org.thoughtcrime.securesms.groups.ui.GroupLimitDialog; import org.thoughtcrime.securesms.groups.ui.GroupMemberEntry; import org.thoughtcrime.securesms.groups.ui.addmembers.AddMembersActivity; import org.thoughtcrime.securesms.groups.ui.managegroup.dialogs.GroupMentionSettingDialog; @@ -306,12 +308,12 @@ public class ManageGroupViewModel extends ViewModel { manageGroupRepository.getGroupCapacity(capacity -> { int remainingCapacity = capacity.getRemainingCapacity(); if (remainingCapacity <= 0) { - Toast.makeText(fragment.requireContext(), R.string.ContactSelectionListFragment_the_group_is_full, Toast.LENGTH_SHORT).show(); + GroupLimitDialog.showHardLimitMessage(fragment.requireContext()); } else { Intent intent = new Intent(fragment.requireActivity(), AddMembersActivity.class); intent.putExtra(AddMembersActivity.GROUP_ID, manageGroupRepository.getGroupId().toString()); intent.putExtra(ContactSelectionListFragment.DISPLAY_MODE, ContactsCursorLoader.DisplayMode.FLAG_PUSH); - intent.putExtra(ContactSelectionListFragment.SELECTION_LIMIT, capacity.getSelectionLimit()); + intent.putExtra(ContactSelectionListFragment.SELECTION_LIMITS, new SelectionLimits(capacity.getSelectionWarning(), capacity.getSelectionLimit())); intent.putParcelableArrayListExtra(ContactSelectionListFragment.CURRENT_SELECTION, capacity.getMembersWithoutSelf()); fragment.startActivityForResult(intent, resultCode); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java b/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java index 37767ca8fd..947e1d328e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java @@ -12,6 +12,7 @@ import org.json.JSONException; import org.json.JSONObject; import org.thoughtcrime.securesms.BuildConfig; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; +import org.thoughtcrime.securesms.groups.SelectionLimits; import org.thoughtcrime.securesms.jobs.RemoteConfigRefreshJob; import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.logging.Log; @@ -50,7 +51,8 @@ public final class FeatureFlags { private static final String USERNAMES = "android.usernames"; private static final String GROUPS_V2_JOIN_VERSION = "android.groupsv2.joinVersion"; private static final String GROUPS_V2_LINKS_VERSION = "android.groupsv2.manageGroupLinksVersion"; - private static final String GROUPS_V2_CAPACITY = "global.groupsv2.maxGroupSize"; + private static final String GROUPS_V2_RECOMMENDED_LIMIT = "global.groupsv2.maxGroupSize"; + private static final String GROUPS_V2_HARD_LIMIT = "global.groupsv2.groupSizeHardLimit"; private static final String INTERNAL_USER = "android.internalUser"; private static final String VERIFY_V2 = "android.verifyV2"; private static final String PHONE_NUMBER_PRIVACY_VERSION = "android.phoneNumberPrivacyVersion"; @@ -64,7 +66,8 @@ public final class FeatureFlags { */ private static final Set REMOTE_CAPABLE = Sets.newHashSet( - GROUPS_V2_CAPACITY, + GROUPS_V2_RECOMMENDED_LIMIT, + GROUPS_V2_HARD_LIMIT, GROUPS_V2_JOIN_VERSION, GROUPS_V2_LINKS_VERSION, INTERNAL_USER, @@ -177,8 +180,9 @@ public final class FeatureFlags { /** * Maximum number of members allowed in a group. */ - public static int gv2GroupCapacity() { - return getInteger(GROUPS_V2_CAPACITY, 151); + public static SelectionLimits groupLimits() { + return new SelectionLimits(getInteger(GROUPS_V2_RECOMMENDED_LIMIT, 151), + getInteger(GROUPS_V2_HARD_LIMIT, 1001)); } /** diff --git a/app/src/main/res/layout/contact_selection_list_fragment.xml b/app/src/main/res/layout/contact_selection_list_fragment.xml index 2aae3b3310..84b178837a 100644 --- a/app/src/main/res/layout/contact_selection_list_fragment.xml +++ b/app/src/main/res/layout/contact_selection_list_fragment.xml @@ -110,14 +110,15 @@ - diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index 528bdee171..ba2e02b636 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -588,4 +588,8 @@ + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 953b92c1b1..31bca907db 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1711,9 +1711,15 @@ Error retrieving contacts, check your network connection Username not found "%1$s" is not a Signal user. Please check the username and try again. - Okay - The group is full You do not need to add yourself to the group + Maximum group size reached + Signal groups can have a maximum of %1$d members. + Recommended member limit reached + Signal groups perform best with %1$d members or fewer. Adding more members will cause delays sending and receiving messages. + + %1$d member + %1$d members + No blocked contacts