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 onVoiceNoteSeekTo(@NonNull Uri uri, double position);
|
||||||
void onGroupMigrationLearnMoreClicked(@NonNull GroupMigrationMembershipChange membershipChange);
|
void onGroupMigrationLearnMoreClicked(@NonNull GroupMigrationMembershipChange membershipChange);
|
||||||
void onJoinGroupCallClicked();
|
void onJoinGroupCallClicked();
|
||||||
|
void onInviteFriendsToGroupClicked(@NonNull GroupId.V2 groupId);
|
||||||
|
|
||||||
/** @return true if handled, false if you want to let the normal url handling continue */
|
/** @return true if handled, false if you want to let the normal url handling continue */
|
||||||
boolean onUrlClicked(@NonNull String url);
|
boolean onUrlClicked(@NonNull String url);
|
||||||
|
|
|
@ -536,6 +536,10 @@ public class ConversationActivity extends PassphraseRequiredActivity
|
||||||
.startChain(new RequestGroupV2InfoJob(groupId))
|
.startChain(new RequestGroupV2InfoJob(groupId))
|
||||||
.then(new GroupV2UpdateSelfProfileKeyJob(groupId))
|
.then(new GroupV2UpdateSelfProfileKeyJob(groupId))
|
||||||
.enqueue();
|
.enqueue();
|
||||||
|
|
||||||
|
if (viewModel.getArgs().isFirstTimeInSelfCreatedGroup()) {
|
||||||
|
groupViewModel.inviteFriendsOneTimeIfJustSelfInGroup(getSupportFragmentManager(), groupId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (groupCallViewModel != null) {
|
if (groupCallViewModel != null) {
|
||||||
|
|
|
@ -93,6 +93,7 @@ import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
|
||||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
import org.thoughtcrime.securesms.groups.GroupId;
|
import org.thoughtcrime.securesms.groups.GroupId;
|
||||||
import org.thoughtcrime.securesms.groups.GroupMigrationMembershipChange;
|
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.groups.ui.migration.GroupsV1MigrationInfoBottomSheetDialogFragment;
|
||||||
import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob;
|
import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob;
|
||||||
import org.thoughtcrime.securesms.jobs.MultiDeviceViewOnceOpenJob;
|
import org.thoughtcrime.securesms.jobs.MultiDeviceViewOnceOpenJob;
|
||||||
|
@ -1418,6 +1419,11 @@ public class ConversationFragment extends LoggingFragment {
|
||||||
public void onJoinGroupCallClicked() {
|
public void onJoinGroupCallClicked() {
|
||||||
CommunicationActions.startVideoCall(requireActivity(), recipient.get());
|
CommunicationActions.startVideoCall(requireActivity(), recipient.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onInviteFriendsToGroupClicked(@NonNull GroupId.V2 groupId) {
|
||||||
|
GroupLinkInviteFriendsBottomSheetDialogFragment.show(requireActivity().getSupportFragmentManager(), groupId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -6,6 +6,7 @@ import android.content.Context;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.WorkerThread;
|
import androidx.annotation.WorkerThread;
|
||||||
|
import androidx.fragment.app.FragmentManager;
|
||||||
import androidx.lifecycle.LiveData;
|
import androidx.lifecycle.LiveData;
|
||||||
import androidx.lifecycle.MutableLiveData;
|
import androidx.lifecycle.MutableLiveData;
|
||||||
import androidx.lifecycle.Transformations;
|
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.GroupManager;
|
||||||
import org.thoughtcrime.securesms.groups.GroupsV1MigrationUtil;
|
import org.thoughtcrime.securesms.groups.GroupsV1MigrationUtil;
|
||||||
import org.thoughtcrime.securesms.groups.ui.GroupChangeFailureReason;
|
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.ReviewRecipient;
|
||||||
import org.thoughtcrime.securesms.profiles.spoofing.ReviewUtil;
|
import org.thoughtcrime.securesms.profiles.spoofing.ReviewUtil;
|
||||||
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.AsynchronousCallback;
|
import org.thoughtcrime.securesms.util.AsynchronousCallback;
|
||||||
import org.thoughtcrime.securesms.util.FeatureFlags;
|
import org.thoughtcrime.securesms.util.FeatureFlags;
|
||||||
|
import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
|
||||||
import org.thoughtcrime.securesms.util.livedata.LiveDataUtil;
|
import org.thoughtcrime.securesms.util.livedata.LiveDataUtil;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -50,6 +53,8 @@ final class ConversationGroupViewModel extends ViewModel {
|
||||||
private final LiveData<List<RecipientId>> gv1MigrationSuggestions;
|
private final LiveData<List<RecipientId>> gv1MigrationSuggestions;
|
||||||
private final LiveData<Boolean> gv1MigrationReminder;
|
private final LiveData<Boolean> gv1MigrationReminder;
|
||||||
|
|
||||||
|
private boolean firstTimeInviteFriendsTriggered;
|
||||||
|
|
||||||
private ConversationGroupViewModel() {
|
private ConversationGroupViewModel() {
|
||||||
this.liveRecipient = new MutableLiveData<>();
|
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 {
|
static final class ReviewState {
|
||||||
|
|
||||||
private static final ReviewState EMPTY = new ReviewState(null, Recipient.UNKNOWN, 0);
|
private static final ReviewState EMPTY = new ReviewState(null, Recipient.UNKNOWN, 0);
|
||||||
|
|
|
@ -18,15 +18,16 @@ import java.util.Objects;
|
||||||
|
|
||||||
public class ConversationIntents {
|
public class ConversationIntents {
|
||||||
|
|
||||||
private static final String BUBBLE_AUTHORITY = "bubble";
|
private static final String BUBBLE_AUTHORITY = "bubble";
|
||||||
private static final String EXTRA_RECIPIENT = "recipient_id";
|
private static final String EXTRA_RECIPIENT = "recipient_id";
|
||||||
private static final String EXTRA_THREAD_ID = "thread_id";
|
private static final String EXTRA_THREAD_ID = "thread_id";
|
||||||
private static final String EXTRA_TEXT = "draft_text";
|
private static final String EXTRA_TEXT = "draft_text";
|
||||||
private static final String EXTRA_MEDIA = "media_list";
|
private static final String EXTRA_MEDIA = "media_list";
|
||||||
private static final String EXTRA_STICKER = "sticker_extra";
|
private static final String EXTRA_STICKER = "sticker_extra";
|
||||||
private static final String EXTRA_BORDERLESS = "borderless_extra";
|
private static final String EXTRA_BORDERLESS = "borderless_extra";
|
||||||
private static final String EXTRA_DISTRIBUTION_TYPE = "distribution_type";
|
private static final String EXTRA_DISTRIBUTION_TYPE = "distribution_type";
|
||||||
private static final String EXTRA_STARTING_POSITION = "starting_position";
|
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() {
|
private ConversationIntents() {
|
||||||
}
|
}
|
||||||
|
@ -63,7 +64,8 @@ public class ConversationIntents {
|
||||||
private final StickerLocator stickerLocator;
|
private final StickerLocator stickerLocator;
|
||||||
private final boolean isBorderless;
|
private final boolean isBorderless;
|
||||||
private final int distributionType;
|
private final int distributionType;
|
||||||
private final int startingPosition;
|
private final int startingPosition;
|
||||||
|
private final boolean firstTimeInSelfCreatedGroup;
|
||||||
|
|
||||||
static Args from(@NonNull Intent intent) {
|
static Args from(@NonNull Intent intent) {
|
||||||
if (isBubbleIntent(intent)) {
|
if (isBubbleIntent(intent)) {
|
||||||
|
@ -74,7 +76,8 @@ public class ConversationIntents {
|
||||||
null,
|
null,
|
||||||
false,
|
false,
|
||||||
ThreadDatabase.DistributionTypes.DEFAULT,
|
ThreadDatabase.DistributionTypes.DEFAULT,
|
||||||
-1);
|
-1,
|
||||||
|
false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Args(RecipientId.from(Objects.requireNonNull(intent.getStringExtra(EXTRA_RECIPIENT))),
|
return new Args(RecipientId.from(Objects.requireNonNull(intent.getStringExtra(EXTRA_RECIPIENT))),
|
||||||
|
@ -84,7 +87,8 @@ public class ConversationIntents {
|
||||||
intent.getParcelableExtra(EXTRA_STICKER),
|
intent.getParcelableExtra(EXTRA_STICKER),
|
||||||
intent.getBooleanExtra(EXTRA_BORDERLESS, false),
|
intent.getBooleanExtra(EXTRA_BORDERLESS, false),
|
||||||
intent.getIntExtra(EXTRA_DISTRIBUTION_TYPE, ThreadDatabase.DistributionTypes.DEFAULT),
|
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,
|
private Args(@NonNull RecipientId recipientId,
|
||||||
|
@ -94,16 +98,18 @@ public class ConversationIntents {
|
||||||
@Nullable StickerLocator stickerLocator,
|
@Nullable StickerLocator stickerLocator,
|
||||||
boolean isBorderless,
|
boolean isBorderless,
|
||||||
int distributionType,
|
int distributionType,
|
||||||
int startingPosition)
|
int startingPosition,
|
||||||
|
boolean firstTimeInSelfCreatedGroup)
|
||||||
{
|
{
|
||||||
this.recipientId = recipientId;
|
this.recipientId = recipientId;
|
||||||
this.threadId = threadId;
|
this.threadId = threadId;
|
||||||
this.draftText = draftText;
|
this.draftText = draftText;
|
||||||
this.media = media;
|
this.media = media;
|
||||||
this.stickerLocator = stickerLocator;
|
this.stickerLocator = stickerLocator;
|
||||||
this.isBorderless = isBorderless;
|
this.isBorderless = isBorderless;
|
||||||
this.distributionType = distributionType;
|
this.distributionType = distributionType;
|
||||||
this.startingPosition = startingPosition;
|
this.startingPosition = startingPosition;
|
||||||
|
this.firstTimeInSelfCreatedGroup = firstTimeInSelfCreatedGroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
public @NonNull RecipientId getRecipientId() {
|
public @NonNull RecipientId getRecipientId() {
|
||||||
|
@ -137,6 +143,10 @@ public class ConversationIntents {
|
||||||
public boolean isBorderless() {
|
public boolean isBorderless() {
|
||||||
return isBorderless;
|
return isBorderless;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isFirstTimeInSelfCreatedGroup() {
|
||||||
|
return firstTimeInSelfCreatedGroup;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final static class Builder {
|
public final static class Builder {
|
||||||
|
@ -153,6 +163,7 @@ public class ConversationIntents {
|
||||||
private int startingPosition = -1;
|
private int startingPosition = -1;
|
||||||
private Uri dataUri;
|
private Uri dataUri;
|
||||||
private String dataType;
|
private String dataType;
|
||||||
|
private boolean firstTimeInSelfCreatedGroup;
|
||||||
|
|
||||||
private Builder(@NonNull Context context,
|
private Builder(@NonNull Context context,
|
||||||
@NonNull RecipientId recipientId,
|
@NonNull RecipientId recipientId,
|
||||||
|
@ -212,6 +223,11 @@ public class ConversationIntents {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Builder firstTimeInSelfCreatedGroup() {
|
||||||
|
this.firstTimeInSelfCreatedGroup = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public @NonNull Intent build() {
|
public @NonNull Intent build() {
|
||||||
if (stickerLocator != null && media != null) {
|
if (stickerLocator != null && media != null) {
|
||||||
throw new IllegalStateException("Cannot have both sticker and media array");
|
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_DISTRIBUTION_TYPE, distributionType);
|
||||||
intent.putExtra(EXTRA_STARTING_POSITION, startingPosition);
|
intent.putExtra(EXTRA_STARTING_POSITION, startingPosition);
|
||||||
intent.putExtra(EXTRA_BORDERLESS, isBorderless);
|
intent.putExtra(EXTRA_BORDERLESS, isBorderless);
|
||||||
|
intent.putExtra(EXTRA_FIRST_TIME_IN_SELF_CREATED_GROUP, firstTimeInSelfCreatedGroup);
|
||||||
|
|
||||||
if (draftText != null) {
|
if (draftText != null) {
|
||||||
intent.putExtra(EXTRA_TEXT, draftText);
|
intent.putExtra(EXTRA_TEXT, draftText);
|
||||||
|
|
|
@ -238,8 +238,7 @@ public final class ConversationUpdateItem extends LinearLayout
|
||||||
actionButton.setVisibility(VISIBLE);
|
actionButton.setVisibility(VISIBLE);
|
||||||
actionButton.setOnClickListener(v -> {
|
actionButton.setOnClickListener(v -> {
|
||||||
if (batchSelected.isEmpty() && eventListener != null) {
|
if (batchSelected.isEmpty() && eventListener != null) {
|
||||||
// TODO [alan]
|
eventListener.onInviteFriendsToGroupClicked(conversationRecipient.requireGroupId().requireV2());
|
||||||
Log.i(TAG, "TODO");
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -15,6 +15,7 @@ import org.signal.zkgroup.groups.GroupMasterKey;
|
||||||
import org.signal.zkgroup.groups.UuidCiphertext;
|
import org.signal.zkgroup.groups.UuidCiphertext;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||||
|
import org.thoughtcrime.securesms.groups.v2.GroupInviteLinkUrl;
|
||||||
import org.thoughtcrime.securesms.groups.v2.GroupLinkPassword;
|
import org.thoughtcrime.securesms.groups.v2.GroupLinkPassword;
|
||||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||||
import org.thoughtcrime.securesms.profiles.AvatarHelper;
|
import org.thoughtcrime.securesms.profiles.AvatarHelper;
|
||||||
|
@ -300,13 +301,13 @@ public final class GroupManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
public static void setGroupLinkEnabledState(@NonNull Context context,
|
public static GroupInviteLinkUrl setGroupLinkEnabledState(@NonNull Context context,
|
||||||
@NonNull GroupId.V2 groupId,
|
@NonNull GroupId.V2 groupId,
|
||||||
@NonNull GroupLinkState state)
|
@NonNull GroupLinkState state)
|
||||||
throws GroupChangeFailedException, GroupInsufficientRightsException, IOException, GroupNotAMemberException, GroupChangeBusyException
|
throws GroupChangeFailedException, GroupInsufficientRightsException, IOException, GroupNotAMemberException, GroupChangeBusyException
|
||||||
{
|
{
|
||||||
try (GroupManagerV2.GroupEditor editor = new GroupManagerV2(context).edit(groupId.requireV2())) {
|
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.database.model.databaseprotos.DecryptedGroupV2Context;
|
||||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
import org.thoughtcrime.securesms.groups.v2.GroupCandidateHelper;
|
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.GroupLinkPassword;
|
||||||
import org.thoughtcrime.securesms.groups.v2.processing.GroupsV2StateProcessor;
|
import org.thoughtcrime.securesms.groups.v2.processing.GroupsV2StateProcessor;
|
||||||
import org.thoughtcrime.securesms.jobs.ProfileUploadJob;
|
import org.thoughtcrime.securesms.jobs.ProfileUploadJob;
|
||||||
|
@ -507,7 +508,7 @@ final class GroupManagerV2 {
|
||||||
}
|
}
|
||||||
|
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
public GroupManager.GroupActionResult setJoinByGroupLinkState(@NonNull GroupManager.GroupLinkState state)
|
public @Nullable GroupInviteLinkUrl setJoinByGroupLinkState(@NonNull GroupManager.GroupLinkState state)
|
||||||
throws GroupChangeFailedException, GroupNotAMemberException, GroupInsufficientRightsException, IOException
|
throws GroupChangeFailedException, GroupNotAMemberException, GroupInsufficientRightsException, IOException
|
||||||
{
|
{
|
||||||
AccessControl.AccessRequired access;
|
AccessControl.AccessRequired access;
|
||||||
|
@ -519,7 +520,7 @@ final class GroupManagerV2 {
|
||||||
default: throw new AssertionError();
|
default: throw new AssertionError();
|
||||||
}
|
}
|
||||||
|
|
||||||
GroupChange.Actions.Builder change = groupOperations.createChangeJoinByLinkRights(access);
|
GroupChange.Actions.Builder change = groupOperations.createChangeJoinByLinkRights(access);
|
||||||
|
|
||||||
if (state != GroupManager.GroupLinkState.DISABLED) {
|
if (state != GroupManager.GroupLinkState.DISABLED) {
|
||||||
DecryptedGroup group = groupDatabase.requireGroup(groupId).requireV2GroupProperties().getDecryptedGroup();
|
DecryptedGroup group = groupDatabase.requireGroup(groupId).requireV2GroupProperties().getDecryptedGroup();
|
||||||
|
@ -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)
|
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) {
|
void goToConversation(@NonNull RecipientId recipientId, long threadId) {
|
||||||
Intent intent = ConversationIntents.createBuilder(this, recipientId, threadId)
|
Intent intent = ConversationIntents.createBuilder(this, recipientId, threadId)
|
||||||
|
.firstTimeInSelfCreatedGroup()
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
startActivity(intent);
|
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 {
|
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";
|
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_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>
|
<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 -->
|
<!-- GV2 Request confirmation dialog -->
|
||||||
<string name="RequestConfirmationDialog_add_s_to_the_group">Add “%1$s” to the group?</string>
|
<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>
|
<string name="RequestConfirmationDialog_deny_request_from_s">Deny request from “%1$s”?</string>
|
||||||
|
|
Loading…
Add table
Reference in a new issue