mirror of
https://github.com/oxen-io/session-android.git
synced 2025-08-26 12:47:35 +00:00
Enforce a local GV2 capacity limit driven by a feature flag.
This commit is contained in:

committed by
Greyson Parrelli

parent
cfcd451db7
commit
9da309ca48
@@ -29,6 +29,7 @@ import android.view.LayoutInflater;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
|
import android.view.animation.CycleInterpolator;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.HorizontalScrollView;
|
import android.widget.HorizontalScrollView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
@@ -67,7 +68,6 @@ import org.thoughtcrime.securesms.permissions.Permissions;
|
|||||||
import org.thoughtcrime.securesms.recipients.LiveRecipient;
|
import org.thoughtcrime.securesms.recipients.LiveRecipient;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
import org.thoughtcrime.securesms.util.Debouncer;
|
|
||||||
import org.thoughtcrime.securesms.util.FeatureFlags;
|
import org.thoughtcrime.securesms.util.FeatureFlags;
|
||||||
import org.thoughtcrime.securesms.util.StickyHeaderDecoration;
|
import org.thoughtcrime.securesms.util.StickyHeaderDecoration;
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
@@ -81,6 +81,7 @@ import org.whispersystems.libsignal.util.guava.Optional;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fragment for selecting a one or more contacts from a list.
|
* Fragment for selecting a one or more contacts from a list.
|
||||||
@@ -94,13 +95,16 @@ public final class ContactSelectionListFragment extends Fragment
|
|||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
private static final String TAG = Log.tag(ContactSelectionListFragment.class);
|
private static final String TAG = Log.tag(ContactSelectionListFragment.class);
|
||||||
|
|
||||||
private static final int CHIP_GROUP_EMPTY_COUNT = 1;
|
private static final int CHIP_GROUP_EMPTY_CHILD_COUNT = 1;
|
||||||
private static final int CHIP_GROUP_REVEAL_DURATION_MS = 150;
|
private static final int CHIP_GROUP_REVEAL_DURATION_MS = 150;
|
||||||
|
|
||||||
public static final String DISPLAY_MODE = "display_mode";
|
public static final int NO_LIMIT = Integer.MAX_VALUE;
|
||||||
public static final String MULTI_SELECT = "multi_select";
|
|
||||||
public static final String REFRESHABLE = "refreshable";
|
public static final String DISPLAY_MODE = "display_mode";
|
||||||
public static final String RECENTS = "recents";
|
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";
|
||||||
|
|
||||||
private ConstraintLayout constraintLayout;
|
private ConstraintLayout constraintLayout;
|
||||||
private TextView emptyText;
|
private TextView emptyText;
|
||||||
@@ -116,12 +120,14 @@ public final class ContactSelectionListFragment extends Fragment
|
|||||||
private ContactSelectionListAdapter cursorRecyclerViewAdapter;
|
private ContactSelectionListAdapter cursorRecyclerViewAdapter;
|
||||||
private ChipGroup chipGroup;
|
private ChipGroup chipGroup;
|
||||||
private HorizontalScrollView chipGroupScrollContainer;
|
private HorizontalScrollView chipGroupScrollContainer;
|
||||||
|
private TextView groupLimit;
|
||||||
|
|
||||||
@Nullable private FixedViewsAdapter headerAdapter;
|
@Nullable private FixedViewsAdapter headerAdapter;
|
||||||
@Nullable private FixedViewsAdapter footerAdapter;
|
@Nullable private FixedViewsAdapter footerAdapter;
|
||||||
@Nullable private ListCallback listCallback;
|
@Nullable private ListCallback listCallback;
|
||||||
@Nullable private ScrollCallback scrollCallback;
|
@Nullable private ScrollCallback scrollCallback;
|
||||||
private GlideRequests glideRequests;
|
private GlideRequests glideRequests;
|
||||||
|
private int selectionLimit;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAttach(@NonNull Context context) {
|
public void onAttach(@NonNull Context context) {
|
||||||
@@ -185,15 +191,29 @@ public final class ContactSelectionListFragment extends Fragment
|
|||||||
showContactsProgress = view.findViewById(R.id.progress);
|
showContactsProgress = view.findViewById(R.id.progress);
|
||||||
chipGroup = view.findViewById(R.id.chipGroup);
|
chipGroup = view.findViewById(R.id.chipGroup);
|
||||||
chipGroupScrollContainer = view.findViewById(R.id.chipGroupScrollContainer);
|
chipGroupScrollContainer = view.findViewById(R.id.chipGroupScrollContainer);
|
||||||
|
groupLimit = view.findViewById(R.id.group_limit);
|
||||||
constraintLayout = view.findViewById(R.id.container);
|
constraintLayout = view.findViewById(R.id.container);
|
||||||
|
|
||||||
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
|
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
|
||||||
|
|
||||||
swipeRefresh.setEnabled(requireActivity().getIntent().getBooleanExtra(REFRESHABLE, true));
|
swipeRefresh.setEnabled(requireActivity().getIntent().getBooleanExtra(REFRESHABLE, true));
|
||||||
|
|
||||||
|
selectionLimit = requireActivity().getIntent().getIntExtra(SELECTION_LIMIT, NO_LIMIT);
|
||||||
|
|
||||||
|
updateGroupLimit(getChipCount());
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateGroupLimit(int childCount) {
|
||||||
|
if (selectionLimit != NO_LIMIT) {
|
||||||
|
groupLimit.setText(String.format(Locale.getDefault(), "%d/%d", childCount, selectionLimit));
|
||||||
|
groupLimit.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
groupLimit.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||||
Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults);
|
Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults);
|
||||||
@@ -408,6 +428,12 @@ public final class ContactSelectionListFragment extends Fragment
|
|||||||
: SelectedContact.forPhone(contact.getRecipientId().orNull(), contact.getNumber());
|
: SelectedContact.forPhone(contact.getRecipientId().orNull(), contact.getNumber());
|
||||||
|
|
||||||
if (!isMulti() || !cursorRecyclerViewAdapter.isSelectedContact(selectedContact)) {
|
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();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (contact.isUsernameType()) {
|
if (contact.isUsernameType()) {
|
||||||
AlertDialog loadingDialog = SimpleProgressDialog.show(requireContext());
|
AlertDialog loadingDialog = SimpleProgressDialog.show(requireContext());
|
||||||
|
|
||||||
@@ -447,6 +473,10 @@ public final class ContactSelectionListFragment extends Fragment
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean selectionLimitReached() {
|
||||||
|
return getChipCount() >= selectionLimit;
|
||||||
|
}
|
||||||
|
|
||||||
private void markContactSelected(@NonNull SelectedContact selectedContact, @NonNull ContactSelectionListItem listItem) {
|
private void markContactSelected(@NonNull SelectedContact selectedContact, @NonNull ContactSelectionListItem listItem) {
|
||||||
cursorRecyclerViewAdapter.addSelectedContact(selectedContact);
|
cursorRecyclerViewAdapter.addSelectedContact(selectedContact);
|
||||||
listItem.setChecked(true);
|
listItem.setChecked(true);
|
||||||
@@ -469,7 +499,9 @@ public final class ContactSelectionListFragment extends Fragment
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chipGroup.getChildCount() == CHIP_GROUP_EMPTY_COUNT) {
|
updateGroupLimit(getChipCount());
|
||||||
|
|
||||||
|
if (getChipCount() == 0) {
|
||||||
setChipGroupVisibility(ConstraintSet.GONE);
|
setChipGroupVisibility(ConstraintSet.GONE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -477,7 +509,7 @@ public final class ContactSelectionListFragment extends Fragment
|
|||||||
private void addChipForContact(@NonNull ContactSelectionListItem contact, @NonNull SelectedContact selectedContact) {
|
private void addChipForContact(@NonNull ContactSelectionListItem contact, @NonNull SelectedContact selectedContact) {
|
||||||
final ContactChip chip = new ContactChip(requireContext());
|
final ContactChip chip = new ContactChip(requireContext());
|
||||||
|
|
||||||
if (chipGroup.getChildCount() == CHIP_GROUP_EMPTY_COUNT) {
|
if (getChipCount() == 0) {
|
||||||
setChipGroupVisibility(ConstraintSet.VISIBLE);
|
setChipGroupVisibility(ConstraintSet.VISIBLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -503,12 +535,23 @@ public final class ContactSelectionListFragment extends Fragment
|
|||||||
|
|
||||||
LiveRecipient recipient = contact.getRecipient();
|
LiveRecipient recipient = contact.getRecipient();
|
||||||
if (recipient != null) {
|
if (recipient != null) {
|
||||||
chip.setAvatar(glideRequests, recipient.get(), () -> chipGroup.addView(chip));
|
chip.setAvatar(glideRequests, recipient.get(), () -> addChip(chip));
|
||||||
} else {
|
} else {
|
||||||
chipGroup.addView(chip);
|
addChip(chip);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addChip(@NonNull ContactChip chip) {
|
||||||
|
chipGroup.addView(chip);
|
||||||
|
updateGroupLimit(getChipCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getChipCount() {
|
||||||
|
int count = chipGroup.getChildCount() - CHIP_GROUP_EMPTY_CHILD_COUNT;
|
||||||
|
if (count < 0) throw new AssertionError();
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
private void registerChipRecipientObserver(@NonNull ContactChip chip, @Nullable LiveRecipient recipient) {
|
private void registerChipRecipientObserver(@NonNull ContactChip chip, @Nullable LiveRecipient recipient) {
|
||||||
if (recipient != null) {
|
if (recipient != null) {
|
||||||
recipient.observe(getViewLifecycleOwner(), resolved -> {
|
recipient.observe(getViewLifecycleOwner(), resolved -> {
|
||||||
|
@@ -44,6 +44,7 @@ public class CreateGroupActivity extends ContactSelectionActivity {
|
|||||||
: ContactsCursorLoader.DisplayMode.FLAG_PUSH;
|
: ContactsCursorLoader.DisplayMode.FLAG_PUSH;
|
||||||
|
|
||||||
intent.putExtra(ContactSelectionListFragment.DISPLAY_MODE, displayMode);
|
intent.putExtra(ContactSelectionListFragment.DISPLAY_MODE, displayMode);
|
||||||
|
intent.putExtra(ContactSelectionListFragment.SELECTION_LIMIT, FeatureFlags.gv2GroupCapacity() - 1);
|
||||||
|
|
||||||
return intent;
|
return intent;
|
||||||
}
|
}
|
||||||
|
@@ -23,14 +23,12 @@ import androidx.fragment.app.FragmentActivity;
|
|||||||
import androidx.lifecycle.ViewModelProviders;
|
import androidx.lifecycle.ViewModelProviders;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.AvatarPreviewActivity;
|
import org.thoughtcrime.securesms.AvatarPreviewActivity;
|
||||||
import org.thoughtcrime.securesms.ContactSelectionListFragment;
|
|
||||||
import org.thoughtcrime.securesms.MediaPreviewActivity;
|
import org.thoughtcrime.securesms.MediaPreviewActivity;
|
||||||
import org.thoughtcrime.securesms.MuteDialog;
|
import org.thoughtcrime.securesms.MuteDialog;
|
||||||
import org.thoughtcrime.securesms.PushContactSelectionActivity;
|
import org.thoughtcrime.securesms.PushContactSelectionActivity;
|
||||||
import org.thoughtcrime.securesms.R;
|
import org.thoughtcrime.securesms.R;
|
||||||
import org.thoughtcrime.securesms.components.AvatarImageView;
|
import org.thoughtcrime.securesms.components.AvatarImageView;
|
||||||
import org.thoughtcrime.securesms.components.ThreadPhotoRailView;
|
import org.thoughtcrime.securesms.components.ThreadPhotoRailView;
|
||||||
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader;
|
|
||||||
import org.thoughtcrime.securesms.contacts.avatars.FallbackContactPhoto;
|
import org.thoughtcrime.securesms.contacts.avatars.FallbackContactPhoto;
|
||||||
import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto;
|
import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto;
|
||||||
import org.thoughtcrime.securesms.groups.GroupId;
|
import org.thoughtcrime.securesms.groups.GroupId;
|
||||||
@@ -225,11 +223,7 @@ public class ManageGroupFragment extends Fragment {
|
|||||||
disappearingMessagesRow.setOnClickListener(v -> viewModel.handleExpirationSelection());
|
disappearingMessagesRow.setOnClickListener(v -> viewModel.handleExpirationSelection());
|
||||||
blockGroup.setOnClickListener(v -> viewModel.blockAndLeave(requireActivity()));
|
blockGroup.setOnClickListener(v -> viewModel.blockAndLeave(requireActivity()));
|
||||||
|
|
||||||
addMembers.setOnClickListener(v -> {
|
addMembers.setOnClickListener(v -> viewModel.onAddMembersClick(this, PICK_CONTACT));
|
||||||
Intent intent = new Intent(requireActivity(), PushContactSelectionActivity.class);
|
|
||||||
intent.putExtra(ContactSelectionListFragment.DISPLAY_MODE, ContactsCursorLoader.DisplayMode.FLAG_PUSH);
|
|
||||||
startActivityForResult(intent, PICK_CONTACT);
|
|
||||||
});
|
|
||||||
|
|
||||||
viewModel.getMembershipRights().observe(getViewLifecycleOwner(), r -> {
|
viewModel.getMembershipRights().observe(getViewLifecycleOwner(), r -> {
|
||||||
if (r != null) {
|
if (r != null) {
|
||||||
|
@@ -6,7 +6,10 @@ import androidx.annotation.NonNull;
|
|||||||
import androidx.annotation.WorkerThread;
|
import androidx.annotation.WorkerThread;
|
||||||
import androidx.core.util.Consumer;
|
import androidx.core.util.Consumer;
|
||||||
|
|
||||||
|
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
|
||||||
|
import org.thoughtcrime.securesms.ContactSelectionListFragment;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
|
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||||
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
||||||
import org.thoughtcrime.securesms.groups.GroupAccessControl;
|
import org.thoughtcrime.securesms.groups.GroupAccessControl;
|
||||||
import org.thoughtcrime.securesms.groups.GroupChangeBusyException;
|
import org.thoughtcrime.securesms.groups.GroupChangeBusyException;
|
||||||
@@ -21,6 +24,7 @@ import org.thoughtcrime.securesms.groups.ui.GroupChangeFailureReason;
|
|||||||
import org.thoughtcrime.securesms.logging.Log;
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
|
import org.thoughtcrime.securesms.util.FeatureFlags;
|
||||||
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
|
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
|
||||||
import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
|
import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
|
||||||
|
|
||||||
@@ -47,6 +51,18 @@ final class ManageGroupRepository {
|
|||||||
SignalExecutors.BOUNDED.execute(() -> onGroupStateLoaded.accept(getGroupState()));
|
SignalExecutors.BOUNDED.execute(() -> onGroupStateLoaded.accept(getGroupState()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void getGroupCapacity(@NonNull Consumer<GroupCapacityResult> onGroupCapacityLoaded) {
|
||||||
|
SimpleTask.run(SignalExecutors.BOUNDED, () -> {
|
||||||
|
GroupDatabase.GroupRecord groupRecord = DatabaseFactory.getGroupDatabase(context).getGroup(groupId).get();
|
||||||
|
if (groupRecord.isV2Group()) {
|
||||||
|
DecryptedGroup decryptedGroup = groupRecord.requireV2GroupProperties().getDecryptedGroup();
|
||||||
|
return new GroupCapacityResult(decryptedGroup.getMembersCount(), decryptedGroup.getPendingMembersCount(), FeatureFlags.gv2GroupCapacity());
|
||||||
|
} else {
|
||||||
|
return new GroupCapacityResult(groupRecord.getMembers().size(), 0, ContactSelectionListFragment.NO_LIMIT);
|
||||||
|
}
|
||||||
|
}, onGroupCapacityLoaded::accept);
|
||||||
|
}
|
||||||
|
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
private GroupStateResult getGroupState() {
|
private GroupStateResult getGroupState() {
|
||||||
ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context);
|
ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context);
|
||||||
@@ -152,4 +168,20 @@ final class ManageGroupRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static final class GroupCapacityResult {
|
||||||
|
private final int fullMembers;
|
||||||
|
private final int pendingMembers;
|
||||||
|
private final int totalCapacity;
|
||||||
|
|
||||||
|
GroupCapacityResult(int fullMembers, int pendingMembers, int totalCapacity) {
|
||||||
|
this.fullMembers = fullMembers;
|
||||||
|
this.pendingMembers = pendingMembers;
|
||||||
|
this.totalCapacity = totalCapacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRemainingCapacity() {
|
||||||
|
return totalCapacity - fullMembers - pendingMembers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,11 +1,13 @@
|
|||||||
package org.thoughtcrime.securesms.groups.ui.managegroup;
|
package org.thoughtcrime.securesms.groups.ui.managegroup;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.WorkerThread;
|
import androidx.annotation.WorkerThread;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.fragment.app.FragmentActivity;
|
import androidx.fragment.app.FragmentActivity;
|
||||||
import androidx.lifecycle.LiveData;
|
import androidx.lifecycle.LiveData;
|
||||||
import androidx.lifecycle.MutableLiveData;
|
import androidx.lifecycle.MutableLiveData;
|
||||||
@@ -14,7 +16,11 @@ import androidx.lifecycle.ViewModel;
|
|||||||
import androidx.lifecycle.ViewModelProvider;
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.BlockUnblockDialog;
|
import org.thoughtcrime.securesms.BlockUnblockDialog;
|
||||||
|
import org.thoughtcrime.securesms.ContactSelectionListFragment;
|
||||||
import org.thoughtcrime.securesms.ExpirationDialog;
|
import org.thoughtcrime.securesms.ExpirationDialog;
|
||||||
|
import org.thoughtcrime.securesms.PushContactSelectionActivity;
|
||||||
|
import org.thoughtcrime.securesms.R;
|
||||||
|
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader;
|
||||||
import org.thoughtcrime.securesms.database.MediaDatabase;
|
import org.thoughtcrime.securesms.database.MediaDatabase;
|
||||||
import org.thoughtcrime.securesms.database.loaders.MediaLoader;
|
import org.thoughtcrime.securesms.database.loaders.MediaLoader;
|
||||||
import org.thoughtcrime.securesms.database.loaders.ThreadMediaLoader;
|
import org.thoughtcrime.securesms.database.loaders.ThreadMediaLoader;
|
||||||
@@ -211,6 +217,20 @@ public class ManageGroupViewModel extends ViewModel {
|
|||||||
Util.runOnMain(() -> Toast.makeText(context, GroupErrors.getUserDisplayMessage(e), Toast.LENGTH_LONG).show());
|
Util.runOnMain(() -> Toast.makeText(context, GroupErrors.getUserDisplayMessage(e), Toast.LENGTH_LONG).show());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void onAddMembersClick(@NonNull Fragment fragment, int resultCode) {
|
||||||
|
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();
|
||||||
|
} else {
|
||||||
|
Intent intent = new Intent(fragment.requireActivity(), PushContactSelectionActivity.class);
|
||||||
|
intent.putExtra(ContactSelectionListFragment.DISPLAY_MODE, ContactsCursorLoader.DisplayMode.FLAG_PUSH);
|
||||||
|
intent.putExtra(ContactSelectionListFragment.SELECTION_LIMIT, remainingCapacity);
|
||||||
|
fragment.startActivityForResult(intent, resultCode);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
static final class GroupViewState {
|
static final class GroupViewState {
|
||||||
private final long threadId;
|
private final long threadId;
|
||||||
@NonNull private final Recipient groupRecipient;
|
@NonNull private final Recipient groupRecipient;
|
||||||
|
@@ -65,6 +65,7 @@ public final class FeatureFlags {
|
|||||||
private static final String VERSIONED_PROFILES = "android.versionedProfiles";
|
private static final String VERSIONED_PROFILES = "android.versionedProfiles";
|
||||||
private static final String GROUPS_V2 = "android.groupsv2";
|
private static final String GROUPS_V2 = "android.groupsv2";
|
||||||
private static final String GROUPS_V2_CREATE = "android.groupsv2.create";
|
private static final String GROUPS_V2_CREATE = "android.groupsv2.create";
|
||||||
|
private static final String GROUPS_V2_CAPACITY = "android.groupsv2.capacity";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* We will only store remote values for flags in this set. If you want a flag to be controllable
|
* We will only store remote values for flags in this set. If you want a flag to be controllable
|
||||||
@@ -87,6 +88,7 @@ public final class FeatureFlags {
|
|||||||
VERSIONED_PROFILES,
|
VERSIONED_PROFILES,
|
||||||
GROUPS_V2,
|
GROUPS_V2,
|
||||||
GROUPS_V2_CREATE,
|
GROUPS_V2_CREATE,
|
||||||
|
GROUPS_V2_CAPACITY,
|
||||||
NEW_GROUP_UI
|
NEW_GROUP_UI
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -283,6 +285,13 @@ public final class FeatureFlags {
|
|||||||
return groupsV2() && getBoolean(GROUPS_V2_CREATE, false);
|
return groupsV2() && getBoolean(GROUPS_V2_CREATE, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum number of members allowed in a group.
|
||||||
|
*/
|
||||||
|
public static int gv2GroupCapacity() {
|
||||||
|
return getInteger(GROUPS_V2_CAPACITY, 100);
|
||||||
|
}
|
||||||
|
|
||||||
/** Only for rendering debug info. */
|
/** Only for rendering debug info. */
|
||||||
public static synchronized @NonNull Map<String, Object> getMemoryValues() {
|
public static synchronized @NonNull Map<String, Object> getMemoryValues() {
|
||||||
return new TreeMap<>(REMOTE_VALUES);
|
return new TreeMap<>(REMOTE_VALUES);
|
||||||
|
@@ -110,6 +110,17 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/group_limit"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="@style/TextAppearance.Signal.Body2"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintEnd_toEndOf="@+id/chipGroupScrollContainer"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:text="999/999"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<HorizontalScrollView
|
<HorizontalScrollView
|
||||||
android:id="@+id/chipGroupScrollContainer"
|
android:id="@+id/chipGroupScrollContainer"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -122,7 +133,7 @@
|
|||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintHorizontal_bias="0.5"
|
app:layout_constraintHorizontal_bias="0.5"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toBottomOf="@id/group_limit"
|
||||||
tools:visibility="visible">
|
tools:visibility="visible">
|
||||||
|
|
||||||
<com.google.android.material.chip.ChipGroup
|
<com.google.android.material.chip.ChipGroup
|
||||||
|
@@ -1372,6 +1372,7 @@
|
|||||||
<string name="ContactSelectionListFragment_username_not_found">Username not found</string>
|
<string name="ContactSelectionListFragment_username_not_found">Username not found</string>
|
||||||
<string name="ContactSelectionListFragment_s_is_not_a_signal_user">"%1$s" is not a Signal user. Please check the username and try again.</string>
|
<string name="ContactSelectionListFragment_s_is_not_a_signal_user">"%1$s" is not a Signal user. Please check the username and try again.</string>
|
||||||
<string name="ContactSelectionListFragment_okay">Okay</string>
|
<string name="ContactSelectionListFragment_okay">Okay</string>
|
||||||
|
<string name="ContactSelectionListFragment_the_group_is_full">The group is full</string>
|
||||||
|
|
||||||
<!-- blocked_contacts_fragment -->
|
<!-- blocked_contacts_fragment -->
|
||||||
<string name="blocked_contacts_fragment__no_blocked_contacts">No blocked contacts</string>
|
<string name="blocked_contacts_fragment__no_blocked_contacts">No blocked contacts</string>
|
||||||
|
Reference in New Issue
Block a user