GroupId class.

This commit is contained in:
Alan Evans 2020-03-26 11:00:17 -03:00 committed by Greyson Parrelli
parent a73a73e42c
commit a860315587
43 changed files with 519 additions and 365 deletions

View file

@ -54,6 +54,7 @@ import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.groups.GroupManager;
import org.thoughtcrime.securesms.groups.GroupManager.GroupActionResult;
import org.thoughtcrime.securesms.logging.Log;
@ -208,7 +209,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
}
private void initializeExistingGroup() {
final String groupId = getIntent().getStringExtra(GROUP_ID_EXTRA);
final GroupId groupId = GroupId.parseNullable(getIntent().getStringExtra(GROUP_ID_EXTRA));
if (groupId != null) {
new FillExistingGroupInfoAsyncTask(this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, groupId);
@ -361,7 +362,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
}
memberAddresses.add(Recipient.self().getId());
String groupId = DatabaseFactory.getGroupDatabase(activity).getOrCreateGroupForMembers(memberAddresses, true);
GroupId groupId = DatabaseFactory.getGroupDatabase(activity).getOrCreateGroupForMembers(memberAddresses, true);
RecipientId groupRecipientId = DatabaseFactory.getRecipientDatabase(activity).getOrInsertFromGroupId(groupId);
Recipient groupRecipient = Recipient.resolved(groupRecipientId);
long threadId = DatabaseFactory.getThreadDatabase(activity).getThreadIdFor(groupRecipient, ThreadDatabase.DistributionTypes.DEFAULT);
@ -443,9 +444,9 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
}
private static class UpdateSignalGroupTask extends SignalGroupTask {
private String groupId;
private final GroupId groupId;
public UpdateSignalGroupTask(GroupCreateActivity activity, String groupId,
public UpdateSignalGroupTask(GroupCreateActivity activity, GroupId groupId,
Bitmap avatar, String name, Set<Recipient> members)
{
super(activity, avatar, name, members);
@ -467,7 +468,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
if (!activity.isFinishing()) {
Intent intent = activity.getIntent();
intent.putExtra(GROUP_THREAD_EXTRA, result.get().getThreadId());
intent.putExtra(GROUP_ID_EXTRA, result.get().getGroupRecipient().requireGroupId());
intent.putExtra(GROUP_ID_EXTRA, result.get().getGroupRecipient().requireGroupId().toString());
activity.setResult(RESULT_OK, intent);
activity.finish();
}
@ -534,7 +535,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
}
}
private static class FillExistingGroupInfoAsyncTask extends ProgressDialogAsyncTask<String,Void,Optional<GroupData>> {
private static class FillExistingGroupInfoAsyncTask extends ProgressDialogAsyncTask<GroupId, Void, Optional<GroupData>> {
private GroupCreateActivity activity;
public FillExistingGroupInfoAsyncTask(GroupCreateActivity activity) {
@ -545,7 +546,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
}
@Override
protected Optional<GroupData> doInBackground(String... groupIds) {
protected Optional<GroupData> doInBackground(GroupId... groupIds) {
final GroupDatabase db = DatabaseFactory.getGroupDatabase(activity);
final List<Recipient> recipients = db.getGroupMembers(groupIds[0], false);
final Optional<GroupRecord> group = db.getGroup(groupIds[0]);
@ -593,13 +594,13 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
}
private static class GroupData {
String id;
GroupId id;
Set<Recipient> recipients;
Bitmap avatarBmp;
byte[] avatarBytes;
String name;
public GroupData(String id, Set<Recipient> recipients, Bitmap avatarBmp, byte[] avatarBytes, String name) {
GroupData(GroupId id, Set<Recipient> recipients, Bitmap avatarBmp, byte[] avatarBytes, String name) {
this.id = id;
this.recipients = recipients;
this.avatarBmp = avatarBmp;

View file

@ -202,7 +202,7 @@ public class ContactAccessor {
reader = DatabaseFactory.getGroupDatabase(context).getGroupsFilteredByTitle(constraint, true);
while ((record = reader.getNext()) != null) {
numberList.add(record.getEncodedId());
numberList.add(record.getId().toString());
}
} finally {
if (reader != null)

View file

@ -2,24 +2,24 @@ package org.thoughtcrime.securesms.contacts;
import android.annotation.SuppressLint;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
import android.widget.CheckBox;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.AvatarImageView;
import org.thoughtcrime.securesms.components.FromTextView;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.recipients.LiveRecipient;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientForeverObserver;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.whispersystems.libsignal.util.guava.Optional;
@ -106,7 +106,7 @@ public class ContactSelectionListItem extends LinearLayout implements RecipientF
@SuppressLint("SetTextI18n")
private void setText(@Nullable Recipient recipient, int type, String name, String number, String label) {
if (number == null || number.isEmpty() || GroupUtil.isEncodedGroup(number)) {
if (number == null || number.isEmpty() || GroupId.isEncodedGroup(number)) {
this.nameView.setEnabled(false);
this.numberView.setText("");
this.labelView.setVisibility(View.GONE);

View file

@ -22,10 +22,11 @@ import android.database.Cursor;
import android.database.MatrixCursor;
import android.database.MergeCursor;
import android.provider.ContactsContract;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.loader.content.CursorLoader;
import android.text.TextUtils;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.DatabaseFactory;
@ -35,8 +36,8 @@ import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.phonenumbers.NumberUtil;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.FeatureFlags;
import org.thoughtcrime.securesms.util.UsernameUtil;
@ -226,7 +227,7 @@ public class ContactsCursorLoader extends CursorLoader {
ThreadRecord threadRecord;
while ((threadRecord = reader.getNext()) != null) {
Recipient recipient = threadRecord.getRecipient();
String stringId = recipient.isGroup() ? recipient.requireGroupId() : recipient.getE164().or(recipient.getEmail()).or("");
String stringId = recipient.isGroup() ? recipient.requireGroupId().toString() : recipient.getE164().or(recipient.getEmail()).or("");
recentConversations.addRow(new Object[] { recipient.getId().serialize(),
recipient.toShortString(getContext()),
@ -265,7 +266,7 @@ public class ContactsCursorLoader extends CursorLoader {
while ((groupRecord = reader.getNext()) != null) {
groupContacts.addRow(new Object[] { groupRecord.getRecipientId().serialize(),
groupRecord.getTitle(),
groupRecord.getEncodedId(),
groupRecord.getId(),
ContactsContract.CommonDataKinds.Phone.TYPE_CUSTOM,
"",
ContactRepository.NORMAL_TYPE });

View file

@ -3,11 +3,13 @@ package org.thoughtcrime.securesms.contacts.avatars;
import android.content.Context;
import android.net.Uri;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.util.Conversions;
import org.whispersystems.libsignal.util.guava.Optional;
@ -16,12 +18,12 @@ import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
public class GroupRecordContactPhoto implements ContactPhoto {
public final class GroupRecordContactPhoto implements ContactPhoto {
private final String groupId;
private final long avatarId;
private final GroupId groupId;
private final long avatarId;
public GroupRecordContactPhoto(@NonNull String groupId, long avatarId) {
public GroupRecordContactPhoto(@NonNull GroupId groupId, long avatarId) {
this.groupId = groupId;
this.avatarId = avatarId;
}
@ -50,13 +52,13 @@ public class GroupRecordContactPhoto implements ContactPhoto {
@Override
public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) {
messageDigest.update(groupId.getBytes());
messageDigest.update(groupId.toString().getBytes());
messageDigest.update(Conversions.longToByteArray(avatarId));
}
@Override
public boolean equals(Object other) {
if (other == null || !(other instanceof GroupRecordContactPhoto)) return false;
if (!(other instanceof GroupRecordContactPhoto)) return false;
GroupRecordContactPhoto that = (GroupRecordContactPhoto)other;
return this.groupId.equals(that.groupId) && this.avatarId == that.avatarId;

View file

@ -1140,7 +1140,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private void handleEditPushGroup() {
Intent intent = new Intent(ConversationActivity.this, GroupCreateActivity.class);
intent.putExtra(GroupCreateActivity.GROUP_ID_EXTRA, recipient.get().requireGroupId());
intent.putExtra(GroupCreateActivity.GROUP_ID_EXTRA, recipient.get().requireGroupId().toString());
startActivityForResult(intent, GROUP_EDIT);
}

View file

@ -6,25 +6,25 @@ import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import android.text.TextUtils;
import com.annimon.stream.Stream;
import net.sqlcipher.database.SQLiteDatabase;
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.BitmapUtil;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer;
import java.io.Closeable;
import java.io.IOException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collections;
@ -95,9 +95,9 @@ public class GroupDatabase extends Database {
}
}
public Optional<GroupRecord> getGroup(String groupId) {
public Optional<GroupRecord> getGroup(@NonNull GroupId groupId) {
try (Cursor cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, null, GROUP_ID + " = ?",
new String[] {groupId},
new String[] {groupId.toString()},
null, null, null))
{
if (cursor != null && cursor.moveToNext()) {
@ -113,7 +113,7 @@ public class GroupDatabase extends Database {
return Optional.fromNullable(reader.getCurrent());
}
public boolean isUnknownGroup(String groupId) {
public boolean isUnknownGroup(@NonNull GroupId groupId) {
Optional<GroupRecord> group = getGroup(groupId);
if (!group.isPresent()) {
@ -143,7 +143,7 @@ public class GroupDatabase extends Database {
return new Reader(cursor);
}
public String getOrCreateGroupForMembers(List<RecipientId> members, boolean mms) {
public GroupId getOrCreateGroupForMembers(List<RecipientId> members, boolean mms) {
Collections.sort(members);
Cursor cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, new String[] {GROUP_ID},
@ -152,9 +152,9 @@ public class GroupDatabase extends Database {
null, null, null);
try {
if (cursor != null && cursor.moveToNext()) {
return cursor.getString(cursor.getColumnIndexOrThrow(GROUP_ID));
return GroupId.parse(cursor.getString(cursor.getColumnIndexOrThrow(GROUP_ID)));
} else {
String groupId = GroupUtil.getEncodedId(allocateGroupId(), mms);
GroupId groupId = allocateGroupId(mms);
create(groupId, null, members, null, null);
return groupId;
}
@ -197,7 +197,7 @@ public class GroupDatabase extends Database {
return new Reader(cursor);
}
public @NonNull List<Recipient> getGroupMembers(String groupId, boolean includeSelf) {
public @NonNull List<Recipient> getGroupMembers(@NonNull GroupId groupId, boolean includeSelf) {
List<RecipientId> members = getCurrentMembers(groupId);
List<Recipient> recipients = new LinkedList<>();
@ -212,14 +212,14 @@ public class GroupDatabase extends Database {
return recipients;
}
public void create(@NonNull String groupId, @Nullable String title, @NonNull List<RecipientId> members,
public void create(@NonNull GroupId groupId, @Nullable String title, @NonNull List<RecipientId> members,
@Nullable SignalServiceAttachmentPointer avatar, @Nullable String relay)
{
Collections.sort(members);
ContentValues contentValues = new ContentValues();
contentValues.put(RECIPIENT_ID, DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(groupId).serialize());
contentValues.put(GROUP_ID, groupId);
contentValues.put(GROUP_ID, groupId.toString());
contentValues.put(TITLE, title);
contentValues.put(MEMBERS, RecipientId.toSerializedList(members));
@ -233,7 +233,7 @@ public class GroupDatabase extends Database {
contentValues.put(AVATAR_RELAY, relay);
contentValues.put(TIMESTAMP, System.currentTimeMillis());
contentValues.put(ACTIVE, 1);
contentValues.put(MMS, GroupUtil.isMmsGroup(groupId));
contentValues.put(MMS, groupId.isMmsGroup());
databaseHelper.getWritableDatabase().insert(TABLE_NAME, null, contentValues);
@ -243,7 +243,7 @@ public class GroupDatabase extends Database {
notifyConversationListListeners();
}
public void update(String groupId, String title, SignalServiceAttachmentPointer avatar) {
public void update(@NonNull GroupId groupId, String title, SignalServiceAttachmentPointer avatar) {
ContentValues contentValues = new ContentValues();
if (title != null) contentValues.put(TITLE, title);
@ -256,7 +256,7 @@ public class GroupDatabase extends Database {
databaseHelper.getWritableDatabase().update(TABLE_NAME, contentValues,
GROUP_ID + " = ?",
new String[] {groupId});
new String[] {groupId.toString()});
RecipientId groupRecipient = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(groupId);
Recipient.live(groupRecipient).refresh();
@ -264,21 +264,21 @@ public class GroupDatabase extends Database {
notifyConversationListListeners();
}
public void updateTitle(String groupId, String title) {
public void updateTitle(@NonNull GroupId groupId, String title) {
ContentValues contentValues = new ContentValues();
contentValues.put(TITLE, title);
databaseHelper.getWritableDatabase().update(TABLE_NAME, contentValues, GROUP_ID + " = ?",
new String[] {groupId});
new String[] {groupId.toString()});
RecipientId groupRecipient = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(groupId);
Recipient.live(groupRecipient).refresh();
}
public void updateAvatar(String groupId, Bitmap avatar) {
public void updateAvatar(@NonNull GroupId groupId, @Nullable Bitmap avatar) {
updateAvatar(groupId, BitmapUtil.toByteArray(avatar));
}
public void updateAvatar(String groupId, byte[] avatar) {
public void updateAvatar(@NonNull GroupId groupId, @Nullable byte[] avatar) {
long avatarId;
if (avatar != null) avatarId = Math.abs(new SecureRandom().nextLong());
@ -290,13 +290,13 @@ public class GroupDatabase extends Database {
contentValues.put(AVATAR_ID, avatarId);
databaseHelper.getWritableDatabase().update(TABLE_NAME, contentValues, GROUP_ID + " = ?",
new String[] {groupId});
new String[] {groupId.toString()});
RecipientId groupRecipient = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(groupId);
Recipient.live(groupRecipient).refresh();
}
public void updateMembers(String groupId, List<RecipientId> members) {
public void updateMembers(@NonNull GroupId groupId, List<RecipientId> members) {
Collections.sort(members);
ContentValues contents = new ContentValues();
@ -304,13 +304,13 @@ public class GroupDatabase extends Database {
contents.put(ACTIVE, 1);
databaseHelper.getWritableDatabase().update(TABLE_NAME, contents, GROUP_ID + " = ?",
new String[] {groupId});
new String[] {groupId.toString()});
RecipientId groupRecipient = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(groupId);
Recipient.live(groupRecipient).refresh();
}
public void remove(String groupId, RecipientId source) {
public void remove(@NonNull GroupId groupId, RecipientId source) {
List<RecipientId> currentMembers = getCurrentMembers(groupId);
currentMembers.remove(source);
@ -318,19 +318,19 @@ public class GroupDatabase extends Database {
contents.put(MEMBERS, RecipientId.toSerializedList(currentMembers));
databaseHelper.getWritableDatabase().update(TABLE_NAME, contents, GROUP_ID + " = ?",
new String[] {groupId});
new String[] {groupId.toString()});
RecipientId groupRecipient = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(groupId);
Recipient.live(groupRecipient).refresh();
}
private List<RecipientId> getCurrentMembers(String groupId) {
private List<RecipientId> getCurrentMembers(@NonNull GroupId groupId) {
Cursor cursor = null;
try {
cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, new String[] {MEMBERS},
GROUP_ID + " = ?",
new String[] {groupId},
new String[] {groupId.toString()},
null, null, null);
if (cursor != null && cursor.moveToFirst()) {
@ -345,23 +345,22 @@ public class GroupDatabase extends Database {
}
}
public boolean isActive(String groupId) {
public boolean isActive(@NonNull GroupId groupId) {
Optional<GroupRecord> record = getGroup(groupId);
return record.isPresent() && record.get().isActive();
}
public void setActive(String groupId, boolean active) {
public void setActive(@NonNull GroupId groupId, boolean active) {
SQLiteDatabase database = databaseHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(ACTIVE, active ? 1 : 0);
database.update(TABLE_NAME, values, GROUP_ID + " = ?", new String[] {groupId});
database.update(TABLE_NAME, values, GROUP_ID + " = ?", new String[] {groupId.toString()});
}
public byte[] allocateGroupId() {
public static GroupId allocateGroupId(boolean mms) {
byte[] groupId = new byte[16];
new SecureRandom().nextBytes(groupId);
return groupId;
return mms ? GroupId.mms(groupId) : GroupId.v1(groupId);
}
public static class Reader implements Closeable {
@ -385,7 +384,7 @@ public class GroupDatabase extends Database {
return null;
}
return new GroupRecord(cursor.getString(cursor.getColumnIndexOrThrow(GROUP_ID)),
return new GroupRecord(GroupId.parse(cursor.getString(cursor.getColumnIndexOrThrow(GROUP_ID))),
RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(RECIPIENT_ID))),
cursor.getString(cursor.getColumnIndexOrThrow(TITLE)),
cursor.getString(cursor.getColumnIndexOrThrow(MEMBERS)),
@ -408,7 +407,7 @@ public class GroupDatabase extends Database {
public static class GroupRecord {
private final String id;
private final GroupId id;
private final RecipientId recipientId;
private final String title;
private final List<RecipientId> members;
@ -421,7 +420,7 @@ public class GroupDatabase extends Database {
private final boolean active;
private final boolean mms;
public GroupRecord(String id, @NonNull RecipientId recipientId, String title, String members, byte[] avatar,
public GroupRecord(@NonNull GroupId id, @NonNull RecipientId recipientId, String title, String members, byte[] avatar,
long avatarId, byte[] avatarKey, String avatarContentType,
String relay, boolean active, byte[] avatarDigest, boolean mms)
{
@ -441,22 +440,14 @@ public class GroupDatabase extends Database {
else this.members = new LinkedList<>();
}
public byte[] getId() {
try {
return GroupUtil.getDecodedId(id);
} catch (IOException ioe) {
throw new AssertionError(ioe);
}
public GroupId getId() {
return id;
}
public @NonNull RecipientId getRecipientId() {
return recipientId;
}
public String getEncodedId() {
return id;
}
public String getTitle() {
return title;
}

View file

@ -17,22 +17,20 @@ import net.sqlcipher.database.SQLiteDatabase;
import org.signal.zkgroup.profiles.ProfileKey;
import org.signal.zkgroup.profiles.ProfileKeyCredential;
import org.thoughtcrime.securesms.color.MaterialColor;
import org.thoughtcrime.securesms.storage.StorageSyncHelper;
import org.thoughtcrime.securesms.storage.StorageSyncHelper.RecordUpdate;
import org.thoughtcrime.securesms.storage.StorageSyncModels;
import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord;
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobs.StorageSyncJob;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.profiles.ProfileName;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.storage.StorageSyncHelper;
import org.thoughtcrime.securesms.storage.StorageSyncHelper.RecordUpdate;
import org.thoughtcrime.securesms.storage.StorageSyncModels;
import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.IdentityUtil;
import org.thoughtcrime.securesms.util.SqlUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.InvalidKeyException;
@ -352,13 +350,13 @@ public class RecipientDatabase extends Database {
return getOrInsertByColumn(EMAIL, email).recipientId;
}
public @NonNull RecipientId getOrInsertFromGroupId(@NonNull String groupId) {
GetOrInsertResult result = getOrInsertByColumn(GROUP_ID, groupId);
public @NonNull RecipientId getOrInsertFromGroupId(@NonNull GroupId groupId) {
GetOrInsertResult result = getOrInsertByColumn(GROUP_ID, groupId.toString());
if (result.neededInsert) {
ContentValues values = new ContentValues();
if (GroupUtil.isMmsGroup(groupId)) {
if (groupId.isMmsGroup()) {
values.put(GROUP_TYPE, GroupType.MMS.getId());
} else {
values.put(GROUP_TYPE, GroupType.SIGNAL_V1.getId());
@ -563,7 +561,7 @@ public class RecipientDatabase extends Database {
for (SignalGroupV1Record insert : groupV1Inserts) {
db.insertOrThrow(TABLE_NAME, null, getValuesForStorageGroupV1(insert));
Recipient recipient = Recipient.externalGroup(context, GroupUtil.getEncodedId(insert.getGroupId(), false));
Recipient recipient = Recipient.externalGroup(context, GroupId.v1(insert.getGroupId()));
threadDatabase.setArchived(recipient.getId(), insert.isArchived());
recipient.live().refresh();
@ -577,7 +575,7 @@ public class RecipientDatabase extends Database {
throw new AssertionError("Had an update, but it didn't match any rows!");
}
Recipient recipient = Recipient.externalGroup(context, GroupUtil.getEncodedId(update.getOld().getGroupId(), false));
Recipient recipient = Recipient.externalGroup(context, GroupId.v1(update.getOld().getGroupId()));
threadDatabase.setArchived(recipient.getId(), update.getNew().isArchived());
recipient.live().refresh();
@ -672,7 +670,7 @@ public class RecipientDatabase extends Database {
private static @NonNull ContentValues getValuesForStorageGroupV1(@NonNull SignalGroupV1Record groupV1) {
ContentValues values = new ContentValues();
values.put(GROUP_ID, GroupUtil.getEncodedId(groupV1.getGroupId(), false));
values.put(GROUP_ID, GroupId.v1(groupV1.getGroupId()).toString());
values.put(GROUP_TYPE, GroupType.SIGNAL_V1.getId());
values.put(PROFILE_SHARING, groupV1.isProfileSharingEnabled() ? "1" : "0");
values.put(BLOCKED, groupV1.isBlocked() ? "1" : "0");
@ -729,13 +727,13 @@ public class RecipientDatabase extends Database {
return out;
}
private @NonNull RecipientSettings getRecipientSettings(@NonNull Cursor cursor) {
private static @NonNull RecipientSettings getRecipientSettings(@NonNull Cursor cursor) {
long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID));
UUID uuid = UuidUtil.parseOrNull(cursor.getString(cursor.getColumnIndexOrThrow(UUID)));
String username = cursor.getString(cursor.getColumnIndexOrThrow(USERNAME));
String e164 = cursor.getString(cursor.getColumnIndexOrThrow(PHONE));
String email = cursor.getString(cursor.getColumnIndexOrThrow(EMAIL));
String groupId = cursor.getString(cursor.getColumnIndexOrThrow(GROUP_ID));
GroupId groupId = GroupId.parseNullable(cursor.getString(cursor.getColumnIndexOrThrow(GROUP_ID)));
int groupType = cursor.getInt(cursor.getColumnIndexOrThrow(GROUP_TYPE));
boolean blocked = cursor.getInt(cursor.getColumnIndexOrThrow(BLOCKED)) == 1;
String messageRingtone = cursor.getString(cursor.getColumnIndexOrThrow(MESSAGE_RINGTONE));
@ -1408,10 +1406,10 @@ public class RecipientDatabase extends Database {
db.update(TABLE_NAME, setBlocked, UUID + " = ?", new String[] { uuid });
}
List<String> groupIdStrings = Stream.of(groupIds).map(g -> GroupUtil.getEncodedId(g, false)).toList();
List<GroupId> groupIdStrings = Stream.of(groupIds).map(GroupId::v1).toList();
for (String groupId : groupIdStrings) {
db.update(TABLE_NAME, setBlocked, GROUP_ID + " = ?", new String[] { groupId });
for (GroupId groupId : groupIdStrings) {
db.update(TABLE_NAME, setBlocked, GROUP_ID + " = ?", new String[] { groupId.toString() });
}
db.setTransactionSuccessful();
@ -1637,7 +1635,7 @@ public class RecipientDatabase extends Database {
private final String username;
private final String e164;
private final String email;
private final String groupId;
private final GroupId groupId;
private final GroupType groupType;
private final boolean blocked;
private final long muteUntil;
@ -1673,7 +1671,7 @@ public class RecipientDatabase extends Database {
@Nullable String username,
@Nullable String e164,
@Nullable String email,
@Nullable String groupId,
@Nullable GroupId groupId,
@NonNull GroupType groupType,
boolean blocked,
long muteUntil,
@ -1761,7 +1759,7 @@ public class RecipientDatabase extends Database {
return email;
}
public @Nullable String getGroupId() {
public @Nullable GroupId getGroupId() {
return groupId;
}

View file

@ -20,6 +20,7 @@ import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteException;
import android.net.Uri;
import androidx.annotation.Nullable;
import com.annimon.stream.Stream;
@ -27,6 +28,7 @@ import com.annimon.stream.Stream;
import net.sqlcipher.database.SQLiteDatabase;
import net.sqlcipher.database.SQLiteStatement;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
@ -231,7 +233,7 @@ public class SmsMigrator {
List<RecipientId> recipientIds = Stream.of(ourRecipients).map(Recipient::getId).toList();
String ourGroupId = DatabaseFactory.getGroupDatabase(context).getOrCreateGroupForMembers(recipientIds, true);
GroupId ourGroupId = DatabaseFactory.getGroupDatabase(context).getOrCreateGroupForMembers(recipientIds, true);
RecipientId ourGroupRecipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(ourGroupId);
Recipient ourGroupRecipient = Recipient.resolved(ourGroupRecipientId);
long ourThreadId = threadDatabase.getThreadIdFor(ourGroupRecipient, ThreadDatabase.DistributionTypes.CONVERSATION);

View file

@ -10,9 +10,9 @@ import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri;
import android.provider.ContactsContract;
import androidx.annotation.Nullable;
import android.text.TextUtils;
import org.thoughtcrime.securesms.logging.Log;
import androidx.annotation.Nullable;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.i18n.phonenumbers.NumberParseException;
@ -35,13 +35,14 @@ import org.thoughtcrime.securesms.database.PushDatabase;
import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.migrations.LegacyMigrationJob;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.phonenumbers.NumberUtil;
import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.DelimiterUtil;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.Hex;
import org.thoughtcrime.securesms.util.JsonUtils;
import org.thoughtcrime.securesms.util.MediaUtil;
@ -1274,7 +1275,7 @@ public class ClassicOpenHelper extends SQLiteOpenHelper {
while (cursor != null && cursor.moveToNext()) {
String address = cursor.getString(cursor.getColumnIndexOrThrow("recipient_ids"));
if (!TextUtils.isEmpty(address) && !GroupUtil.isEncodedGroup(address) && !NumberUtil.isValidEmail(address)) {
if (!TextUtils.isEmpty(address) && !GroupId.isEncodedGroup(address) && !NumberUtil.isValidEmail(address)) {
Uri lookup = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(address));
try (Cursor contactCursor = context.getContentResolver().query(lookup, new String[] {ContactsContract.PhoneLookup.DISPLAY_NAME,

View file

@ -9,10 +9,10 @@ import androidx.annotation.Nullable;
import net.sqlcipher.database.SQLiteDatabase;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.phonenumbers.NumberUtil;
import org.thoughtcrime.securesms.util.DelimiterUtil;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.Util;
import java.util.HashSet;
@ -153,7 +153,7 @@ public class RecipientIdMigrationHelper {
try (Cursor cursor = db.query("recipient_preferences", null, null, null, null, null, null)) {
while (cursor != null && cursor.moveToNext()) {
String address = cursor.getString(cursor.getColumnIndexOrThrow("recipient_ids"));
boolean isGroup = GroupUtil.isEncodedGroup(address);
boolean isGroup = GroupId.isEncodedGroup(address);
boolean isEmail = !isGroup && NumberUtil.isValidEmail(address);
boolean isPhone = !isGroup && !isEmail;

View file

@ -21,7 +21,6 @@ import net.sqlcipher.database.SQLiteDatabase;
import net.sqlcipher.database.SQLiteDatabaseHook;
import net.sqlcipher.database.SQLiteOpenHelper;
import org.thoughtcrime.securesms.storage.StorageSyncHelper;
import org.thoughtcrime.securesms.crypto.DatabaseSecret;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.AttachmentDatabase;
@ -44,13 +43,14 @@ import org.thoughtcrime.securesms.database.StickerDatabase;
import org.thoughtcrime.securesms.database.StorageKeyDatabase;
import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.jobs.RefreshPreKeysJob;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.storage.StorageSyncHelper;
import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.ServiceUtil;
import org.thoughtcrime.securesms.util.SqlUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
@ -349,7 +349,7 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
String displayName = NotificationChannels.getChannelDisplayNameFor(context, systemName, profileName, null, address);
boolean vibrateEnabled = vibrateState == 0 ? TextSecurePreferences.isNotificationVibrateEnabled(context) : vibrateState == 1;
if (GroupUtil.isEncodedGroup(address)) {
if (GroupId.isEncodedGroup(address)) {
try(Cursor groupCursor = db.rawQuery("SELECT title FROM groups WHERE group_id = ?", new String[] { address })) {
if (groupCursor != null && groupCursor.moveToFirst()) {
String title = groupCursor.getString(groupCursor.getColumnIndexOrThrow("title"));

View file

@ -0,0 +1,93 @@
package org.thoughtcrime.securesms.groups;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.util.Hex;
import java.io.IOException;
public final class GroupId {
private static final String ENCODED_SIGNAL_GROUP_PREFIX = "__textsecure_group__!";
private static final String ENCODED_MMS_GROUP_PREFIX = "__signal_mms_group__!";
private final String encodedId;
private GroupId(@NonNull String encodedId) {
this.encodedId = encodedId;
}
public static @NonNull GroupId v1(byte[] gv1GroupIdBytes) {
return new GroupId(ENCODED_SIGNAL_GROUP_PREFIX + Hex.toStringCondensed(gv1GroupIdBytes));
}
public static @NonNull GroupId mms(byte[] mmsGroupIdBytes) {
return new GroupId(ENCODED_MMS_GROUP_PREFIX + Hex.toStringCondensed(mmsGroupIdBytes));
}
public static @NonNull GroupId parse(@NonNull String encodedGroupId) {
try {
if (!isEncodedGroup(encodedGroupId)) {
throw new IOException("Invalid encoding");
}
byte[] bytes = extractDecodedId(encodedGroupId);
return isMmsGroup(encodedGroupId) ? mms(bytes) : v1(bytes);
} catch (IOException e) {
throw new AssertionError(e);
}
}
public static @Nullable GroupId parseNullable(@Nullable String encodedGroupId) {
if (encodedGroupId == null) {
return null;
}
return parse(encodedGroupId);
}
public static boolean isEncodedGroup(@NonNull String groupId) {
return groupId.startsWith(ENCODED_SIGNAL_GROUP_PREFIX) || groupId.startsWith(ENCODED_MMS_GROUP_PREFIX);
}
private static byte[] extractDecodedId(@NonNull String encodedGroupId) throws IOException {
return Hex.fromStringCondensed(encodedGroupId.split("!", 2)[1]);
}
private static boolean isMmsGroup(@NonNull String groupId) {
return groupId.startsWith(ENCODED_MMS_GROUP_PREFIX);
}
public byte[] getDecodedId() {
try {
return extractDecodedId(encodedId);
} catch (IOException e) {
throw new AssertionError(e);
}
}
public boolean isMmsGroup() {
return isMmsGroup(encodedId);
}
@Override
public boolean equals(@Nullable Object obj) {
if (obj instanceof GroupId) {
return ((GroupId) obj).encodedId.equals(encodedId);
}
return false;
}
@Override
public int hashCode() {
return encodedId.hashCode();
}
@NonNull
@Override
public String toString() {
return encodedId;
}
}

View file

@ -29,7 +29,7 @@ public final class GroupManager {
}
public static GroupActionResult updateGroup(@NonNull Context context,
@NonNull String groupId,
@NonNull GroupId groupId,
@NonNull Set<Recipient> members,
@Nullable Bitmap avatar,
@Nullable String name)
@ -51,7 +51,7 @@ public final class GroupManager {
@WorkerThread
public static boolean leaveGroup(@NonNull Context context, @NonNull Recipient groupRecipient) {
String groupId = groupRecipient.requireGroupId();
GroupId groupId = groupRecipient.requireGroupId();
return V1GroupManager.leaveGroup(context, groupId, groupRecipient);
}

View file

@ -28,7 +28,6 @@ import org.thoughtcrime.securesms.sms.IncomingGroupMessage;
import org.thoughtcrime.securesms.sms.IncomingTextMessage;
import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.FeatureFlags;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
import org.whispersystems.signalservice.api.messages.SignalServiceContent;
@ -63,7 +62,7 @@ public class GroupMessageProcessor {
GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
SignalServiceGroup group = message.getGroupInfo().get();
String id = GroupUtil.getEncodedId(group.getGroupId(), false);
GroupId id = GroupId.v1(group.getGroupId());
Optional<GroupRecord> record = database.getGroup(id);
if (record.isPresent() && group.getType() == Type.UPDATE) {
@ -73,7 +72,7 @@ public class GroupMessageProcessor {
} else if (record.isPresent() && group.getType() == Type.QUIT) {
return handleGroupLeave(context, content, group, record.get(), outgoing);
} else if (record.isPresent() && group.getType() == Type.REQUEST_INFO) {
return handleGroupInfoRequest(context, content, group, record.get());
return handleGroupInfoRequest(context, content, record.get());
} else {
Log.w(TAG, "Received unknown type, ignoring...");
return null;
@ -86,7 +85,7 @@ public class GroupMessageProcessor {
boolean outgoing)
{
GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
String id = GroupUtil.getEncodedId(group.getGroupId(), false);
GroupId id = GroupId.v1(group.getGroupId());
GroupContext.Builder builder = createGroupContext(group);
builder.setType(GroupContext.Type.UPDATE);
@ -106,7 +105,7 @@ public class GroupMessageProcessor {
if (FeatureFlags.messageRequests() && (sender.isSystemContact() || sender.isProfileSharing())) {
Log.i(TAG, "Auto-enabling profile sharing because 'adder' is trusted. contact: " + sender.isSystemContact() + ", profileSharing: " + sender.isProfileSharing());
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(Recipient.external(context, id).getId(), true);
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(Recipient.externalGroup(context, id).getId(), true);
}
return storeMessage(context, content, group, builder.build(), outgoing);
@ -120,7 +119,7 @@ public class GroupMessageProcessor {
{
GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
String id = GroupUtil.getEncodedId(group.getGroupId(), false);
GroupId id = GroupId.v1(group.getGroupId());
Set<RecipientId> recordMembers = new HashSet<>(groupRecord.getMembers());
Set<RecipientId> messageMembers = new HashSet<>();
@ -178,13 +177,12 @@ public class GroupMessageProcessor {
private static Long handleGroupInfoRequest(@NonNull Context context,
@NonNull SignalServiceContent content,
@NonNull SignalServiceGroup group,
@NonNull GroupRecord record)
{
Recipient sender = Recipient.externalPush(context, content.getSender());
if (record.getMembers().contains(sender.getId())) {
ApplicationDependencies.getJobManager().add(new PushGroupUpdateJob(sender.getId(), group.getGroupId()));
ApplicationDependencies.getJobManager().add(new PushGroupUpdateJob(sender.getId(), record.getId()));
}
return null;
@ -197,7 +195,7 @@ public class GroupMessageProcessor {
boolean outgoing)
{
GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
String id = GroupUtil.getEncodedId(group.getGroupId(), false);
GroupId id = GroupId.v1(group.getGroupId());
List<RecipientId> members = record.getMembers();
GroupContext.Builder builder = createGroupContext(group);
@ -222,13 +220,13 @@ public class GroupMessageProcessor {
{
if (group.getAvatar().isPresent()) {
ApplicationDependencies.getJobManager()
.add(new AvatarDownloadJob(group.getGroupId()));
.add(new AvatarDownloadJob(GroupId.v1(group.getGroupId())));
}
try {
if (outgoing) {
MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(context);
RecipientId recipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(GroupUtil.getEncodedId(group.getGroupId(), false));
RecipientId recipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(GroupId.v1(group.getGroupId()));
Recipient recipient = Recipient.resolved(recipientId);
OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(recipient, storage, null, content.getTimestamp(), 0, false, null, Collections.emptyList(), Collections.emptyList());
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
@ -240,7 +238,7 @@ public class GroupMessageProcessor {
} else {
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
String body = Base64.encodeBytes(storage.toByteArray());
IncomingTextMessage incoming = new IncomingTextMessage(Recipient.externalPush(context, content.getSender()).getId(), content.getSenderDevice(), content.getTimestamp(), body, Optional.of(GroupUtil.getEncodedId(group.getGroupId(), false)), 0, content.isNeedsReceipt());
IncomingTextMessage incoming = new IncomingTextMessage(Recipient.externalPush(context, content.getSender()).getId(), content.getSenderDevice(), content.getTimestamp(), body, Optional.of(GroupId.v1(group.getGroupId())), 0, content.isNeedsReceipt());
IncomingGroupMessage groupMessage = new IncomingGroupMessage(incoming, storage, body);
Optional<InsertResult> insertResult = smsDatabase.insertMessageInbox(groupMessage);

View file

@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.groups;
import android.content.Context;
import android.graphics.Bitmap;
import android.net.Uri;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
@ -11,7 +12,6 @@ import com.google.protobuf.ByteString;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.attachments.UriAttachment;
import org.thoughtcrime.securesms.blurhash.BlurHash;
import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase;
@ -28,12 +28,10 @@ import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.util.BitmapUtil;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.util.InvalidNumberException;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext;
import java.io.IOException;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
@ -49,7 +47,7 @@ final class V1GroupManager {
{
final byte[] avatarBytes = BitmapUtil.toByteArray(avatar);
final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
final String groupId = GroupUtil.getEncodedId(groupDatabase.allocateGroupId(), mms);
final GroupId groupId = GroupDatabase.allocateGroupId(mms);
final RecipientId groupRecipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(groupId);
final Recipient groupRecipient = Recipient.resolved(groupRecipientId);
@ -67,7 +65,7 @@ final class V1GroupManager {
}
static GroupActionResult updateGroup(@NonNull Context context,
@NonNull String groupId,
@NonNull GroupId groupId,
@NonNull Set<RecipientId> memberAddresses,
@Nullable Bitmap avatar,
@Nullable String name)
@ -81,7 +79,7 @@ final class V1GroupManager {
groupDatabase.updateTitle(groupId, name);
groupDatabase.updateAvatar(groupId, avatarBytes);
if (!GroupUtil.isMmsGroup(groupId)) {
if (!groupId.isMmsGroup()) {
return sendGroupUpdate(context, groupId, memberAddresses, name, avatarBytes);
} else {
RecipientId groupRecipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(groupId);
@ -92,48 +90,44 @@ final class V1GroupManager {
}
private static GroupActionResult sendGroupUpdate(@NonNull Context context,
@NonNull String groupId,
@NonNull GroupId groupId,
@NonNull Set<RecipientId> members,
@Nullable String groupName,
@Nullable byte[] avatar)
{
try {
Attachment avatarAttachment = null;
RecipientId groupRecipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(groupId);
Recipient groupRecipient = Recipient.resolved(groupRecipientId);
Attachment avatarAttachment = null;
RecipientId groupRecipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(groupId);
Recipient groupRecipient = Recipient.resolved(groupRecipientId);
List<GroupContext.Member> uuidMembers = new LinkedList<>();
List<String> e164Members = new LinkedList<>();
List<GroupContext.Member> uuidMembers = new LinkedList<>();
List<String> e164Members = new LinkedList<>();
for (RecipientId member : members) {
Recipient recipient = Recipient.resolved(member);
uuidMembers.add(GroupMessageProcessor.createMember(RecipientUtil.toSignalServiceAddress(context, recipient)));
}
GroupContext.Builder groupContextBuilder = GroupContext.newBuilder()
.setId(ByteString.copyFrom(GroupUtil.getDecodedId(groupId)))
.setType(GroupContext.Type.UPDATE)
.addAllMembersE164(e164Members)
.addAllMembers(uuidMembers);
if (groupName != null) groupContextBuilder.setName(groupName);
GroupContext groupContext = groupContextBuilder.build();
if (avatar != null) {
Uri avatarUri = BlobProvider.getInstance().forData(avatar).createForSingleUseInMemory();
avatarAttachment = new UriAttachment(avatarUri, MediaUtil.IMAGE_PNG, AttachmentDatabase.TRANSFER_PROGRESS_DONE, avatar.length, null, false, false, null, null, null, null);
}
OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(groupRecipient, groupContext, avatarAttachment, System.currentTimeMillis(), 0, false, null, Collections.emptyList(), Collections.emptyList());
long threadId = MessageSender.send(context, outgoingMessage, -1, false, null);
return new GroupActionResult(groupRecipient, threadId);
} catch (IOException e) {
throw new AssertionError(e);
for (RecipientId member : members) {
Recipient recipient = Recipient.resolved(member);
uuidMembers.add(GroupMessageProcessor.createMember(RecipientUtil.toSignalServiceAddress(context, recipient)));
}
GroupContext.Builder groupContextBuilder = GroupContext.newBuilder()
.setId(ByteString.copyFrom(groupId.getDecodedId()))
.setType(GroupContext.Type.UPDATE)
.addAllMembersE164(e164Members)
.addAllMembers(uuidMembers);
if (groupName != null) groupContextBuilder.setName(groupName);
GroupContext groupContext = groupContextBuilder.build();
if (avatar != null) {
Uri avatarUri = BlobProvider.getInstance().forData(avatar).createForSingleUseInMemory();
avatarAttachment = new UriAttachment(avatarUri, MediaUtil.IMAGE_PNG, AttachmentDatabase.TRANSFER_PROGRESS_DONE, avatar.length, null, false, false, null, null, null, null);
}
OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(groupRecipient, groupContext, avatarAttachment, System.currentTimeMillis(), 0, false, null, Collections.emptyList(), Collections.emptyList());
long threadId = MessageSender.send(context, outgoingMessage, -1, false, null);
return new GroupActionResult(groupRecipient, threadId);
}
@WorkerThread
static boolean leaveGroup(@NonNull Context context, @NonNull String groupId, @NonNull Recipient groupRecipient) {
static boolean leaveGroup(@NonNull Context context, @NonNull GroupId groupId, @NonNull Recipient groupRecipient) {
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipient);
Optional<OutgoingGroupMediaMessage> leaveMessage = GroupUtil.createGroupLeaveMessage(context, groupRecipient);

View file

@ -1,12 +1,14 @@
package org.thoughtcrime.securesms.jobs;
import android.graphics.Bitmap;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
@ -14,7 +16,6 @@ import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.mms.AttachmentStreamUriLoader.AttachmentModel;
import org.thoughtcrime.securesms.util.BitmapDecodingException;
import org.thoughtcrime.securesms.util.BitmapUtil;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.Hex;
import org.whispersystems.libsignal.InvalidMessageException;
import org.whispersystems.libsignal.util.guava.Optional;
@ -36,9 +37,9 @@ public class AvatarDownloadJob extends BaseJob {
private static final String KEY_GROUP_ID = "group_id";
private byte[] groupId;
private @NonNull GroupId groupId;
public AvatarDownloadJob(@NonNull byte[] groupId) {
public AvatarDownloadJob(@NonNull GroupId groupId) {
this(new Job.Parameters.Builder()
.addConstraint(NetworkConstraint.KEY)
.setMaxAttempts(10)
@ -46,14 +47,14 @@ public class AvatarDownloadJob extends BaseJob {
groupId);
}
private AvatarDownloadJob(@NonNull Job.Parameters parameters, @NonNull byte[] groupId) {
private AvatarDownloadJob(@NonNull Job.Parameters parameters, @NonNull GroupId groupId) {
super(parameters);
this.groupId = groupId;
}
@Override
public @NonNull Data serialize() {
return new Data.Builder().putString(KEY_GROUP_ID, GroupUtil.getEncodedId(groupId, false)).build();
return new Data.Builder().putString(KEY_GROUP_ID, groupId.toString()).build();
}
@Override
@ -63,9 +64,8 @@ public class AvatarDownloadJob extends BaseJob {
@Override
public void onRun() throws IOException {
String encodeId = GroupUtil.getEncodedId(groupId, false);
GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
Optional<GroupRecord> record = database.getGroup(encodeId);
Optional<GroupRecord> record = database.getGroup(groupId);
File attachment = null;
try {
@ -93,7 +93,7 @@ public class AvatarDownloadJob extends BaseJob {
InputStream inputStream = receiver.retrieveAttachment(pointer, attachment, MAX_AVATAR_SIZE);
Bitmap avatar = BitmapUtil.createScaledBitmap(context, new AttachmentModel(attachment, key, 0, digest), 500, 500);
database.updateAvatar(encodeId, avatar);
database.updateAvatar(groupId, avatar);
inputStream.close();
}
} catch (BitmapDecodingException | NonSuccessfulResponseCodeException | InvalidMessageException e) {
@ -116,11 +116,7 @@ public class AvatarDownloadJob extends BaseJob {
public static final class Factory implements Job.Factory<AvatarDownloadJob> {
@Override
public @NonNull AvatarDownloadJob create(@NonNull Parameters parameters, @NonNull Data data) {
try {
return new AvatarDownloadJob(parameters, GroupUtil.getDecodedId(data.getString(KEY_GROUP_ID)));
} catch (IOException e) {
throw new AssertionError(e);
}
return new AvatarDownloadJob(parameters, GroupId.parse(data.getString(KEY_GROUP_ID)));
}
}
}

View file

@ -3,12 +3,12 @@ package org.thoughtcrime.securesms.jobs;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.WorkerThread;
import com.annimon.stream.Stream;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
@ -18,7 +18,6 @@ import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.recipients.RecipientUtil;
import org.thoughtcrime.securesms.transport.RetryLaterException;
import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
@ -27,8 +26,6 @@ import org.whispersystems.signalservice.api.messages.SendMessageResult;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos;
import java.io.IOException;
import java.util.Collections;
@ -54,7 +51,7 @@ public class LeaveGroupJob extends BaseJob {
private static final String KEY_MEMBERS = "members";
private static final String KEY_RECIPIENTS = "recipients";
private final byte[] groupId;
private final GroupId groupId;
private final String name;
private final List<RecipientId> members;
private final List<RecipientId> recipients;
@ -63,7 +60,7 @@ public class LeaveGroupJob extends BaseJob {
List<RecipientId> members = Stream.of(group.resolve().getParticipants()).map(Recipient::getId).toList();
members.remove(Recipient.self().getId());
return new LeaveGroupJob(GroupUtil.getDecodedIdOrThrow(group.getGroupId().get()),
return new LeaveGroupJob(group.getGroupId().get(),
group.resolve().getDisplayName(ApplicationDependencies.getApplication()),
members,
members,
@ -75,7 +72,7 @@ public class LeaveGroupJob extends BaseJob {
.build());
}
private LeaveGroupJob(@NonNull byte[] groupId,
private LeaveGroupJob(@NonNull GroupId groupId,
@NonNull String name,
@NonNull List<RecipientId> members,
@NonNull List<RecipientId> recipients,
@ -90,7 +87,7 @@ public class LeaveGroupJob extends BaseJob {
@Override
public @NonNull Data serialize() {
return new Data.Builder().putString(KEY_GROUP_ID, Base64.encodeBytes(groupId))
return new Data.Builder().putString(KEY_GROUP_ID, Base64.encodeBytes(groupId.getDecodedId()))
.putString(KEY_GROUP_NAME, name)
.putString(KEY_MEMBERS, RecipientId.toSerializedList(members))
.putString(KEY_RECIPIENTS, RecipientId.toSerializedList(recipients))
@ -128,7 +125,7 @@ public class LeaveGroupJob extends BaseJob {
}
private static @NonNull List<Recipient> deliver(@NonNull Context context,
@NonNull byte[] groupId,
@NonNull GroupId groupId,
@NonNull String name,
@NonNull List<RecipientId> members,
@NonNull List<RecipientId> destinations)
@ -138,7 +135,7 @@ public class LeaveGroupJob extends BaseJob {
List<SignalServiceAddress> addresses = Stream.of(destinations).map(Recipient::resolved).map(t -> RecipientUtil.toSignalServiceAddress(context, t)).toList();
List<SignalServiceAddress> memberAddresses = Stream.of(members).map(Recipient::resolved).map(t -> RecipientUtil.toSignalServiceAddress(context, t)).toList();
List<Optional<UnidentifiedAccessPair>> unidentifiedAccess = Stream.of(destinations).map(Recipient::resolved).map(recipient -> UnidentifiedAccessUtil.getAccessFor(context, recipient)).toList();
SignalServiceGroup serviceGroup = new SignalServiceGroup(SignalServiceGroup.Type.QUIT, groupId, name, memberAddresses, null);
SignalServiceGroup serviceGroup = new SignalServiceGroup(SignalServiceGroup.Type.QUIT, groupId.getDecodedId(), name, memberAddresses, null);
SignalServiceDataMessage.Builder dataMessage = SignalServiceDataMessage.newBuilder()
.withTimestamp(System.currentTimeMillis())
.asGroupMessage(serviceGroup);
@ -169,7 +166,7 @@ public class LeaveGroupJob extends BaseJob {
public static class Factory implements Job.Factory<LeaveGroupJob> {
@Override
public @NonNull LeaveGroupJob create(@NonNull Parameters parameters, @NonNull Data data) {
return new LeaveGroupJob(Base64.decodeOrThrow(data.getString(KEY_GROUP_ID)),
return new LeaveGroupJob(GroupId.v1(Base64.decodeOrThrow(data.getString(KEY_GROUP_ID))),
data.getString(KEY_GROUP_NAME),
RecipientId.fromSerializedList(data.getString(KEY_MEMBERS)),
RecipientId.fromSerializedList(data.getString(KEY_RECIPIENTS)),

View file

@ -1,6 +1,7 @@
package org.thoughtcrime.securesms.jobs;
import android.net.Uri;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@ -16,6 +17,7 @@ import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MessagingDatabase.InsertResult;
import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.logging.Log;
@ -177,11 +179,11 @@ public class MmsDownloadJob extends BaseJob {
int subscriptionId, @Nullable RecipientId notificationFrom)
throws MmsException
{
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
Optional<String> group = Optional.absent();
Set<RecipientId> members = new HashSet<>();
String body = null;
List<Attachment> attachments = new LinkedList<>();
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
Optional<GroupId> group = Optional.absent();
Set<RecipientId> members = new HashSet<>();
String body = null;
List<Attachment> attachments = new LinkedList<>();
RecipientId from = null;

View file

@ -13,7 +13,6 @@ import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientUtil;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
@ -76,7 +75,7 @@ public class MultiDeviceBlockedUpdateJob extends BaseJob {
while ((recipient = reader.getNext()) != null) {
if (recipient.isPushGroup()) {
blockedGroups.add(GroupUtil.getDecodedId(recipient.requireGroupId()));
blockedGroups.add(recipient.requireGroupId().getDecodedId());
} else if (recipient.hasServiceIdentifier()) {
blockedIndividuals.add(RecipientUtil.toSignalServiceAddress(context, recipient));
}

View file

@ -14,7 +14,6 @@ import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.recipients.RecipientUtil;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
@ -92,13 +91,13 @@ public class MultiDeviceGroupUpdateJob extends BaseJob {
members.add(RecipientUtil.toSignalServiceAddress(context, Recipient.resolved(member)));
}
RecipientId recipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(GroupUtil.getEncodedId(record.getId(), record.isMms()));
RecipientId recipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(record.getId());
Recipient recipient = Recipient.resolved(recipientId);
Optional<Integer> expirationTimer = recipient.getExpireMessages() > 0 ? Optional.of(recipient.getExpireMessages()) : Optional.absent();
Map<RecipientId, Integer> inboxPositions = DatabaseFactory.getThreadDatabase(context).getInboxPositions();
Set<RecipientId> archived = DatabaseFactory.getThreadDatabase(context).getArchivedRecipients();
out.write(new DeviceGroup(record.getId(),
out.write(new DeviceGroup(record.getId().getDecodedId(),
Optional.fromNullable(record.getTitle()),
members,
getAvatar(record.getAvatar()),

View file

@ -8,18 +8,13 @@ import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.recipients.RecipientUtil;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
import org.whispersystems.signalservice.api.kbs.MasterKey;
import org.whispersystems.signalservice.api.messages.multidevice.KeysMessage;
import org.whispersystems.signalservice.api.messages.multidevice.MessageRequestResponseMessage;
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
@ -100,7 +95,7 @@ public class MultiDeviceMessageRequestResponseJob extends BaseJob {
MessageRequestResponseMessage response;
if (recipient.isGroup()) {
response = MessageRequestResponseMessage.forGroup(GroupUtil.getDecodedId(recipient.getGroupId().get()), localToRemoteType(type));
response = MessageRequestResponseMessage.forGroup(recipient.getGroupId().get().getDecodedId(), localToRemoteType(type));
} else {
response = MessageRequestResponseMessage.forIndividual(RecipientUtil.toSignalServiceAddress(context, recipient), localToRemoteType(type));
}

View file

@ -29,13 +29,13 @@ import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.NoSuchMessageException;
import org.thoughtcrime.securesms.database.PushDatabase;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.JobManager;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.thoughtcrime.securesms.transport.RetryLaterException;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.state.SignalProtocolStore;
import org.whispersystems.libsignal.util.guava.Optional;
@ -233,7 +233,7 @@ public final class PushDecryptMessageJob extends BaseJob {
return new PushProcessMessageJob.ExceptionMetadata(sender,
e.getSenderDevice(),
e.getGroup().transform(g -> GroupUtil.getEncodedId(g.getGroupId(), false)).orNull());
e.getGroup().transform(g -> GroupId.v1(g.getGroupId())).orNull());
}
private static PushProcessMessageJob.ExceptionMetadata toExceptionMetadata(@NonNull ProtocolException e) throws NoSenderException {

View file

@ -1,6 +1,7 @@
package org.thoughtcrime.securesms.jobs;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
@ -18,6 +19,7 @@ import org.thoughtcrime.securesms.database.NoSuchMessageException;
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
import org.thoughtcrime.securesms.database.documents.NetworkFailure;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.JobManager;
@ -31,8 +33,6 @@ import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.recipients.RecipientUtil;
import org.thoughtcrime.securesms.transport.RetryLaterException;
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
import org.thoughtcrime.securesms.util.FeatureFlags;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
@ -242,7 +242,7 @@ public class PushGroupSendJob extends PushSendJob {
rotateSenderCertificateIfNecessary();
SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
String groupId = groupRecipient.requireGroupId();
GroupId groupId = groupRecipient.requireGroupId();
Optional<byte[]> profileKey = getProfileKey(groupRecipient);
Optional<Quote> quote = getQuoteFor(message);
Optional<SignalServiceDataMessage.Sticker> sticker = getStickerFor(message);
@ -266,7 +266,7 @@ public class PushGroupSendJob extends PushSendJob {
List<SignalServiceAddress> members = Stream.of(groupContext.getMembersList())
.map(m -> new SignalServiceAddress(UuidUtil.parseOrNull(m.getUuid()), m.getE164()))
.toList();
SignalServiceGroup group = new SignalServiceGroup(type, GroupUtil.getDecodedId(groupId), groupContext.getName(), members, avatar);
SignalServiceGroup group = new SignalServiceGroup(type, groupId.getDecodedId(), groupContext.getName(), members, avatar);
SignalServiceDataMessage groupDataMessage = SignalServiceDataMessage.newBuilder()
.withTimestamp(message.getSentTimeMillis())
.withExpiration(groupRecipient.getExpireMessages())
@ -275,7 +275,7 @@ public class PushGroupSendJob extends PushSendJob {
return messageSender.sendMessage(addresses, unidentifiedAccess, isRecipientUpdate, groupDataMessage);
} else {
SignalServiceGroup group = new SignalServiceGroup(GroupUtil.getDecodedId(groupId));
SignalServiceGroup group = new SignalServiceGroup(groupId.getDecodedId());
SignalServiceDataMessage groupMessage = SignalServiceDataMessage.newBuilder()
.withTimestamp(message.getSentTimeMillis())
.asGroupMessage(group)
@ -295,7 +295,7 @@ public class PushGroupSendJob extends PushSendJob {
}
}
private @NonNull List<RecipientId> getGroupMessageRecipients(String groupId, long messageId) {
private @NonNull List<RecipientId> getGroupMessageRecipients(@NonNull GroupId groupId, long messageId) {
List<GroupReceiptInfo> destinations = DatabaseFactory.getGroupReceiptDatabase(context).getGroupReceiptInfo(messageId);
if (!destinations.isEmpty()) return Stream.of(destinations).map(GroupReceiptInfo::getRecipientId).toList();

View file

@ -8,6 +8,7 @@ import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
@ -15,7 +16,6 @@ import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.recipients.RecipientUtil;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
@ -26,8 +26,6 @@ import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
import org.whispersystems.signalservice.api.messages.SignalServiceGroup.Type;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext;
import java.io.ByteArrayInputStream;
import java.io.IOException;
@ -44,10 +42,10 @@ public class PushGroupUpdateJob extends BaseJob {
private static final String KEY_SOURCE = "source";
private static final String KEY_GROUP_ID = "group_id";
private RecipientId source;
private byte[] groupId;
private final RecipientId source;
private final GroupId groupId;
public PushGroupUpdateJob(@NonNull RecipientId source, byte[] groupId) {
public PushGroupUpdateJob(@NonNull RecipientId source, @NonNull GroupId groupId) {
this(new Job.Parameters.Builder()
.addConstraint(NetworkConstraint.KEY)
.setLifespan(TimeUnit.DAYS.toMillis(1))
@ -57,7 +55,7 @@ public class PushGroupUpdateJob extends BaseJob {
groupId);
}
private PushGroupUpdateJob(@NonNull Job.Parameters parameters, RecipientId source, byte[] groupId) {
private PushGroupUpdateJob(@NonNull Job.Parameters parameters, RecipientId source, @NonNull GroupId groupId) {
super(parameters);
this.source = source;
@ -67,7 +65,7 @@ public class PushGroupUpdateJob extends BaseJob {
@Override
public @NonNull Data serialize() {
return new Data.Builder().putString(KEY_SOURCE, source.serialize())
.putString(KEY_GROUP_ID, GroupUtil.getEncodedId(groupId, false))
.putString(KEY_GROUP_ID, groupId.toString())
.build();
}
@ -79,11 +77,11 @@ public class PushGroupUpdateJob extends BaseJob {
@Override
public void onRun() throws IOException, UntrustedIdentityException {
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
Optional<GroupRecord> record = groupDatabase.getGroup(GroupUtil.getEncodedId(groupId, false));
Optional<GroupRecord> record = groupDatabase.getGroup(groupId);
SignalServiceAttachment avatar = null;
if (record == null) {
Log.w(TAG, "No information for group record info request: " + new String(groupId));
Log.w(TAG, "No information for group record info request: " + groupId.toString());
return;
}
@ -104,12 +102,12 @@ public class PushGroupUpdateJob extends BaseJob {
SignalServiceGroup groupContext = SignalServiceGroup.newBuilder(Type.UPDATE)
.withAvatar(avatar)
.withId(groupId)
.withId(groupId.getDecodedId())
.withMembers(members)
.withName(record.get().getTitle())
.build();
RecipientId groupRecipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(GroupUtil.getEncodedId(groupId, false));
RecipientId groupRecipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(groupId);
Recipient groupRecipient = Recipient.resolved(groupRecipientId);
SignalServiceDataMessage message = SignalServiceDataMessage.newBuilder()
@ -139,13 +137,9 @@ public class PushGroupUpdateJob extends BaseJob {
public static final class Factory implements Job.Factory<PushGroupUpdateJob> {
@Override
public @NonNull PushGroupUpdateJob create(@NonNull Parameters parameters, @NonNull org.thoughtcrime.securesms.jobmanager.Data data) {
try {
return new PushGroupUpdateJob(parameters,
RecipientId.from(data.getString(KEY_SOURCE)),
GroupUtil.getDecodedId(data.getString(KEY_GROUP_ID)));
} catch (IOException e) {
throw new AssertionError(e);
}
return new PushGroupUpdateJob(parameters,
RecipientId.from(data.getString(KEY_SOURCE)),
GroupId.parse(data.getString(KEY_GROUP_ID)));
}
}
}

View file

@ -43,6 +43,7 @@ import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
import org.thoughtcrime.securesms.database.model.ReactionRecord;
import org.thoughtcrime.securesms.database.model.StickerRecord;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.groups.GroupMessageProcessor;
import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job;
@ -62,9 +63,9 @@ import org.thoughtcrime.securesms.mms.StickerSlide;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.service.WebRtcCallService;
import org.thoughtcrime.securesms.ringrtc.IceCandidateParcel;
import org.thoughtcrime.securesms.ringrtc.RemotePeer;
import org.thoughtcrime.securesms.service.WebRtcCallService;
import org.thoughtcrime.securesms.sms.IncomingEncryptedMessage;
import org.thoughtcrime.securesms.sms.IncomingEndSessionMessage;
import org.thoughtcrime.securesms.sms.IncomingTextMessage;
@ -74,7 +75,6 @@ import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
import org.thoughtcrime.securesms.stickers.StickerLocator;
import org.thoughtcrime.securesms.storage.StorageSyncHelper;
import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.Hex;
import org.thoughtcrime.securesms.util.IdentityUtil;
import org.thoughtcrime.securesms.util.MediaUtil;
@ -217,7 +217,7 @@ public final class PushProcessMessageJob extends BaseJob {
//noinspection ConstantConditions
dataBuilder.putString(KEY_EXCEPTION_SENDER, exceptionMetadata.sender)
.putInt(KEY_EXCEPTION_DEVICE, exceptionMetadata.senderDevice)
.putString(KEY_EXCEPTION_GROUP_ID, exceptionMetadata.groupId);
.putString(KEY_EXCEPTION_GROUP_ID, exceptionMetadata.groupId == null ? null : exceptionMetadata.groupId.toString());
}
return dataBuilder.build();
@ -272,7 +272,7 @@ public final class PushProcessMessageJob extends BaseJob {
else if (isMediaMessage) handleMediaMessage(content, message, smsMessageId);
else if (message.getBody().isPresent()) handleTextMessage(content, message, smsMessageId);
if (message.getGroupInfo().isPresent() && groupDatabase.isUnknownGroup(GroupUtil.getEncodedId(message.getGroupInfo().get().getGroupId(), false))) {
if (message.getGroupInfo().isPresent() && groupDatabase.isUnknownGroup(GroupId.v1(message.getGroupInfo().get().getGroupId()))) {
handleUnknownGroupMessage(content, message.getGroupInfo().get());
}
@ -327,8 +327,8 @@ public final class PushProcessMessageJob extends BaseJob {
}
}
private static @NonNull Optional<String> toEncodedId(@NonNull Optional<SignalServiceGroup> groupInfo) {
return groupInfo.transform(g -> GroupUtil.getEncodedId(g.getGroupId(), false));
private static @NonNull Optional<GroupId> toEncodedId(@NonNull Optional<SignalServiceGroup> groupInfo) {
return groupInfo.transform(g -> GroupId.v1(g.getGroupId()));
}
private void handleExceptionMessage(@NonNull ExceptionMetadata e, @NonNull Optional<Long> smsMessageId) {
@ -546,7 +546,7 @@ public final class PushProcessMessageJob extends BaseJob {
@NonNull SignalServiceGroup group)
{
if (group.getType() != SignalServiceGroup.Type.REQUEST_INFO) {
ApplicationDependencies.getJobManager().add(new RequestGroupInfoJob(Recipient.externalPush(context, content.getSender()).getId(), group.getGroupId()));
ApplicationDependencies.getJobManager().add(new RequestGroupInfoJob(Recipient.externalPush(context, content.getSender()).getId(), GroupId.v1(group.getGroupId())));
} else {
Log.w(TAG, "Received a REQUEST_INFO message for a group we don't know about. Ignoring.");
}
@ -682,7 +682,7 @@ public final class PushProcessMessageJob extends BaseJob {
if (response.getPerson().isPresent()) {
recipient = Recipient.externalPush(context, response.getPerson().get());
} else if (response.getGroupId().isPresent()) {
String groupId = GroupUtil.getEncodedId(response.getGroupId().get(), false);
GroupId groupId = GroupId.v1(response.getGroupId().get());
recipient = Recipient.externalGroup(context, groupId);
} else {
Log.w(TAG, "Message request response was missing a thread recipient! Skipping.");
@ -743,7 +743,7 @@ public final class PushProcessMessageJob extends BaseJob {
threadId = handleSynchronizeSentTextMessage(message);
}
if (message.getMessage().getGroupInfo().isPresent() && groupDatabase.isUnknownGroup(GroupUtil.getEncodedId(message.getMessage().getGroupInfo().get().getGroupId(), false))) {
if (message.getMessage().getGroupInfo().isPresent() && groupDatabase.isUnknownGroup(GroupId.v1(message.getMessage().getGroupInfo().get().getGroupId()))) {
handleUnknownGroupMessage(content, message.getMessage().getGroupInfo().get());
}
@ -1017,7 +1017,7 @@ public final class PushProcessMessageJob extends BaseJob {
updateGroupReceiptStatus(message, record.getId(), recipient.requireGroupId());
}
private void updateGroupReceiptStatus(@NonNull SentTranscriptMessage message, long messageId, @NonNull String groupString) {
private void updateGroupReceiptStatus(@NonNull SentTranscriptMessage message, long messageId, @NonNull GroupId groupString) {
GroupReceiptDatabase receiptDatabase = DatabaseFactory.getGroupReceiptDatabase(context);
List<Recipient> messageRecipients = Stream.of(message.getRecipients()).map(address -> Recipient.externalPush(context, address)).toList();
List<Recipient> members = DatabaseFactory.getGroupDatabase(context).getGroupMembers(groupString, false);
@ -1183,7 +1183,7 @@ public final class PushProcessMessageJob extends BaseJob {
private void handleUnsupportedDataMessage(@NonNull String sender,
int senderDevice,
@NonNull Optional<String> groupId,
@NonNull Optional<GroupId> groupId,
long timestamp,
@NonNull Optional<Long> smsMessageId)
{
@ -1203,7 +1203,7 @@ public final class PushProcessMessageJob extends BaseJob {
private void handleInvalidMessage(@NonNull SignalServiceAddress sender,
int senderDevice,
@NonNull Optional<String> groupId,
@NonNull Optional<GroupId> groupId,
long timestamp,
@NonNull Optional<Long> smsMessageId)
{
@ -1313,7 +1313,7 @@ public final class PushProcessMessageJob extends BaseJob {
long threadId;
if (typingMessage.getGroupId().isPresent()) {
RecipientId recipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(GroupUtil.getEncodedId(typingMessage.getGroupId().get(), false));
RecipientId recipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(GroupId.v1(typingMessage.getGroupId().get()));
Recipient groupRecipient = Recipient.resolved(recipientId);
threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipient);
@ -1478,7 +1478,7 @@ public final class PushProcessMessageJob extends BaseJob {
return insertPlaceholder(sender, senderDevice, timestamp, Optional.absent());
}
private Optional<InsertResult> insertPlaceholder(@NonNull String sender, int senderDevice, long timestamp, Optional<String> groupId) {
private Optional<InsertResult> insertPlaceholder(@NonNull String sender, int senderDevice, long timestamp, Optional<GroupId> groupId) {
SmsDatabase database = DatabaseFactory.getSmsDatabase(context);
IncomingTextMessage textMessage = new IncomingTextMessage(Recipient.external(context, sender).getId(),
senderDevice, timestamp, "",
@ -1490,7 +1490,7 @@ public final class PushProcessMessageJob extends BaseJob {
private Recipient getSyncMessageDestination(SentTranscriptMessage message) {
if (message.getMessage().getGroupInfo().isPresent()) {
return Recipient.external(context, GroupUtil.getEncodedId(message.getMessage().getGroupInfo().get().getGroupId(), false));
return Recipient.externalGroup(context, GroupId.v1(message.getMessage().getGroupInfo().get().getGroupId()));
} else {
return Recipient.externalPush(context, message.getDestination().get());
}
@ -1498,7 +1498,7 @@ public final class PushProcessMessageJob extends BaseJob {
private Recipient getMessageDestination(SignalServiceContent content, SignalServiceDataMessage message) {
if (message.getGroupInfo().isPresent()) {
return Recipient.external(context, GroupUtil.getEncodedId(message.getGroupInfo().get().getGroupId(), false));
return Recipient.externalGroup(context, GroupId.v1(message.getGroupInfo().get().getGroupId()));
} else {
return Recipient.externalPush(context, content.getSender());
}
@ -1529,9 +1529,9 @@ public final class PushProcessMessageJob extends BaseJob {
if (conversation.isGroup() && conversation.isBlocked()) {
return true;
} else if (conversation.isGroup()) {
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
Optional<String> groupId = message.getGroupInfo().isPresent() ? Optional.of(GroupUtil.getEncodedId(message.getGroupInfo().get().getGroupId(), false))
: Optional.absent();
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
Optional<GroupId> groupId = message.getGroupInfo().isPresent() ? Optional.of(GroupId.v1(message.getGroupInfo().get().getGroupId()))
: Optional.absent();
if (groupId.isPresent() && groupDatabase.isUnknownGroup(groupId.get())) {
return false;
@ -1616,7 +1616,7 @@ public final class PushProcessMessageJob extends BaseJob {
} else {
ExceptionMetadata exceptionMetadata = new ExceptionMetadata(data.getString(KEY_EXCEPTION_SENDER),
data.getInt(KEY_EXCEPTION_DEVICE),
data.getStringOrDefault(KEY_EXCEPTION_GROUP_ID, null));
GroupId.parseNullable(data.getStringOrDefault(KEY_EXCEPTION_GROUP_ID, null)));
return new PushProcessMessageJob(parameters,
state,
@ -1643,11 +1643,11 @@ public final class PushProcessMessageJob extends BaseJob {
}
static class ExceptionMetadata {
@NonNull private final String sender;
private final int senderDevice;
@Nullable private final String groupId;
@NonNull private final String sender;
private final int senderDevice;
@Nullable private final GroupId groupId;
ExceptionMetadata(@NonNull String sender, int senderDevice, @Nullable String groupId) {
ExceptionMetadata(@NonNull String sender, int senderDevice, @Nullable GroupId groupId) {
this.sender = sender;
this.senderDevice = senderDevice;
this.groupId = groupId;

View file

@ -21,7 +21,6 @@ import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.recipients.RecipientUtil;
import org.thoughtcrime.securesms.transport.RetryLaterException;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
@ -217,7 +216,7 @@ public class ReactionSendJob extends BaseJob {
.withReaction(buildReaction(context, reaction, remove, targetAuthor, targetSentTimestamp));
if (conversationRecipient.isGroup()) {
dataMessage.asGroupMessage(new SignalServiceGroup(GroupUtil.getDecodedId(conversationRecipient.requireGroupId())));
dataMessage.asGroupMessage(new SignalServiceGroup(conversationRecipient.requireGroupId().getDecodedId()));
}

View file

@ -4,20 +4,18 @@ import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.recipients.RecipientUtil;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
import org.whispersystems.signalservice.api.messages.SignalServiceGroup.Type;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
import java.io.IOException;
@ -33,10 +31,10 @@ public class RequestGroupInfoJob extends BaseJob {
private static final String KEY_SOURCE = "source";
private static final String KEY_GROUP_ID = "group_id";
private RecipientId source;
private byte[] groupId;
private final RecipientId source;
private final GroupId groupId;
public RequestGroupInfoJob(@NonNull RecipientId source, @NonNull byte[] groupId) {
public RequestGroupInfoJob(@NonNull RecipientId source, @NonNull GroupId groupId) {
this(new Job.Parameters.Builder()
.addConstraint(NetworkConstraint.KEY)
.setLifespan(TimeUnit.DAYS.toMillis(1))
@ -47,7 +45,7 @@ public class RequestGroupInfoJob extends BaseJob {
}
private RequestGroupInfoJob(@NonNull Job.Parameters parameters, @NonNull RecipientId source, @NonNull byte[] groupId) {
private RequestGroupInfoJob(@NonNull Job.Parameters parameters, @NonNull RecipientId source, @NonNull GroupId groupId) {
super(parameters);
this.source = source;
@ -57,7 +55,7 @@ public class RequestGroupInfoJob extends BaseJob {
@Override
public @NonNull Data serialize() {
return new Data.Builder().putString(KEY_SOURCE, source.serialize())
.putString(KEY_GROUP_ID, GroupUtil.getEncodedId(groupId, false))
.putString(KEY_GROUP_ID, groupId.toString())
.build();
}
@ -69,7 +67,7 @@ public class RequestGroupInfoJob extends BaseJob {
@Override
public void onRun() throws IOException, UntrustedIdentityException {
SignalServiceGroup group = SignalServiceGroup.newBuilder(Type.REQUEST_INFO)
.withId(groupId)
.withId(groupId.getDecodedId())
.build();
SignalServiceDataMessage message = SignalServiceDataMessage.newBuilder()
@ -99,13 +97,9 @@ public class RequestGroupInfoJob extends BaseJob {
@Override
public @NonNull RequestGroupInfoJob create(@NonNull Parameters parameters, @NonNull Data data) {
try {
return new RequestGroupInfoJob(parameters,
RecipientId.from(data.getString(KEY_SOURCE)),
GroupUtil.getDecodedId(data.getString(KEY_GROUP_ID)));
} catch (IOException e) {
throw new AssertionError(e);
}
return new RequestGroupInfoJob(parameters,
RecipientId.from(data.getString(KEY_SOURCE)),
GroupId.parse(data.getString(KEY_GROUP_ID)));
}
}
}

View file

@ -12,7 +12,6 @@ import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientUtil;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
@ -87,7 +86,7 @@ public class TypingSendJob extends BaseJob {
if (recipient.isGroup()) {
recipients = DatabaseFactory.getGroupDatabase(context).getGroupMembers(recipient.requireGroupId(), false);
groupId = Optional.of(GroupUtil.getDecodedId(recipient.requireGroupId()));
groupId = Optional.of(recipient.requireGroupId().getDecodedId());
}
recipients = Stream.of(recipients).map(Recipient::resolve).toList();

View file

@ -101,7 +101,7 @@ class CameraContactsRepository {
try (GroupDatabase.Reader reader = groupDatabase.getGroupsFilteredByTitle(query, false)) {
GroupDatabase.GroupRecord groupRecord;
while ((groupRecord = reader.getNext()) != null) {
RecipientId recipientId = recipientDatabase.getOrInsertFromGroupId(groupRecord.getEncodedId());
RecipientId recipientId = recipientDatabase.getOrInsertFromGroupId(groupRecord.getId());
recipients.add(Recipient.resolved(recipientId));
}
}

View file

@ -2,14 +2,13 @@ package org.thoughtcrime.securesms.migrations;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.phonenumbers.NumberUtil;
import org.thoughtcrime.securesms.profiles.AvatarHelper;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.Util;
import java.io.File;
@ -83,7 +82,7 @@ public class AvatarMigrationJob extends MigrationJob {
}
private static boolean isValidFileName(@NonNull String name) {
return NUMBER_PATTERN.matcher(name).matches() || GroupUtil.isEncodedGroup(name) || NumberUtil.isValidEmail(name);
return NUMBER_PATTERN.matcher(name).matches() || GroupId.isEncodedGroup(name) || NumberUtil.isValidEmail(name);
}
public static class Factory implements Job.Factory<AvatarMigrationJob> {

View file

@ -5,9 +5,9 @@ import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.attachments.PointerAttachment;
import org.thoughtcrime.securesms.contactshare.Contact;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.linkpreview.LinkPreview;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
@ -19,7 +19,7 @@ import java.util.List;
public class IncomingMediaMessage {
private final RecipientId from;
private final String groupId;
private final GroupId groupId;
private final String body;
private final boolean push;
private final long sentTimeMillis;
@ -35,7 +35,7 @@ public class IncomingMediaMessage {
private final List<LinkPreview> linkPreviews = new LinkedList<>();
public IncomingMediaMessage(@NonNull RecipientId from,
Optional<String> groupId,
Optional<GroupId> groupId,
String body,
long sentTimeMillis,
List<Attachment> attachments,
@ -86,7 +86,7 @@ public class IncomingMediaMessage {
this.quote = quote.orNull();
this.unidentified = unidentified;
if (group.isPresent()) this.groupId = GroupUtil.getEncodedId(group.get().getGroupId(), false);
if (group.isPresent()) this.groupId = GroupId.v1(group.get().getGroupId());
else this.groupId = null;
this.attachments.addAll(PointerAttachment.forPointers(attachments));
@ -114,7 +114,7 @@ public class IncomingMediaMessage {
return from;
}
public String getGroupId() {
public GroupId getGroupId() {
return groupId;
}

View file

@ -11,8 +11,8 @@ import com.google.i18n.phonenumbers.PhoneNumberUtil;
import com.google.i18n.phonenumbers.Phonenumber;
import com.google.i18n.phonenumbers.ShortNumberInfo;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.util.Pair;
@ -83,7 +83,7 @@ public class PhoneNumberFormatter {
public String format(@Nullable String number) {
if (number == null) return "Unknown";
if (GroupUtil.isEncodedGroup(number)) return number;
if (GroupId.isEncodedGroup(number)) return number;
if (ALPHA_PATTERN.matcher(number).find()) return number.trim();
String bareNumber = number.replaceAll("[^0-9+]", "");

View file

@ -19,7 +19,6 @@ import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.database.RecipientDatabase.RecipientSettings;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.util.guava.Optional;
@ -198,7 +197,7 @@ public final class LiveRecipient {
List<Recipient> members = Stream.of(groupRecord.get().getMembers()).filterNot(RecipientId::isUnknown).map(this::fetchRecipientFromDisk).toList();
Optional<Long> avatarId = Optional.absent();
if (settings.getGroupId() != null && !GroupUtil.isMmsGroup(settings.getGroupId()) && title == null) {
if (settings.getGroupId() != null && !settings.getGroupId().isMmsGroup() && title == null) {
title = unnamedGroupName;
}

View file

@ -28,6 +28,7 @@ import org.thoughtcrime.securesms.database.RecipientDatabase.RegisteredState;
import org.thoughtcrime.securesms.database.RecipientDatabase.UnidentifiedAccessMode;
import org.thoughtcrime.securesms.database.RecipientDatabase.VibrateState;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.notifications.NotificationChannels;
@ -35,7 +36,6 @@ import org.thoughtcrime.securesms.phonenumbers.NumberUtil;
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
import org.thoughtcrime.securesms.profiles.ProfileName;
import org.thoughtcrime.securesms.util.FeatureFlags;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.libsignal.util.guava.Preconditions;
@ -64,7 +64,7 @@ public class Recipient {
private final String username;
private final String e164;
private final String email;
private final String groupId;
private final GroupId groupId;
private final List<Recipient> participants;
private final Optional<Long> groupAvatarId;
private final boolean localNumber;
@ -236,11 +236,7 @@ public class Recipient {
* identifier is a groupId.
*/
@WorkerThread
public static @NonNull Recipient externalGroup(@NonNull Context context, @NonNull String groupId) {
if (!GroupUtil.isEncodedGroup(groupId)) {
throw new IllegalArgumentException("Invalid groupId!");
}
public static @NonNull Recipient externalGroup(@NonNull Context context, @NonNull GroupId groupId) {
return Recipient.resolved(DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(groupId));
}
@ -274,8 +270,8 @@ public class Recipient {
throw new UuidRecipientError();
}
}
} else if (GroupUtil.isEncodedGroup(identifier)) {
id = db.getOrInsertFromGroupId(identifier);
} else if (GroupId.isEncodedGroup(identifier)) {
id = db.getOrInsertFromGroupId(GroupId.parse(identifier));
} else if (NumberUtil.isValidEmail(identifier)) {
id = db.getOrInsertFromEmail(identifier);
} else {
@ -385,7 +381,7 @@ public class Recipient {
}
public @Nullable String getName(@NonNull Context context) {
if (this.name == null && groupId != null && GroupUtil.isMmsGroup(groupId)) {
if (this.name == null && groupId != null && groupId.isMmsGroup()) {
List<String> names = new LinkedList<>();
for (Recipient recipient : participants) {
@ -443,7 +439,7 @@ public class Recipient {
return Optional.fromNullable(email);
}
public @NonNull Optional<String> getGroupId() {
public @NonNull Optional<GroupId> getGroupId() {
return Optional.fromNullable(groupId);
}
@ -495,8 +491,8 @@ public class Recipient {
return getUuid().isPresent();
}
public @NonNull String requireGroupId() {
String resolved = resolving ? resolve().groupId : groupId;
public @NonNull GroupId requireGroupId() {
GroupId resolved = resolving ? resolve().groupId : groupId;
if (resolved == null) {
throw new MissingAddressError();
@ -532,7 +528,7 @@ public class Recipient {
Recipient resolved = resolving ? resolve() : this;
if (resolved.isGroup()) {
return resolved.requireGroupId();
return resolved.requireGroupId().toString();
} else if (resolved.getUuid().isPresent()) {
return resolved.getUuid().get().toString();
}
@ -570,13 +566,13 @@ public class Recipient {
}
public boolean isMmsGroup() {
String groupId = resolve().groupId;
return groupId != null && GroupUtil.isMmsGroup(groupId);
GroupId groupId = resolve().groupId;
return groupId != null && groupId.isMmsGroup();
}
public boolean isPushGroup() {
String groupId = resolve().groupId;
return groupId != null && !GroupUtil.isMmsGroup(groupId);
GroupId groupId = resolve().groupId;
return groupId != null && !groupId.isMmsGroup();
}
public @NonNull List<Recipient> getParticipants() {

View file

@ -13,6 +13,7 @@ import org.thoughtcrime.securesms.database.RecipientDatabase.RecipientSettings;
import org.thoughtcrime.securesms.database.RecipientDatabase.RegisteredState;
import org.thoughtcrime.securesms.database.RecipientDatabase.UnidentifiedAccessMode;
import org.thoughtcrime.securesms.database.RecipientDatabase.VibrateState;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.profiles.ProfileName;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
@ -28,7 +29,7 @@ public class RecipientDetails {
final String username;
final String e164;
final String email;
final String groupId;
final GroupId groupId;
final String name;
final String customLabel;
final Uri systemContactPhoto;

View file

@ -14,8 +14,9 @@ import org.thoughtcrime.securesms.contacts.sync.DirectoryHelper;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.RecipientDatabase.RegisteredState;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob;
import org.thoughtcrime.securesms.jobs.LeaveGroupJob;
import org.thoughtcrime.securesms.jobs.MultiDeviceBlockedUpdateJob;
@ -23,9 +24,9 @@ import org.thoughtcrime.securesms.jobs.MultiDeviceMessageRequestResponseJob;
import org.thoughtcrime.securesms.jobs.RotateProfileKeyJob;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
import org.thoughtcrime.securesms.storage.StorageSyncHelper;
import org.thoughtcrime.securesms.util.FeatureFlags;
import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
@ -123,7 +124,7 @@ public class RecipientUtil {
ApplicationDependencies.getJobManager().add(LeaveGroupJob.create(recipient));
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
String groupId = resolved.requireGroupId();
GroupId groupId = resolved.requireGroupId();
groupDatabase.setActive(groupId, false);
groupDatabase.remove(groupId, Recipient.self().getId());
} else {

View file

@ -7,6 +7,7 @@ import android.telephony.SmsMessage;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
@ -28,19 +29,19 @@ public class IncomingTextMessage implements Parcelable {
};
private static final String TAG = IncomingTextMessage.class.getSimpleName();
private final String message;
private RecipientId sender;
private final int senderDeviceId;
private final int protocol;
private final String serviceCenterAddress;
private final boolean replyPathPresent;
private final String pseudoSubject;
private final long sentTimestampMillis;
private final String groupId;
private final boolean push;
private final int subscriptionId;
private final long expiresInMillis;
private final boolean unidentified;
private final String message;
private final RecipientId sender;
private final int senderDeviceId;
private final int protocol;
private final String serviceCenterAddress;
private final boolean replyPathPresent;
private final String pseudoSubject;
private final long sentTimestampMillis;
@Nullable private final GroupId groupId;
private final boolean push;
private final int subscriptionId;
private final long expiresInMillis;
private final boolean unidentified;
public IncomingTextMessage(@NonNull RecipientId sender, @NonNull SmsMessage message, int subscriptionId) {
this.message = message.getDisplayMessageBody();
@ -59,7 +60,7 @@ public class IncomingTextMessage implements Parcelable {
}
public IncomingTextMessage(@NonNull RecipientId sender, int senderDeviceId, long sentTimestampMillis,
String encodedBody, Optional<String> groupId,
String encodedBody, Optional<GroupId> groupId,
long expiresInMillis, boolean unidentified)
{
this.message = encodedBody;
@ -86,7 +87,7 @@ public class IncomingTextMessage implements Parcelable {
this.replyPathPresent = (in.readInt() == 1);
this.pseudoSubject = in.readString();
this.sentTimestampMillis = in.readLong();
this.groupId = in.readString();
this.groupId = GroupId.parseNullable(in.readString());
this.push = (in.readInt() == 1);
this.subscriptionId = in.readInt();
this.expiresInMillis = in.readLong();
@ -131,7 +132,7 @@ public class IncomingTextMessage implements Parcelable {
this.unidentified = fragments.get(0).isUnidentified();
}
protected IncomingTextMessage(@NonNull RecipientId sender, @Nullable String groupId)
protected IncomingTextMessage(@NonNull RecipientId sender, @Nullable GroupId groupId)
{
this.message = "";
this.sender = sender;
@ -216,7 +217,7 @@ public class IncomingTextMessage implements Parcelable {
return push;
}
public @Nullable String getGroupId() {
public @Nullable GroupId getGroupId() {
return groupId;
}
@ -259,7 +260,7 @@ public class IncomingTextMessage implements Parcelable {
out.writeInt(replyPathPresent ? 1 : 0);
out.writeString(pseudoSubject);
out.writeLong(sentTimestampMillis);
out.writeString(groupId);
out.writeString(groupId == null ? null : groupId.toString());
out.writeInt(push ? 1 : 0);
out.writeInt(subscriptionId);
out.writeLong(expiresInMillis);

View file

@ -5,7 +5,7 @@ import androidx.annotation.NonNull;
import com.annimon.stream.Collectors;
import com.annimon.stream.Stream;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.groups.GroupId;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.storage.SignalGroupV1Record;
@ -15,15 +15,15 @@ import java.util.Map;
class GroupV1ConflictMerger implements StorageSyncHelper.ConflictMerger<SignalGroupV1Record> {
private final Map<String, SignalGroupV1Record> localByGroupId;
private final Map<GroupId, SignalGroupV1Record> localByGroupId;
GroupV1ConflictMerger(@NonNull Collection<SignalGroupV1Record> localOnly) {
localByGroupId = Stream.of(localOnly).collect(Collectors.toMap(g -> GroupUtil.getEncodedId(g.getGroupId(), false), g -> g));
localByGroupId = Stream.of(localOnly).collect(Collectors.toMap(g -> GroupId.v1(g.getGroupId()), g -> g));
}
@Override
public @NonNull Optional<SignalGroupV1Record> getMatching(@NonNull SignalGroupV1Record record) {
return Optional.fromNullable(localByGroupId.get(GroupUtil.getEncodedId(record.getGroupId(), false)));
return Optional.fromNullable(localByGroupId.get(GroupId.v1(record.getGroupId())));
}
@Override

View file

@ -5,7 +5,6 @@ import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.database.IdentityDatabase;
import org.thoughtcrime.securesms.database.RecipientDatabase.RecipientSettings;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.storage.SignalContactRecord;
import org.whispersystems.signalservice.api.storage.SignalGroupV1Record;
@ -56,7 +55,7 @@ public final class StorageSyncModels {
throw new AssertionError("Must have a groupId!");
}
return new SignalGroupV1Record.Builder(rawStorageId, GroupUtil.getDecodedIdOrThrow(recipient.getGroupId()))
return new SignalGroupV1Record.Builder(rawStorageId, recipient.getGroupId().getDecodedId())
.setBlocked(recipient.isBlocked())
.setProfileSharingEnabled(recipient.isProfileSharing())
.setArchived(archived.contains(recipient.getId()))

View file

@ -11,6 +11,7 @@ import com.google.protobuf.ByteString;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
import org.thoughtcrime.securesms.recipients.Recipient;
@ -26,43 +27,16 @@ import java.util.List;
import static org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext;
public class GroupUtil {
public final class GroupUtil {
private static final String ENCODED_SIGNAL_GROUP_PREFIX = "__textsecure_group__!";
private static final String ENCODED_MMS_GROUP_PREFIX = "__signal_mms_group__!";
private static final String TAG = GroupUtil.class.getSimpleName();
public static String getEncodedId(byte[] groupId, boolean mms) {
return (mms ? ENCODED_MMS_GROUP_PREFIX : ENCODED_SIGNAL_GROUP_PREFIX) + Hex.toStringCondensed(groupId);
private GroupUtil() {
}
public static byte[] getDecodedId(String groupId) throws IOException {
if (!isEncodedGroup(groupId)) {
throw new IOException("Invalid encoding");
}
return Hex.fromStringCondensed(groupId.split("!", 2)[1]);
}
public static byte[] getDecodedIdOrThrow(String groupId) {
try {
return getDecodedId(groupId);
} catch (IOException e) {
throw new AssertionError(e);
}
}
public static boolean isEncodedGroup(@NonNull String groupId) {
return groupId.startsWith(ENCODED_SIGNAL_GROUP_PREFIX) || groupId.startsWith(ENCODED_MMS_GROUP_PREFIX);
}
public static boolean isMmsGroup(@NonNull String groupId) {
return groupId.startsWith(ENCODED_MMS_GROUP_PREFIX);
}
private static final String TAG = Log.tag(GroupUtil.class);
@WorkerThread
public static Optional<OutgoingGroupMediaMessage> createGroupLeaveMessage(@NonNull Context context, @NonNull Recipient groupRecipient) {
String encodedGroupId = groupRecipient.requireGroupId();
GroupId encodedGroupId = groupRecipient.requireGroupId();
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
if (!groupDatabase.isActive(encodedGroupId)) {
@ -70,13 +44,7 @@ public class GroupUtil {
return Optional.absent();
}
ByteString decodedGroupId;
try {
decodedGroupId = ByteString.copyFrom(getDecodedId(encodedGroupId));
} catch (IOException e) {
Log.w(TAG, "Failed to decode group ID.", e);
return Optional.absent();
}
ByteString decodedGroupId = ByteString.copyFrom(encodedGroupId.getDecodedId());
GroupContext groupContext = GroupContext.newBuilder()
.setId(decodedGroupId)

View file

@ -78,14 +78,14 @@ public class IdentityUtil {
if (groupRecord.getMembers().contains(recipient.getId()) && groupRecord.isActive() && !groupRecord.isMms()) {
if (remote) {
IncomingTextMessage incoming = new IncomingTextMessage(recipient.getId(), 1, time, null, Optional.of(groupRecord.getEncodedId()), 0, false);
IncomingTextMessage incoming = new IncomingTextMessage(recipient.getId(), 1, time, null, Optional.of(groupRecord.getId()), 0, false);
if (verified) incoming = new IncomingIdentityVerifiedMessage(incoming);
else incoming = new IncomingIdentityDefaultMessage(incoming);
smsDatabase.insertMessageInbox(incoming);
} else {
RecipientId recipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(groupRecord.getEncodedId());
RecipientId recipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(groupRecord.getId());
Recipient groupRecipient = Recipient.resolved(recipientId);
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipient);
OutgoingTextMessage outgoing ;
@ -129,7 +129,7 @@ public class IdentityUtil {
while ((groupRecord = reader.getNext()) != null) {
if (groupRecord.getMembers().contains(recipient.getId()) && groupRecord.isActive()) {
IncomingTextMessage incoming = new IncomingTextMessage(recipient.getId(), 1, time, null, Optional.of(groupRecord.getEncodedId()), 0, false);
IncomingTextMessage incoming = new IncomingTextMessage(recipient.getId(), 1, time, null, Optional.of(groupRecord.getId()), 0, false);
IncomingIdentityUpdateMessage groupUpdate = new IncomingIdentityUpdateMessage(incoming);
smsDatabase.insertMessageInbox(groupUpdate);

View file

@ -0,0 +1,135 @@
package org.thoughtcrime.securesms.groups;
import org.junit.Test;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
public final class GroupIdTest {
@Test
public void can_create_for_gv1() {
GroupId groupId = GroupId.v1(new byte[]{ 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15 });
assertEquals("__textsecure_group__!0001020305060708090b0c0d0e0f", groupId.toString());
assertFalse(groupId.isMmsGroup());
}
@Test
public void can_parse_gv1() {
GroupId groupId = GroupId.parse("__textsecure_group__!0001020305060708090b0c0d0e0f");
assertEquals("__textsecure_group__!0001020305060708090b0c0d0e0f", groupId.toString());
assertArrayEquals(new byte[]{ 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15 }, groupId.getDecodedId());
assertFalse(groupId.isMmsGroup());
}
@Test
public void can_create_for_mms() {
GroupId groupId = GroupId.mms(new byte[]{ 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15 });
assertEquals("__signal_mms_group__!0001020305060708090b0c0d0e0f", groupId.toString());
assertTrue(groupId.isMmsGroup());
}
@Test
public void can_parse_mms() {
GroupId groupId = GroupId.parse("__signal_mms_group__!0001020305060708090b0c0d0e0f");
assertEquals("__signal_mms_group__!0001020305060708090b0c0d0e0f", groupId.toString());
assertArrayEquals(new byte[]{ 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15 }, groupId.getDecodedId());
assertTrue(groupId.isMmsGroup());
}
@SuppressWarnings("ConstantConditions")
@Test
public void can_parse_null() {
GroupId groupId = GroupId.parseNullable(null);
assertNull(groupId);
}
@Test
public void can_parse_gv1_with_parseNullable() {
GroupId groupId = GroupId.parseNullable("__textsecure_group__!0001020305060708090b0c0d0e0f");
assertEquals("__textsecure_group__!0001020305060708090b0c0d0e0f", groupId.toString());
assertArrayEquals(new byte[]{ 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15 }, groupId.getDecodedId());
assertFalse(groupId.isMmsGroup());
}
@Test(expected = AssertionError.class)
public void bad_encoding__bad_prefix__parseNullable() {
GroupId.parseNullable("__BAD_PREFIX__!0001020305060708090b0c0d0e0f");
}
@Test(expected = AssertionError.class)
public void bad_encoding__empty__parseNullable() {
GroupId.parseNullable("");
}
@Test(expected = AssertionError.class)
public void bad_encoding__odd_hex__parseNullable() {
GroupId.parseNullable("__textsecure_group__!0001020305060708090bODD_HEX");
}
@Test(expected = AssertionError.class)
public void bad_encoding__bad_prefix__parse() {
GroupId.parse("__BAD_PREFIX__!0001020305060708090b0c0d0e0f");
}
@Test(expected = AssertionError.class)
public void bad_encoding__odd_hex__parse() {
GroupId.parse("__textsecure_group__!0001020305060708090b0c0d0e0fODD_HEX");
}
@Test
public void get_bytes() {
byte[] bytes = { 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15 };
GroupId groupId = GroupId.v1(bytes);
assertArrayEquals(bytes, groupId.getDecodedId());
}
@Test
public void equality() {
GroupId groupId1 = GroupId.v1(new byte[]{ 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15 });
GroupId groupId2 = GroupId.v1(new byte[]{ 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15 });
assertNotSame(groupId1, groupId2);
assertEquals(groupId1, groupId2);
assertEquals(groupId1.hashCode(), groupId2.hashCode());
}
@Test
public void inequality_by_bytes() {
GroupId groupId1 = GroupId.v1(new byte[]{ 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15 });
GroupId groupId2 = GroupId.v1(new byte[]{ 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 14, 16 });
assertNotSame(groupId1, groupId2);
assertNotEquals(groupId1, groupId2);
assertNotEquals(groupId1.hashCode(), groupId2.hashCode());
}
@Test
public void inequality_of_sms_and_mms() {
GroupId groupId1 = GroupId.v1(new byte[]{ 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15 });
GroupId groupId2 = GroupId.mms(new byte[]{ 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15 });
assertNotSame(groupId1, groupId2);
assertNotEquals(groupId1, groupId2);
assertNotEquals(groupId1.hashCode(), groupId2.hashCode());
}
@Test
public void inequality_with_null() {
GroupId groupId = GroupId.v1(new byte[]{ 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15 });
assertNotEquals(groupId, null);
}
}