Invite Friends bottom sheet.
This commit is contained in:
parent
3739eb7731
commit
4d229862b6
17 changed files with 616 additions and 31 deletions
|
@ -62,6 +62,7 @@ public interface BindableConversationItem extends Unbindable {
|
|||
void onVoiceNoteSeekTo(@NonNull Uri uri, double position);
|
||||
void onGroupMigrationLearnMoreClicked(@NonNull GroupMigrationMembershipChange membershipChange);
|
||||
void onJoinGroupCallClicked();
|
||||
void onInviteFriendsToGroupClicked(@NonNull GroupId.V2 groupId);
|
||||
|
||||
/** @return true if handled, false if you want to let the normal url handling continue */
|
||||
boolean onUrlClicked(@NonNull String url);
|
||||
|
|
|
@ -536,6 +536,10 @@ public class ConversationActivity extends PassphraseRequiredActivity
|
|||
.startChain(new RequestGroupV2InfoJob(groupId))
|
||||
.then(new GroupV2UpdateSelfProfileKeyJob(groupId))
|
||||
.enqueue();
|
||||
|
||||
if (viewModel.getArgs().isFirstTimeInSelfCreatedGroup()) {
|
||||
groupViewModel.inviteFriendsOneTimeIfJustSelfInGroup(getSupportFragmentManager(), groupId);
|
||||
}
|
||||
}
|
||||
|
||||
if (groupCallViewModel != null) {
|
||||
|
|
|
@ -93,6 +93,7 @@ import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
|
|||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.groups.GroupId;
|
||||
import org.thoughtcrime.securesms.groups.GroupMigrationMembershipChange;
|
||||
import org.thoughtcrime.securesms.groups.ui.invitesandrequests.invite.GroupLinkInviteFriendsBottomSheetDialogFragment;
|
||||
import org.thoughtcrime.securesms.groups.ui.migration.GroupsV1MigrationInfoBottomSheetDialogFragment;
|
||||
import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob;
|
||||
import org.thoughtcrime.securesms.jobs.MultiDeviceViewOnceOpenJob;
|
||||
|
@ -1418,6 +1419,11 @@ public class ConversationFragment extends LoggingFragment {
|
|||
public void onJoinGroupCallClicked() {
|
||||
CommunicationActions.startVideoCall(requireActivity(), recipient.get());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInviteFriendsToGroupClicked(@NonNull GroupId.V2 groupId) {
|
||||
GroupLinkInviteFriendsBottomSheetDialogFragment.show(requireActivity().getSupportFragmentManager(), groupId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -6,6 +6,7 @@ import android.content.Context;
|
|||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.WorkerThread;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.Transformations;
|
||||
|
@ -25,12 +26,14 @@ import org.thoughtcrime.securesms.groups.GroupId;
|
|||
import org.thoughtcrime.securesms.groups.GroupManager;
|
||||
import org.thoughtcrime.securesms.groups.GroupsV1MigrationUtil;
|
||||
import org.thoughtcrime.securesms.groups.ui.GroupChangeFailureReason;
|
||||
import org.thoughtcrime.securesms.groups.ui.invitesandrequests.invite.GroupLinkInviteFriendsBottomSheetDialogFragment;
|
||||
import org.thoughtcrime.securesms.profiles.spoofing.ReviewRecipient;
|
||||
import org.thoughtcrime.securesms.profiles.spoofing.ReviewUtil;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.util.AsynchronousCallback;
|
||||
import org.thoughtcrime.securesms.util.FeatureFlags;
|
||||
import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
|
||||
import org.thoughtcrime.securesms.util.livedata.LiveDataUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -50,6 +53,8 @@ final class ConversationGroupViewModel extends ViewModel {
|
|||
private final LiveData<List<RecipientId>> gv1MigrationSuggestions;
|
||||
private final LiveData<Boolean> gv1MigrationReminder;
|
||||
|
||||
private boolean firstTimeInviteFriendsTriggered;
|
||||
|
||||
private ConversationGroupViewModel() {
|
||||
this.liveRecipient = new MutableLiveData<>();
|
||||
|
||||
|
@ -225,6 +230,28 @@ final class ConversationGroupViewModel extends ViewModel {
|
|||
});
|
||||
}
|
||||
|
||||
void inviteFriendsOneTimeIfJustSelfInGroup(@NonNull FragmentManager supportFragmentManager, @NonNull GroupId.V2 groupId) {
|
||||
if (firstTimeInviteFriendsTriggered) {
|
||||
return;
|
||||
}
|
||||
|
||||
firstTimeInviteFriendsTriggered = true;
|
||||
|
||||
SimpleTask.run(() -> DatabaseFactory.getGroupDatabase(ApplicationDependencies.getApplication())
|
||||
.requireGroup(groupId)
|
||||
.getMembers().equals(Collections.singletonList(Recipient.self().getId())),
|
||||
justSelf -> {
|
||||
if (justSelf) {
|
||||
inviteFriends(supportFragmentManager, groupId);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void inviteFriends(@NonNull FragmentManager supportFragmentManager, @NonNull GroupId.V2 groupId) {
|
||||
GroupLinkInviteFriendsBottomSheetDialogFragment.show(supportFragmentManager, groupId);
|
||||
}
|
||||
|
||||
static final class ReviewState {
|
||||
|
||||
private static final ReviewState EMPTY = new ReviewState(null, Recipient.UNKNOWN, 0);
|
||||
|
|
|
@ -27,6 +27,7 @@ public class ConversationIntents {
|
|||
private static final String EXTRA_BORDERLESS = "borderless_extra";
|
||||
private static final String EXTRA_DISTRIBUTION_TYPE = "distribution_type";
|
||||
private static final String EXTRA_STARTING_POSITION = "starting_position";
|
||||
private static final String EXTRA_FIRST_TIME_IN_SELF_CREATED_GROUP = "first_time_in_group";
|
||||
|
||||
private ConversationIntents() {
|
||||
}
|
||||
|
@ -64,6 +65,7 @@ public class ConversationIntents {
|
|||
private final boolean isBorderless;
|
||||
private final int distributionType;
|
||||
private final int startingPosition;
|
||||
private final boolean firstTimeInSelfCreatedGroup;
|
||||
|
||||
static Args from(@NonNull Intent intent) {
|
||||
if (isBubbleIntent(intent)) {
|
||||
|
@ -74,7 +76,8 @@ public class ConversationIntents {
|
|||
null,
|
||||
false,
|
||||
ThreadDatabase.DistributionTypes.DEFAULT,
|
||||
-1);
|
||||
-1,
|
||||
false);
|
||||
}
|
||||
|
||||
return new Args(RecipientId.from(Objects.requireNonNull(intent.getStringExtra(EXTRA_RECIPIENT))),
|
||||
|
@ -84,7 +87,8 @@ public class ConversationIntents {
|
|||
intent.getParcelableExtra(EXTRA_STICKER),
|
||||
intent.getBooleanExtra(EXTRA_BORDERLESS, false),
|
||||
intent.getIntExtra(EXTRA_DISTRIBUTION_TYPE, ThreadDatabase.DistributionTypes.DEFAULT),
|
||||
intent.getIntExtra(EXTRA_STARTING_POSITION, -1));
|
||||
intent.getIntExtra(EXTRA_STARTING_POSITION, -1),
|
||||
intent.getBooleanExtra(EXTRA_FIRST_TIME_IN_SELF_CREATED_GROUP, false));
|
||||
}
|
||||
|
||||
private Args(@NonNull RecipientId recipientId,
|
||||
|
@ -94,7 +98,8 @@ public class ConversationIntents {
|
|||
@Nullable StickerLocator stickerLocator,
|
||||
boolean isBorderless,
|
||||
int distributionType,
|
||||
int startingPosition)
|
||||
int startingPosition,
|
||||
boolean firstTimeInSelfCreatedGroup)
|
||||
{
|
||||
this.recipientId = recipientId;
|
||||
this.threadId = threadId;
|
||||
|
@ -104,6 +109,7 @@ public class ConversationIntents {
|
|||
this.isBorderless = isBorderless;
|
||||
this.distributionType = distributionType;
|
||||
this.startingPosition = startingPosition;
|
||||
this.firstTimeInSelfCreatedGroup = firstTimeInSelfCreatedGroup;
|
||||
}
|
||||
|
||||
public @NonNull RecipientId getRecipientId() {
|
||||
|
@ -137,6 +143,10 @@ public class ConversationIntents {
|
|||
public boolean isBorderless() {
|
||||
return isBorderless;
|
||||
}
|
||||
|
||||
public boolean isFirstTimeInSelfCreatedGroup() {
|
||||
return firstTimeInSelfCreatedGroup;
|
||||
}
|
||||
}
|
||||
|
||||
public final static class Builder {
|
||||
|
@ -153,6 +163,7 @@ public class ConversationIntents {
|
|||
private int startingPosition = -1;
|
||||
private Uri dataUri;
|
||||
private String dataType;
|
||||
private boolean firstTimeInSelfCreatedGroup;
|
||||
|
||||
private Builder(@NonNull Context context,
|
||||
@NonNull RecipientId recipientId,
|
||||
|
@ -212,6 +223,11 @@ public class ConversationIntents {
|
|||
return this;
|
||||
}
|
||||
|
||||
public Builder firstTimeInSelfCreatedGroup() {
|
||||
this.firstTimeInSelfCreatedGroup = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public @NonNull Intent build() {
|
||||
if (stickerLocator != null && media != null) {
|
||||
throw new IllegalStateException("Cannot have both sticker and media array");
|
||||
|
@ -235,6 +251,7 @@ public class ConversationIntents {
|
|||
intent.putExtra(EXTRA_DISTRIBUTION_TYPE, distributionType);
|
||||
intent.putExtra(EXTRA_STARTING_POSITION, startingPosition);
|
||||
intent.putExtra(EXTRA_BORDERLESS, isBorderless);
|
||||
intent.putExtra(EXTRA_FIRST_TIME_IN_SELF_CREATED_GROUP, firstTimeInSelfCreatedGroup);
|
||||
|
||||
if (draftText != null) {
|
||||
intent.putExtra(EXTRA_TEXT, draftText);
|
||||
|
|
|
@ -238,8 +238,7 @@ public final class ConversationUpdateItem extends LinearLayout
|
|||
actionButton.setVisibility(VISIBLE);
|
||||
actionButton.setOnClickListener(v -> {
|
||||
if (batchSelected.isEmpty() && eventListener != null) {
|
||||
// TODO [alan]
|
||||
Log.i(TAG, "TODO");
|
||||
eventListener.onInviteFriendsToGroupClicked(conversationRecipient.requireGroupId().requireV2());
|
||||
}
|
||||
});
|
||||
} else {
|
||||
|
|
|
@ -15,6 +15,7 @@ import org.signal.zkgroup.groups.GroupMasterKey;
|
|||
import org.signal.zkgroup.groups.UuidCiphertext;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||
import org.thoughtcrime.securesms.groups.v2.GroupInviteLinkUrl;
|
||||
import org.thoughtcrime.securesms.groups.v2.GroupLinkPassword;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
import org.thoughtcrime.securesms.profiles.AvatarHelper;
|
||||
|
@ -300,13 +301,13 @@ public final class GroupManager {
|
|||
}
|
||||
|
||||
@WorkerThread
|
||||
public static void setGroupLinkEnabledState(@NonNull Context context,
|
||||
public static GroupInviteLinkUrl setGroupLinkEnabledState(@NonNull Context context,
|
||||
@NonNull GroupId.V2 groupId,
|
||||
@NonNull GroupLinkState state)
|
||||
throws GroupChangeFailedException, GroupInsufficientRightsException, IOException, GroupNotAMemberException, GroupChangeBusyException
|
||||
{
|
||||
try (GroupManagerV2.GroupEditor editor = new GroupManagerV2(context).edit(groupId.requireV2())) {
|
||||
editor.setJoinByGroupLinkState(state);
|
||||
return editor.setJoinByGroupLinkState(state);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@ import org.thoughtcrime.securesms.database.ThreadDatabase;
|
|||
import org.thoughtcrime.securesms.database.model.databaseprotos.DecryptedGroupV2Context;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.groups.v2.GroupCandidateHelper;
|
||||
import org.thoughtcrime.securesms.groups.v2.GroupInviteLinkUrl;
|
||||
import org.thoughtcrime.securesms.groups.v2.GroupLinkPassword;
|
||||
import org.thoughtcrime.securesms.groups.v2.processing.GroupsV2StateProcessor;
|
||||
import org.thoughtcrime.securesms.jobs.ProfileUploadJob;
|
||||
|
@ -507,7 +508,7 @@ final class GroupManagerV2 {
|
|||
}
|
||||
|
||||
@WorkerThread
|
||||
public GroupManager.GroupActionResult setJoinByGroupLinkState(@NonNull GroupManager.GroupLinkState state)
|
||||
public @Nullable GroupInviteLinkUrl setJoinByGroupLinkState(@NonNull GroupManager.GroupLinkState state)
|
||||
throws GroupChangeFailedException, GroupNotAMemberException, GroupInsufficientRightsException, IOException
|
||||
{
|
||||
AccessControl.AccessRequired access;
|
||||
|
@ -530,7 +531,17 @@ final class GroupManagerV2 {
|
|||
}
|
||||
}
|
||||
|
||||
return commitChangeWithConflictResolution(change);
|
||||
commitChangeWithConflictResolution(change);
|
||||
|
||||
if (state != GroupManager.GroupLinkState.DISABLED) {
|
||||
GroupDatabase.V2GroupProperties v2GroupProperties = groupDatabase.requireGroup(groupId).requireV2GroupProperties();
|
||||
GroupMasterKey groupMasterKey = v2GroupProperties.getGroupMasterKey();
|
||||
DecryptedGroup decryptedGroup = v2GroupProperties.getDecryptedGroup();
|
||||
|
||||
return GroupInviteLinkUrl.forGroup(groupMasterKey, decryptedGroup);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private @NonNull GroupManager.GroupActionResult commitChangeWithConflictResolution(@NonNull GroupChange.Actions.Builder change)
|
||||
|
|
|
@ -73,6 +73,7 @@ public class AddGroupDetailsActivity extends PassphraseRequiredActivity implemen
|
|||
|
||||
void goToConversation(@NonNull RecipientId recipientId, long threadId) {
|
||||
Intent intent = ConversationIntents.createBuilder(this, recipientId, threadId)
|
||||
.firstTimeInSelfCreatedGroup()
|
||||
.build();
|
||||
|
||||
startActivity(intent);
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
package org.thoughtcrime.securesms.groups.ui.invitesandrequests.invite;
|
||||
|
||||
enum EnableInviteLinkError {
|
||||
BUSY,
|
||||
FAILED,
|
||||
NETWORK_ERROR,
|
||||
INSUFFICIENT_RIGHTS,
|
||||
NOT_IN_GROUP,
|
||||
}
|
|
@ -0,0 +1,175 @@
|
|||
package org.thoughtcrime.securesms.groups.ui.invitesandrequests.invite;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.widget.SwitchCompat;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.lifecycle.ViewModelProviders;
|
||||
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
|
||||
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.groups.BadGroupIdException;
|
||||
import org.thoughtcrime.securesms.groups.GroupId;
|
||||
import org.thoughtcrime.securesms.recipients.ui.sharablegrouplink.GroupLinkBottomSheetDialogFragment;
|
||||
import org.thoughtcrime.securesms.util.BottomSheetUtil;
|
||||
import org.thoughtcrime.securesms.util.ThemeUtil;
|
||||
import org.thoughtcrime.securesms.util.views.SimpleProgressDialog;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public final class GroupLinkInviteFriendsBottomSheetDialogFragment extends BottomSheetDialogFragment {
|
||||
|
||||
private static final String TAG = Log.tag(GroupLinkInviteFriendsBottomSheetDialogFragment.class);
|
||||
|
||||
private static final String ARG_GROUP_ID = "group_id";
|
||||
|
||||
private Button groupLinkEnableAndShareButton;
|
||||
private Button groupLinkShareButton;
|
||||
private View memberApprovalRow;
|
||||
private View memberApprovalRow2;
|
||||
private SwitchCompat memberApprovalSwitch;
|
||||
|
||||
private SimpleProgressDialog.DismissibleDialog busyDialog;
|
||||
|
||||
public static void show(@NonNull FragmentManager manager,
|
||||
@NonNull GroupId.V2 groupId)
|
||||
{
|
||||
GroupLinkInviteFriendsBottomSheetDialogFragment fragment = new GroupLinkInviteFriendsBottomSheetDialogFragment();
|
||||
|
||||
Bundle args = new Bundle();
|
||||
args.putString(ARG_GROUP_ID, groupId.toString());
|
||||
fragment.setArguments(args);
|
||||
|
||||
fragment.show(manager, BottomSheetUtil.STANDARD_BOTTOM_SHEET_FRAGMENT_TAG);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
setStyle(DialogFragment.STYLE_NORMAL,
|
||||
ThemeUtil.isDarkTheme(requireContext()) ? R.style.Theme_Signal_RoundedBottomSheet
|
||||
: R.style.Theme_Signal_RoundedBottomSheet_Light);
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.group_invite_link_enable_and_share_bottom_sheet, container, false);
|
||||
|
||||
groupLinkEnableAndShareButton = view.findViewById(R.id.group_link_enable_and_share_button);
|
||||
groupLinkShareButton = view.findViewById(R.id.group_link_share_button);
|
||||
memberApprovalRow = view.findViewById(R.id.group_link_enable_and_share_approve_new_members_row);
|
||||
memberApprovalRow2 = view.findViewById(R.id.group_link_enable_and_share_approve_new_members_row2);
|
||||
memberApprovalSwitch = view.findViewById(R.id.group_link_enable_and_share_approve_new_members_switch);
|
||||
|
||||
view.findViewById(R.id.group_link_enable_and_share_cancel_button).setOnClickListener(v -> dismiss());
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
GroupId.V2 groupId = getGroupId();
|
||||
|
||||
GroupLinkInviteFriendsViewModel.Factory factory = new GroupLinkInviteFriendsViewModel.Factory(requireContext().getApplicationContext(), groupId);
|
||||
GroupLinkInviteFriendsViewModel viewModel = ViewModelProviders.of(this, factory).get(GroupLinkInviteFriendsViewModel.class);
|
||||
|
||||
viewModel.getGroupInviteLinkAndStatus()
|
||||
.observe(getViewLifecycleOwner(), groupLinkUrlAndStatus -> {
|
||||
if (groupLinkUrlAndStatus.isEnabled()) {
|
||||
groupLinkShareButton.setVisibility(View.VISIBLE);
|
||||
groupLinkEnableAndShareButton.setVisibility(View.INVISIBLE);
|
||||
memberApprovalRow.setVisibility(View.GONE);
|
||||
memberApprovalRow2.setVisibility(View.GONE);
|
||||
|
||||
groupLinkShareButton.setOnClickListener(v -> shareGroupLinkAndDismiss(groupId));
|
||||
} else {
|
||||
memberApprovalRow.setVisibility(View.VISIBLE);
|
||||
memberApprovalRow2.setVisibility(View.VISIBLE);
|
||||
|
||||
groupLinkEnableAndShareButton.setVisibility(View.VISIBLE);
|
||||
groupLinkShareButton.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
});
|
||||
|
||||
memberApprovalRow.setOnClickListener(v -> viewModel.toggleMemberApproval());
|
||||
|
||||
viewModel.getMemberApproval()
|
||||
.observe(getViewLifecycleOwner(), enabled -> memberApprovalSwitch.setChecked(enabled));
|
||||
|
||||
viewModel.isBusy()
|
||||
.observe(getViewLifecycleOwner(), this::setBusy);
|
||||
|
||||
viewModel.getEnableErrors()
|
||||
.observe(getViewLifecycleOwner(), error -> {
|
||||
Toast.makeText(requireContext(), errorToMessage(error), Toast.LENGTH_SHORT).show();
|
||||
|
||||
if (error == EnableInviteLinkError.NOT_IN_GROUP || error == EnableInviteLinkError.INSUFFICIENT_RIGHTS) {
|
||||
dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
groupLinkEnableAndShareButton.setOnClickListener(v -> viewModel.enable());
|
||||
|
||||
viewModel.getEnableSuccess()
|
||||
.observe(getViewLifecycleOwner(), joinGroupSuccess -> {
|
||||
Log.i(TAG, "Group link enabled, sharing");
|
||||
shareGroupLinkAndDismiss(groupId);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
protected void shareGroupLinkAndDismiss(@NonNull GroupId.V2 groupId) {
|
||||
dismiss();
|
||||
|
||||
GroupLinkBottomSheetDialogFragment.show(requireFragmentManager(), groupId);
|
||||
}
|
||||
|
||||
protected GroupId.V2 getGroupId() {
|
||||
try {
|
||||
return GroupId.parse(Objects.requireNonNull(requireArguments().getString(ARG_GROUP_ID)))
|
||||
.requireV2();
|
||||
} catch (BadGroupIdException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void setBusy(boolean isBusy) {
|
||||
if (isBusy) {
|
||||
if (busyDialog == null) {
|
||||
busyDialog = SimpleProgressDialog.showDelayed(requireContext());
|
||||
}
|
||||
} else {
|
||||
if (busyDialog != null) {
|
||||
busyDialog.dismiss();
|
||||
busyDialog = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private @NonNull String errorToMessage(@NonNull EnableInviteLinkError error) {
|
||||
switch (error) {
|
||||
case NETWORK_ERROR : return getString(R.string.GroupInviteLinkEnableAndShareBottomSheetDialogFragment_encountered_a_network_error);
|
||||
case INSUFFICIENT_RIGHTS : return getString(R.string.GroupInviteLinkEnableAndShareBottomSheetDialogFragment_you_dont_have_the_right_to_enable_group_link);
|
||||
case NOT_IN_GROUP : return getString(R.string.GroupInviteLinkEnableAndShareBottomSheetDialogFragment_you_are_not_currently_a_member_of_the_group);
|
||||
default : return getString(R.string.GroupInviteLinkEnableAndShareBottomSheetDialogFragment_unable_to_enable_group_link_please_try_again_later);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void show(@NonNull FragmentManager manager, @Nullable String tag) {
|
||||
BottomSheetUtil.show(manager, tag, this);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
package org.thoughtcrime.securesms.groups.ui.invitesandrequests.invite;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MediatorLiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
import org.thoughtcrime.securesms.groups.GroupId;
|
||||
import org.thoughtcrime.securesms.groups.LiveGroup;
|
||||
import org.thoughtcrime.securesms.groups.v2.GroupInviteLinkUrl;
|
||||
import org.thoughtcrime.securesms.groups.v2.GroupLinkUrlAndStatus;
|
||||
import org.thoughtcrime.securesms.util.AsynchronousCallback;
|
||||
import org.thoughtcrime.securesms.util.SingleLiveEvent;
|
||||
|
||||
public class GroupLinkInviteFriendsViewModel extends ViewModel {
|
||||
|
||||
private static final boolean INITIAL_MEMBER_APPROVAL_STATE = false;
|
||||
|
||||
private final GroupLinkInviteRepository repository;
|
||||
private final MutableLiveData<EnableInviteLinkError> enableErrors = new SingleLiveEvent<>();
|
||||
private final MutableLiveData<Boolean> busy = new MediatorLiveData<>();
|
||||
private final MutableLiveData<GroupInviteLinkUrl> enableSuccess = new SingleLiveEvent<>();
|
||||
private final LiveData<GroupLinkUrlAndStatus> groupLink;
|
||||
private final MutableLiveData<Boolean> memberApproval = new MutableLiveData<>(INITIAL_MEMBER_APPROVAL_STATE);
|
||||
|
||||
private GroupLinkInviteFriendsViewModel(GroupId.V2 groupId, @NonNull GroupLinkInviteRepository repository) {
|
||||
this.repository = repository;
|
||||
|
||||
LiveGroup liveGroup = new LiveGroup(groupId);
|
||||
|
||||
this.groupLink = liveGroup.getGroupLink();
|
||||
}
|
||||
|
||||
LiveData<GroupLinkUrlAndStatus> getGroupInviteLinkAndStatus() {
|
||||
return groupLink;
|
||||
}
|
||||
|
||||
void enable() {
|
||||
busy.setValue(true);
|
||||
repository.enableGroupInviteLink(getCurrentMemberApproval(), new AsynchronousCallback.WorkerThread<GroupInviteLinkUrl, EnableInviteLinkError>() {
|
||||
@Override
|
||||
public void onComplete(@Nullable GroupInviteLinkUrl groupInviteLinkUrl) {
|
||||
busy.postValue(false);
|
||||
enableSuccess.postValue(groupInviteLinkUrl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(@Nullable EnableInviteLinkError error) {
|
||||
busy.postValue(false);
|
||||
enableErrors.postValue(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
LiveData<Boolean> isBusy() {
|
||||
return busy;
|
||||
}
|
||||
|
||||
LiveData<GroupInviteLinkUrl> getEnableSuccess() {
|
||||
return enableSuccess;
|
||||
}
|
||||
|
||||
LiveData<EnableInviteLinkError> getEnableErrors() {
|
||||
return enableErrors;
|
||||
}
|
||||
|
||||
LiveData<Boolean> getMemberApproval() {
|
||||
return memberApproval;
|
||||
}
|
||||
|
||||
private boolean getCurrentMemberApproval() {
|
||||
Boolean value = memberApproval.getValue();
|
||||
if (value == null) {
|
||||
return INITIAL_MEMBER_APPROVAL_STATE;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
void toggleMemberApproval() {
|
||||
memberApproval.postValue(!getCurrentMemberApproval());
|
||||
}
|
||||
|
||||
public static class Factory implements ViewModelProvider.Factory {
|
||||
|
||||
private final Context context;
|
||||
private final GroupId.V2 groupId;
|
||||
|
||||
public Factory(@NonNull Context context, @NonNull GroupId.V2 groupId) {
|
||||
this.context = context;
|
||||
this.groupId = groupId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
|
||||
//noinspection unchecked
|
||||
return (T) new GroupLinkInviteFriendsViewModel(groupId, new GroupLinkInviteRepository(context.getApplicationContext(), groupId));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
package org.thoughtcrime.securesms.groups.ui.invitesandrequests.invite;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.signal.core.util.concurrent.SignalExecutors;
|
||||
import org.thoughtcrime.securesms.groups.GroupChangeBusyException;
|
||||
import org.thoughtcrime.securesms.groups.GroupChangeFailedException;
|
||||
import org.thoughtcrime.securesms.groups.GroupId;
|
||||
import org.thoughtcrime.securesms.groups.GroupInsufficientRightsException;
|
||||
import org.thoughtcrime.securesms.groups.GroupManager;
|
||||
import org.thoughtcrime.securesms.groups.GroupNotAMemberException;
|
||||
import org.thoughtcrime.securesms.groups.v2.GroupInviteLinkUrl;
|
||||
import org.thoughtcrime.securesms.util.AsynchronousCallback;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
final class GroupLinkInviteRepository {
|
||||
|
||||
private final Context context;
|
||||
private final GroupId.V2 groupId;
|
||||
|
||||
GroupLinkInviteRepository(@NonNull Context context, @NonNull GroupId.V2 groupId) {
|
||||
this.context = context;
|
||||
this.groupId = groupId;
|
||||
}
|
||||
|
||||
void enableGroupInviteLink(boolean requireMemberApproval, @NonNull AsynchronousCallback.WorkerThread<GroupInviteLinkUrl, EnableInviteLinkError> callback) {
|
||||
SignalExecutors.UNBOUNDED.execute(() -> {
|
||||
try {
|
||||
GroupInviteLinkUrl groupInviteLinkUrl = GroupManager.setGroupLinkEnabledState(context,
|
||||
groupId,
|
||||
requireMemberApproval ? GroupManager.GroupLinkState.ENABLED_WITH_APPROVAL
|
||||
: GroupManager.GroupLinkState.ENABLED);
|
||||
|
||||
if (groupInviteLinkUrl == null) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
callback.onComplete(groupInviteLinkUrl);
|
||||
} catch (IOException e) {
|
||||
callback.onError(EnableInviteLinkError.NETWORK_ERROR);
|
||||
} catch (GroupChangeBusyException e) {
|
||||
callback.onError(EnableInviteLinkError.BUSY);
|
||||
} catch (GroupChangeFailedException e) {
|
||||
callback.onError(EnableInviteLinkError.FAILED);
|
||||
} catch (GroupInsufficientRightsException e) {
|
||||
callback.onError(EnableInviteLinkError.INSUFFICIENT_RIGHTS);
|
||||
} catch (GroupNotAMemberException e) {
|
||||
callback.onError(EnableInviteLinkError.NOT_IN_GROUP);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -34,7 +34,7 @@ import org.thoughtcrime.securesms.util.ThemeUtil;
|
|||
|
||||
public final class GroupJoinBottomSheetDialogFragment extends BottomSheetDialogFragment {
|
||||
|
||||
private static final String TAG = Log.tag(GroupJoinUpdateRequiredBottomSheetDialogFragment.class);
|
||||
private static final String TAG = Log.tag(GroupJoinBottomSheetDialogFragment.class);
|
||||
|
||||
private static final String ARG_GROUP_INVITE_LINK_URL = "group_invite_url";
|
||||
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<corners android:radius="8dp" />
|
||||
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@color/core_grey_05" />
|
||||
</shape>
|
|
@ -0,0 +1,154 @@
|
|||
<?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"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:theme="@style/Theme.Signal.RoundedBottomSheet.Light">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/group_link_enable_and_share_title"
|
||||
style="@style/TextAppearance.Signal.Title2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/GroupInviteLinkEnableAndShareBottomSheetDialogFragment_invite_friends"
|
||||
android:textColor="@color/signal_text_primary"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/group_link_enable_and_share_explain"
|
||||
style="@style/TextAppearance.Signal.Body2"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:text="@string/GroupInviteLinkEnableAndShareBottomSheetDialogFragment_share_a_link_with_friends_to_let_them_quickly_join_this_group"
|
||||
android:textAlignment="center"
|
||||
android:textColor="@color/signal_text_primary"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/group_link_enable_and_share_title" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/group_link_enable_and_share_approve_new_members_row"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="24dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:background="@drawable/group_link_admin_approval_border"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:orientation="horizontal"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/group_link_enable_and_share_explain">
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center_vertical|start"
|
||||
android:text="@string/ShareableGroupLinkDialogFragment__approve_new_members"
|
||||
android:textAlignment="viewStart"
|
||||
android:textAppearance="@style/TextAppearance.Signal.Body2"
|
||||
android:textColor="@color/text_color_primary_enabled_selector" />
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:id="@+id/group_link_enable_and_share_approve_new_members_switch"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:clickable="false"
|
||||
app:layout_constraintBottom_toBottomOf="@id/shareable_group_link_enable_label"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/shareable_group_link_enable_label"
|
||||
app:layout_constraintTop_toTopOf="@id/shareable_group_link_enable_label" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/group_link_enable_and_share_approve_new_members_row2"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:background="?selectableItemBackground"
|
||||
android:orientation="horizontal"
|
||||
android:paddingStart="32dp"
|
||||
android:paddingEnd="32dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/group_link_enable_and_share_approve_new_members_row">
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center_vertical|start"
|
||||
android:text="@string/ShareableGroupLinkDialogFragment__require_an_admin_to_approve_new_members_joining_via_the_group_link"
|
||||
android:textAlignment="viewStart"
|
||||
android:textAppearance="@style/TextAppearance.Signal.Body2"
|
||||
android:textColor="@color/text_color_secondary_enabled_selector" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<Button
|
||||
android:id="@+id/group_link_enable_and_share_button"
|
||||
style="@style/Button.Primary"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="64dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="32dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:text="@string/GroupInviteLinkEnableAndShareBottomSheetDialogFragment_enable_and_share_link"
|
||||
android:visibility="invisible"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/group_link_enable_and_share_approve_new_members_row2"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/group_link_share_button"
|
||||
style="@style/Button.Primary"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="64dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="32dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:text="@string/GroupInviteLinkEnableAndShareBottomSheetDialogFragment_share_link"
|
||||
android:visibility="invisible"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/group_link_enable_and_share_approve_new_members_row2" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/group_link_enable_and_share_cancel_button"
|
||||
style="@style/Button.Primary"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="64dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:text="@android:string/cancel"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/group_link_enable_and_share_button"
|
||||
app:layout_constraintVertical_bias="0" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -889,6 +889,18 @@
|
|||
<string name="GroupJoinUpdateRequiredBottomSheetDialogFragment_update_linked_device_message">One or more of your linked devices are running a version of Signal that doesn\'t support group links. Update Signal on your linked device(s) to join this group.</string>
|
||||
<string name="GroupJoinUpdateRequiredBottomSheetDialogFragment_group_link_is_not_valid">Group link is not valid</string>
|
||||
|
||||
<!-- GroupInviteLinkEnableAndShareBottomSheetDialogFragment -->
|
||||
<string name="GroupInviteLinkEnableAndShareBottomSheetDialogFragment_invite_friends">Invite friends</string>
|
||||
<string name="GroupInviteLinkEnableAndShareBottomSheetDialogFragment_share_a_link_with_friends_to_let_them_quickly_join_this_group">Share a link with friends to let them quickly join this group.</string>
|
||||
|
||||
<string name="GroupInviteLinkEnableAndShareBottomSheetDialogFragment_enable_and_share_link">Enable and share link</string>
|
||||
<string name="GroupInviteLinkEnableAndShareBottomSheetDialogFragment_share_link">Share link</string>
|
||||
|
||||
<string name="GroupInviteLinkEnableAndShareBottomSheetDialogFragment_unable_to_enable_group_link_please_try_again_later">Unable to enable group link. Please try again later</string>
|
||||
<string name="GroupInviteLinkEnableAndShareBottomSheetDialogFragment_encountered_a_network_error">Encountered a network error.</string>
|
||||
<string name="GroupInviteLinkEnableAndShareBottomSheetDialogFragment_you_dont_have_the_right_to_enable_group_link">You don\'t have the right to enable the group link. Please ask an admin.</string>
|
||||
<string name="GroupInviteLinkEnableAndShareBottomSheetDialogFragment_you_are_not_currently_a_member_of_the_group">You are not currently a member of the group.</string>
|
||||
|
||||
<!-- GV2 Request confirmation dialog -->
|
||||
<string name="RequestConfirmationDialog_add_s_to_the_group">Add “%1$s” to the group?</string>
|
||||
<string name="RequestConfirmationDialog_deny_request_from_s">Deny request from “%1$s”?</string>
|
||||
|
|
Loading…
Add table
Reference in a new issue