Add mentions for v2 group chats.

This commit is contained in:
Cody Henthorne
2020-08-05 16:45:52 -04:00
committed by Greyson Parrelli
parent 0bb9c1d650
commit b2d4c5d14b
90 changed files with 2279 additions and 372 deletions

View File

@@ -149,7 +149,7 @@ final class GroupManagerV1 {
avatarAttachment = new UriAttachment(avatarUri, MediaUtil.IMAGE_PNG, AttachmentDatabase.TRANSFER_PROGRESS_DONE, avatar.length, null, false, false, false, null, null, null, null, null);
}
OutgoingGroupUpdateMessage outgoingMessage = new OutgoingGroupUpdateMessage(groupRecipient, groupContext, avatarAttachment, System.currentTimeMillis(), 0, false, null, Collections.emptyList(), Collections.emptyList());
OutgoingGroupUpdateMessage outgoingMessage = new OutgoingGroupUpdateMessage(groupRecipient, groupContext, avatarAttachment, System.currentTimeMillis(), 0, false, null, Collections.emptyList(), Collections.emptyList(), Collections.emptyList());
long threadId = MessageSender.send(context, outgoingMessage, -1, false, null);
return new GroupActionResult(groupRecipient, threadId, newMemberCount, Collections.emptyList());
@@ -241,6 +241,7 @@ final class GroupManagerV1 {
false,
null,
Collections.emptyList(),
Collections.emptyList(),
Collections.emptyList()));
}
}

View File

@@ -519,6 +519,7 @@ final class GroupManagerV2 {
false,
null,
Collections.emptyList(),
Collections.emptyList(),
Collections.emptyList());
if (plainGroupChange != null && DecryptedGroupUtil.changeIsEmptyExceptForProfileKeyChanges(plainGroupChange)) {

View File

@@ -240,7 +240,7 @@ public final class GroupV1MessageProcessor {
MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(context);
RecipientId recipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(GroupId.v1orThrow(group.getGroupId()));
Recipient recipient = Recipient.resolved(recipientId);
OutgoingGroupUpdateMessage outgoingMessage = new OutgoingGroupUpdateMessage(recipient, storage, null, content.getTimestamp(), 0, false, null, Collections.emptyList(), Collections.emptyList());
OutgoingGroupUpdateMessage outgoingMessage = new OutgoingGroupUpdateMessage(recipient, storage, null, content.getTimestamp(), 0, false, null, Collections.emptyList(), Collections.emptyList(), Collections.emptyList());
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
long messageId = mmsDatabase.insertMessageOutbox(outgoingMessage, threadId, false, null);

View File

@@ -51,6 +51,7 @@ import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.recipients.ui.bottomsheet.RecipientBottomSheetDialogFragment;
import org.thoughtcrime.securesms.recipients.ui.notifications.CustomNotificationsDialogFragment;
import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.FeatureFlags;
import org.thoughtcrime.securesms.util.LifecycleCursorWrapper;
import org.thoughtcrime.securesms.util.views.LearnMoreTextView;
@@ -99,6 +100,8 @@ public class ManageGroupFragment extends LoggingFragment {
private TextView muteNotificationsUntilLabel;
private TextView customNotificationsButton;
private View customNotificationsRow;
private View mentionsRow;
private TextView mentionsValue;
private View toggleAllMembers;
private final Recipient.FallbackPhotoProvider fallbackPhotoProvider = new Recipient.FallbackPhotoProvider() {
@@ -156,6 +159,8 @@ public class ManageGroupFragment extends LoggingFragment {
muteNotificationsRow = view.findViewById(R.id.group_mute_notifications_row);
customNotificationsButton = view.findViewById(R.id.group_custom_notifications_button);
customNotificationsRow = view.findViewById(R.id.group_custom_notifications_row);
mentionsRow = view.findViewById(R.id.group_mentions_row);
mentionsValue = view.findViewById(R.id.group_mentions_value);
toggleAllMembers = view.findViewById(R.id.toggle_all_members);
groupV1Indicator.setOnLinkClickListener(v -> GroupsLearnMoreBottomSheetDialogFragment.show(requireFragmentManager()));
@@ -317,7 +322,6 @@ public class ManageGroupFragment extends LoggingFragment {
customNotificationsRow.setVisibility(View.VISIBLE);
//noinspection CodeBlock2Expr
if (NotificationChannels.supported()) {
viewModel.hasCustomNotifications().observe(getViewLifecycleOwner(), hasCustomNotifications -> {
customNotificationsButton.setText(hasCustomNotifications ? R.string.ManageGroupActivity_on
@@ -325,6 +329,10 @@ public class ManageGroupFragment extends LoggingFragment {
});
}
mentionsRow.setVisibility(FeatureFlags.mentions() && groupId.isV2() ? View.VISIBLE : View.GONE);
mentionsRow.setOnClickListener(v -> viewModel.handleMentionNotificationSelection());
viewModel.getMentionSetting().observe(getViewLifecycleOwner(), value -> mentionsValue.setText(value));
viewModel.getSnackbarEvents().observe(getViewLifecycleOwner(), this::handleSnackbarEvent);
viewModel.getInvitedDialogEvents().observe(getViewLifecycleOwner(), this::handleInvitedDialogEvent);

View File

@@ -12,6 +12,7 @@ import org.signal.storageservice.protos.groups.local.DecryptedGroup;
import org.thoughtcrime.securesms.ContactSelectionListFragment;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.groups.GroupAccessControl;
import org.thoughtcrime.securesms.groups.GroupChangeException;
@@ -152,6 +153,13 @@ final class ManageGroupRepository {
});
}
void setMentionSetting(RecipientDatabase.MentionSetting mentionSetting) {
SignalExecutors.BOUNDED.execute(() -> {
RecipientId recipientId = Recipient.externalGroup(context, groupId).getId();
DatabaseFactory.getRecipientDatabase(context).setMentionSetting(recipientId, mentionSetting);
});
}
static final class GroupStateResult {
private final long threadId;

View File

@@ -22,6 +22,8 @@ import org.thoughtcrime.securesms.ExpirationDialog;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader;
import org.thoughtcrime.securesms.database.MediaDatabase;
import org.thoughtcrime.securesms.database.MentionUtil;
import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.database.loaders.MediaLoader;
import org.thoughtcrime.securesms.database.loaders.ThreadMediaLoader;
import org.thoughtcrime.securesms.groups.GroupAccessControl;
@@ -31,6 +33,7 @@ import org.thoughtcrime.securesms.groups.ui.GroupChangeFailureReason;
import org.thoughtcrime.securesms.groups.ui.GroupErrors;
import org.thoughtcrime.securesms.groups.ui.GroupMemberEntry;
import org.thoughtcrime.securesms.groups.ui.addmembers.AddMembersActivity;
import org.thoughtcrime.securesms.groups.ui.managegroup.dialogs.GroupMentionSettingDialog;
import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
@@ -75,6 +78,7 @@ public class ManageGroupViewModel extends ViewModel {
private final LiveData<Boolean> canLeaveGroup;
private final LiveData<Boolean> canBlockGroup;
private final LiveData<Boolean> showLegacyIndicator;
private final LiveData<String> mentionSetting;
private ManageGroupViewModel(@NonNull Context context, @NonNull ManageGroupRepository manageGroupRepository) {
this.context = context;
@@ -114,6 +118,8 @@ public class ManageGroupViewModel extends ViewModel {
recipient -> recipient.getNotificationChannel() != null || !NotificationChannels.supported());
this.canLeaveGroup = liveGroup.isActive();
this.canBlockGroup = Transformations.map(this.groupRecipient, recipient -> !recipient.isBlocked());
this.mentionSetting = Transformations.distinctUntilChanged(Transformations.map(this.groupRecipient,
recipient -> MentionUtil.getMentionSettingDisplayValue(context, recipient.getMentionSetting())));
}
@WorkerThread
@@ -207,6 +213,10 @@ public class ManageGroupViewModel extends ViewModel {
return canLeaveGroup;
}
LiveData<String> getMentionSetting() {
return mentionSetting;
}
void handleExpirationSelection() {
manageGroupRepository.getRecipient(groupRecipient ->
ExpirationDialog.show(context,
@@ -250,6 +260,10 @@ public class ManageGroupViewModel extends ViewModel {
memberListCollapseState.setValue(CollapseState.OPEN);
}
void handleMentionNotificationSelection() {
manageGroupRepository.getRecipient(r -> GroupMentionSettingDialog.show(context, r.getMentionSetting(), mentionSetting -> manageGroupRepository.setMentionSetting(mentionSetting)));
}
private void onBlockAndLeaveConfirmed() {
SimpleProgressDialog.DismissibleDialog dismissibleDialog = SimpleProgressDialog.showDelayed(context);

View File

@@ -0,0 +1,90 @@
package org.thoughtcrime.securesms.groups.ui.managegroup.dialogs;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.DialogInterface;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.CheckedTextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.core.util.Consumer;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.RecipientDatabase.MentionSetting;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
public final class GroupMentionSettingDialog {
public static void show(@NonNull Context context, @NonNull MentionSetting mentionSetting, @Nullable Consumer<MentionSetting> callback) {
SelectionCallback selectionCallback = new SelectionCallback(mentionSetting, callback);
new AlertDialog.Builder(context)
.setTitle(R.string.GroupMentionSettingDialog_notify_me_for_mentions)
.setView(getView(context, mentionSetting, selectionCallback))
.setPositiveButton(android.R.string.ok, selectionCallback)
.setNegativeButton(android.R.string.cancel, null)
.show();
}
@SuppressLint("InflateParams")
private static View getView(@NonNull Context context, @NonNull MentionSetting mentionSetting, @NonNull SelectionCallback selectionCallback) {
View root = LayoutInflater.from(context).inflate(R.layout.group_mention_setting_dialog, null, false);
CheckedTextView defaultOption = root.findViewById(R.id.group_mention_setting_default);
CheckedTextView alwaysNotify = root.findViewById(R.id.group_mention_setting_always_notify);
CheckedTextView dontNotify = root.findViewById(R.id.group_mention_setting_dont_notify);
defaultOption.setText(SignalStore.notificationSettings().isMentionNotifiesMeEnabled() ? R.string.GroupMentionSettingDialog_default_notify_me
: R.string.GroupMentionSettingDialog_default_dont_notify_me);
View.OnClickListener listener = (v) -> {
defaultOption.setChecked(defaultOption == v);
alwaysNotify.setChecked(alwaysNotify == v);
dontNotify.setChecked(dontNotify == v);
if (defaultOption.isChecked()) selectionCallback.selection = MentionSetting.GLOBAL;
else if (alwaysNotify.isChecked()) selectionCallback.selection = MentionSetting.ALWAYS_NOTIFY;
else if (dontNotify.isChecked()) selectionCallback.selection = MentionSetting.DO_NOT_NOTIFY;
};
defaultOption.setOnClickListener(listener);
alwaysNotify.setOnClickListener(listener);
dontNotify.setOnClickListener(listener);
switch (mentionSetting) {
case GLOBAL:
listener.onClick(defaultOption);
break;
case ALWAYS_NOTIFY:
listener.onClick(alwaysNotify);
break;
case DO_NOT_NOTIFY:
listener.onClick(dontNotify);
break;
}
return root;
}
private static class SelectionCallback implements DialogInterface.OnClickListener {
@NonNull private final MentionSetting previousMentionSetting;
@NonNull private MentionSetting selection;
@Nullable private final Consumer<MentionSetting> callback;
public SelectionCallback(@NonNull MentionSetting previousMentionSetting, @Nullable Consumer<MentionSetting> callback) {
this.previousMentionSetting = previousMentionSetting;
this.selection = previousMentionSetting;
this.callback = callback;
}
@Override
public void onClick(DialogInterface dialog, int which) {
if (callback != null && selection != previousMentionSetting) {
callback.accept(selection);
}
}
}
}

View File

@@ -232,6 +232,7 @@ public final class GroupsV2StateProcessor {
false,
null,
Collections.emptyList(),
Collections.emptyList(),
Collections.emptyList());
try {
@@ -397,7 +398,7 @@ public final class GroupsV2StateProcessor {
MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(context);
RecipientId recipientId = recipientDatabase.getOrInsertFromGroupId(groupId);
Recipient recipient = Recipient.resolved(recipientId);
OutgoingGroupUpdateMessage outgoingMessage = new OutgoingGroupUpdateMessage(recipient, decryptedGroupV2Context, null, timestamp, 0, false, null, Collections.emptyList(), Collections.emptyList());
OutgoingGroupUpdateMessage outgoingMessage = new OutgoingGroupUpdateMessage(recipient, decryptedGroupV2Context, null, timestamp, 0, false, null, Collections.emptyList(), Collections.emptyList(), Collections.emptyList());
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
long messageId = mmsDatabase.insertMessageOutbox(outgoingMessage, threadId, false, null);