2014-02-03 04:38:06 +01:00
|
|
|
package org.thoughtcrime.securesms.util;
|
|
|
|
|
2014-12-15 15:44:41 +01:00
|
|
|
import android.content.Context;
|
2019-09-07 05:40:06 +02:00
|
|
|
|
2019-06-05 21:47:14 +02:00
|
|
|
import androidx.annotation.NonNull;
|
|
|
|
import androidx.annotation.Nullable;
|
|
|
|
import androidx.annotation.WorkerThread;
|
2018-09-10 17:40:00 +02:00
|
|
|
|
|
|
|
import com.google.protobuf.ByteString;
|
2014-02-17 20:42:51 +01:00
|
|
|
|
2015-09-17 02:31:24 +02:00
|
|
|
import org.thoughtcrime.securesms.R;
|
2018-09-10 17:40:00 +02:00
|
|
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
|
|
|
import org.thoughtcrime.securesms.database.GroupDatabase;
|
2020-03-26 15:00:17 +01:00
|
|
|
import org.thoughtcrime.securesms.groups.GroupId;
|
2018-08-01 17:09:24 +02:00
|
|
|
import org.thoughtcrime.securesms.logging.Log;
|
2018-09-10 17:40:00 +02:00
|
|
|
import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
|
2016-04-05 16:33:40 +02:00
|
|
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
2019-08-07 20:22:51 +02:00
|
|
|
import org.thoughtcrime.securesms.recipients.RecipientForeverObserver;
|
2018-09-10 17:40:00 +02:00
|
|
|
import org.whispersystems.libsignal.util.guava.Optional;
|
2020-03-27 19:12:10 +01:00
|
|
|
import org.whispersystems.signalservice.api.messages.SignalServiceGroupContext;
|
2019-09-07 05:40:06 +02:00
|
|
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
|
|
|
import org.whispersystems.signalservice.api.util.UuidUtil;
|
2014-02-17 20:42:51 +01:00
|
|
|
|
2014-02-03 04:38:06 +01:00
|
|
|
import java.io.IOException;
|
2018-09-10 17:40:00 +02:00
|
|
|
import java.util.Collections;
|
2017-07-26 18:59:15 +02:00
|
|
|
import java.util.LinkedList;
|
|
|
|
import java.util.List;
|
2014-02-03 04:38:06 +01:00
|
|
|
|
2016-03-23 18:34:41 +01:00
|
|
|
import static org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext;
|
2014-02-15 00:59:57 +01:00
|
|
|
|
2020-03-26 15:00:17 +01:00
|
|
|
public final class GroupUtil {
|
2014-02-03 04:38:06 +01:00
|
|
|
|
2020-03-26 15:00:17 +01:00
|
|
|
private GroupUtil() {
|
2014-02-03 04:38:06 +01:00
|
|
|
}
|
|
|
|
|
2020-03-26 15:00:17 +01:00
|
|
|
private static final String TAG = Log.tag(GroupUtil.class);
|
2014-02-03 04:38:06 +01:00
|
|
|
|
2020-03-27 19:12:10 +01:00
|
|
|
/**
|
|
|
|
* Result may be a v1 or v2 GroupId.
|
|
|
|
*/
|
|
|
|
public static GroupId idFromGroupContext(@NonNull SignalServiceGroupContext groupContext) {
|
|
|
|
if (groupContext.getGroupV1().isPresent()) {
|
|
|
|
return GroupId.v1(groupContext.getGroupV1().get().getGroupId());
|
|
|
|
} else if (groupContext.getGroupV2().isPresent()) {
|
|
|
|
return GroupId.v2(groupContext.getGroupV2().get().getMasterKey());
|
|
|
|
} else {
|
|
|
|
throw new AssertionError();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Result may be a v1 or v2 GroupId.
|
|
|
|
*/
|
|
|
|
public static @NonNull Optional<GroupId> idFromGroupContext(@NonNull Optional<SignalServiceGroupContext> groupContext) {
|
|
|
|
return groupContext.transform(GroupUtil::idFromGroupContext);
|
|
|
|
}
|
|
|
|
|
2018-09-10 17:40:00 +02:00
|
|
|
@WorkerThread
|
|
|
|
public static Optional<OutgoingGroupMediaMessage> createGroupLeaveMessage(@NonNull Context context, @NonNull Recipient groupRecipient) {
|
2020-03-26 15:00:17 +01:00
|
|
|
GroupId encodedGroupId = groupRecipient.requireGroupId();
|
2018-09-10 17:40:00 +02:00
|
|
|
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
|
|
|
|
|
|
|
|
if (!groupDatabase.isActive(encodedGroupId)) {
|
|
|
|
Log.w(TAG, "Group has already been left.");
|
|
|
|
return Optional.absent();
|
|
|
|
}
|
|
|
|
|
2020-03-26 15:00:17 +01:00
|
|
|
ByteString decodedGroupId = ByteString.copyFrom(encodedGroupId.getDecodedId());
|
2018-09-10 17:40:00 +02:00
|
|
|
|
|
|
|
GroupContext groupContext = GroupContext.newBuilder()
|
|
|
|
.setId(decodedGroupId)
|
|
|
|
.setType(GroupContext.Type.QUIT)
|
|
|
|
.build();
|
|
|
|
|
2019-08-01 01:33:56 +02:00
|
|
|
return Optional.of(new OutgoingGroupMediaMessage(groupRecipient, groupContext, null, System.currentTimeMillis(), 0, false, null, Collections.emptyList(), Collections.emptyList()));
|
2018-09-10 17:40:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-09-17 02:31:24 +02:00
|
|
|
public static @NonNull GroupDescription getDescription(@NonNull Context context, @Nullable String encodedGroup) {
|
2014-02-22 02:51:25 +01:00
|
|
|
if (encodedGroup == null) {
|
2015-09-17 02:31:24 +02:00
|
|
|
return new GroupDescription(context, null);
|
2014-02-22 02:51:25 +01:00
|
|
|
}
|
2014-02-18 07:20:43 +01:00
|
|
|
|
2014-02-22 02:51:25 +01:00
|
|
|
try {
|
2014-12-29 23:32:29 +01:00
|
|
|
GroupContext groupContext = GroupContext.parseFrom(Base64.decode(encodedGroup));
|
2015-09-17 02:31:24 +02:00
|
|
|
return new GroupDescription(context, groupContext);
|
|
|
|
} catch (IOException e) {
|
|
|
|
Log.w(TAG, e);
|
|
|
|
return new GroupDescription(context, null);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public static class GroupDescription {
|
|
|
|
|
|
|
|
@NonNull private final Context context;
|
|
|
|
@Nullable private final GroupContext groupContext;
|
2017-08-01 17:56:00 +02:00
|
|
|
@Nullable private final List<Recipient> members;
|
2015-09-17 02:31:24 +02:00
|
|
|
|
|
|
|
public GroupDescription(@NonNull Context context, @Nullable GroupContext groupContext) {
|
|
|
|
this.context = context.getApplicationContext();
|
|
|
|
this.groupContext = groupContext;
|
|
|
|
|
|
|
|
if (groupContext == null || groupContext.getMembersList().isEmpty()) {
|
|
|
|
this.members = null;
|
|
|
|
} else {
|
2017-08-01 17:56:00 +02:00
|
|
|
this.members = new LinkedList<>();
|
2017-07-26 18:59:15 +02:00
|
|
|
|
2019-09-07 05:40:06 +02:00
|
|
|
for (GroupContext.Member member : groupContext.getMembersList()) {
|
2020-01-04 18:41:18 +01:00
|
|
|
Recipient recipient = Recipient.externalPush(context, new SignalServiceAddress(UuidUtil.parseOrNull(member.getUuid()), member.getE164()));
|
|
|
|
if (!recipient.isLocalNumber()) {
|
|
|
|
this.members.add(recipient);
|
|
|
|
}
|
2017-07-26 18:59:15 +02:00
|
|
|
}
|
2015-09-17 02:31:24 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-05 16:33:40 +02:00
|
|
|
public String toString(Recipient sender) {
|
|
|
|
StringBuilder description = new StringBuilder();
|
2019-10-31 17:20:55 +01:00
|
|
|
description.append(context.getString(R.string.MessageRecord_s_updated_group, sender.toShortString(context)));
|
2016-04-05 16:33:40 +02:00
|
|
|
|
2015-09-17 02:31:24 +02:00
|
|
|
if (groupContext == null) {
|
2016-04-05 16:33:40 +02:00
|
|
|
return description.toString();
|
2015-09-17 02:31:24 +02:00
|
|
|
}
|
2014-02-17 20:42:51 +01:00
|
|
|
|
2016-04-05 16:33:40 +02:00
|
|
|
String title = groupContext.getName();
|
2015-09-17 02:31:24 +02:00
|
|
|
|
2020-01-07 00:36:56 +01:00
|
|
|
if (members != null && members.size() > 0) {
|
2016-04-05 16:33:40 +02:00
|
|
|
description.append("\n");
|
2015-11-14 13:30:33 +01:00
|
|
|
description.append(context.getResources().getQuantityString(R.plurals.GroupUtil_joined_the_group,
|
2017-08-01 17:56:00 +02:00
|
|
|
members.size(), toString(members)));
|
2014-02-22 02:51:25 +01:00
|
|
|
}
|
2014-02-17 20:42:51 +01:00
|
|
|
|
2014-02-22 02:51:25 +01:00
|
|
|
if (title != null && !title.trim().isEmpty()) {
|
2016-04-05 16:33:40 +02:00
|
|
|
if (members != null) description.append(" ");
|
|
|
|
else description.append("\n");
|
2015-06-13 23:16:02 +02:00
|
|
|
description.append(context.getString(R.string.GroupUtil_group_name_is_now, title));
|
2014-02-22 02:51:25 +01:00
|
|
|
}
|
2014-02-15 00:59:57 +01:00
|
|
|
|
2016-04-05 16:33:40 +02:00
|
|
|
return description.toString();
|
2015-09-17 02:31:24 +02:00
|
|
|
}
|
|
|
|
|
2019-08-07 20:22:51 +02:00
|
|
|
public void addObserver(RecipientForeverObserver listener) {
|
2015-09-17 02:31:24 +02:00
|
|
|
if (this.members != null) {
|
2017-08-01 17:56:00 +02:00
|
|
|
for (Recipient member : this.members) {
|
2019-08-07 20:22:51 +02:00
|
|
|
member.live().observeForever(listener);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void removeObserver(RecipientForeverObserver listener) {
|
|
|
|
if (this.members != null) {
|
|
|
|
for (Recipient member : this.members) {
|
|
|
|
member.live().removeForeverObserver(listener);
|
2017-08-01 17:56:00 +02:00
|
|
|
}
|
2015-09-17 02:31:24 +02:00
|
|
|
}
|
2014-02-17 20:42:51 +01:00
|
|
|
}
|
2017-08-01 17:56:00 +02:00
|
|
|
|
|
|
|
private String toString(List<Recipient> recipients) {
|
|
|
|
String result = "";
|
|
|
|
|
|
|
|
for (int i=0;i<recipients.size();i++) {
|
2019-10-31 17:20:55 +01:00
|
|
|
result += recipients.get(i).toShortString(context);
|
2017-08-01 17:56:00 +02:00
|
|
|
|
|
|
|
if (i != recipients.size() -1 )
|
|
|
|
result += ", ";
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
2014-02-15 00:59:57 +01:00
|
|
|
}
|
2014-02-03 04:38:06 +01:00
|
|
|
}
|