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

View file

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

View file

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

View file

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

View file

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

View file

@ -1140,7 +1140,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private void handleEditPushGroup() { private void handleEditPushGroup() {
Intent intent = new Intent(ConversationActivity.this, GroupCreateActivity.class); 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); startActivityForResult(intent, GROUP_EDIT);
} }

View file

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

View file

@ -17,22 +17,20 @@ import net.sqlcipher.database.SQLiteDatabase;
import org.signal.zkgroup.profiles.ProfileKey; import org.signal.zkgroup.profiles.ProfileKey;
import org.signal.zkgroup.profiles.ProfileKeyCredential; import org.signal.zkgroup.profiles.ProfileKeyCredential;
import org.thoughtcrime.securesms.color.MaterialColor; 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.IdentityDatabase.IdentityRecord;
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper; import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; 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.logging.Log;
import org.thoughtcrime.securesms.profiles.ProfileName; import org.thoughtcrime.securesms.profiles.ProfileName;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.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.Base64;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.IdentityUtil; import org.thoughtcrime.securesms.util.IdentityUtil;
import org.thoughtcrime.securesms.util.SqlUtil; import org.thoughtcrime.securesms.util.SqlUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.IdentityKey; import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.InvalidKeyException; import org.whispersystems.libsignal.InvalidKeyException;
@ -352,13 +350,13 @@ public class RecipientDatabase extends Database {
return getOrInsertByColumn(EMAIL, email).recipientId; return getOrInsertByColumn(EMAIL, email).recipientId;
} }
public @NonNull RecipientId getOrInsertFromGroupId(@NonNull String groupId) { public @NonNull RecipientId getOrInsertFromGroupId(@NonNull GroupId groupId) {
GetOrInsertResult result = getOrInsertByColumn(GROUP_ID, groupId); GetOrInsertResult result = getOrInsertByColumn(GROUP_ID, groupId.toString());
if (result.neededInsert) { if (result.neededInsert) {
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
if (GroupUtil.isMmsGroup(groupId)) { if (groupId.isMmsGroup()) {
values.put(GROUP_TYPE, GroupType.MMS.getId()); values.put(GROUP_TYPE, GroupType.MMS.getId());
} else { } else {
values.put(GROUP_TYPE, GroupType.SIGNAL_V1.getId()); values.put(GROUP_TYPE, GroupType.SIGNAL_V1.getId());
@ -563,7 +561,7 @@ public class RecipientDatabase extends Database {
for (SignalGroupV1Record insert : groupV1Inserts) { for (SignalGroupV1Record insert : groupV1Inserts) {
db.insertOrThrow(TABLE_NAME, null, getValuesForStorageGroupV1(insert)); 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()); threadDatabase.setArchived(recipient.getId(), insert.isArchived());
recipient.live().refresh(); 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!"); 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()); threadDatabase.setArchived(recipient.getId(), update.getNew().isArchived());
recipient.live().refresh(); recipient.live().refresh();
@ -672,7 +670,7 @@ public class RecipientDatabase extends Database {
private static @NonNull ContentValues getValuesForStorageGroupV1(@NonNull SignalGroupV1Record groupV1) { private static @NonNull ContentValues getValuesForStorageGroupV1(@NonNull SignalGroupV1Record groupV1) {
ContentValues values = new ContentValues(); 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(GROUP_TYPE, GroupType.SIGNAL_V1.getId());
values.put(PROFILE_SHARING, groupV1.isProfileSharingEnabled() ? "1" : "0"); values.put(PROFILE_SHARING, groupV1.isProfileSharingEnabled() ? "1" : "0");
values.put(BLOCKED, groupV1.isBlocked() ? "1" : "0"); values.put(BLOCKED, groupV1.isBlocked() ? "1" : "0");
@ -729,13 +727,13 @@ public class RecipientDatabase extends Database {
return out; return out;
} }
private @NonNull RecipientSettings getRecipientSettings(@NonNull Cursor cursor) { private static @NonNull RecipientSettings getRecipientSettings(@NonNull Cursor cursor) {
long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID)); long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID));
UUID uuid = UuidUtil.parseOrNull(cursor.getString(cursor.getColumnIndexOrThrow(UUID))); UUID uuid = UuidUtil.parseOrNull(cursor.getString(cursor.getColumnIndexOrThrow(UUID)));
String username = cursor.getString(cursor.getColumnIndexOrThrow(USERNAME)); String username = cursor.getString(cursor.getColumnIndexOrThrow(USERNAME));
String e164 = cursor.getString(cursor.getColumnIndexOrThrow(PHONE)); String e164 = cursor.getString(cursor.getColumnIndexOrThrow(PHONE));
String email = cursor.getString(cursor.getColumnIndexOrThrow(EMAIL)); 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)); int groupType = cursor.getInt(cursor.getColumnIndexOrThrow(GROUP_TYPE));
boolean blocked = cursor.getInt(cursor.getColumnIndexOrThrow(BLOCKED)) == 1; boolean blocked = cursor.getInt(cursor.getColumnIndexOrThrow(BLOCKED)) == 1;
String messageRingtone = cursor.getString(cursor.getColumnIndexOrThrow(MESSAGE_RINGTONE)); 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 }); 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) { for (GroupId groupId : groupIdStrings) {
db.update(TABLE_NAME, setBlocked, GROUP_ID + " = ?", new String[] { groupId }); db.update(TABLE_NAME, setBlocked, GROUP_ID + " = ?", new String[] { groupId.toString() });
} }
db.setTransactionSuccessful(); db.setTransactionSuccessful();
@ -1637,7 +1635,7 @@ public class RecipientDatabase extends Database {
private final String username; private final String username;
private final String e164; private final String e164;
private final String email; private final String email;
private final String groupId; private final GroupId groupId;
private final GroupType groupType; private final GroupType groupType;
private final boolean blocked; private final boolean blocked;
private final long muteUntil; private final long muteUntil;
@ -1673,7 +1671,7 @@ public class RecipientDatabase extends Database {
@Nullable String username, @Nullable String username,
@Nullable String e164, @Nullable String e164,
@Nullable String email, @Nullable String email,
@Nullable String groupId, @Nullable GroupId groupId,
@NonNull GroupType groupType, @NonNull GroupType groupType,
boolean blocked, boolean blocked,
long muteUntil, long muteUntil,
@ -1761,7 +1759,7 @@ public class RecipientDatabase extends Database {
return email; return email;
} }
public @Nullable String getGroupId() { public @Nullable GroupId getGroupId() {
return groupId; return groupId;
} }

View file

@ -20,6 +20,7 @@ import android.content.Context;
import android.database.Cursor; import android.database.Cursor;
import android.database.sqlite.SQLiteException; import android.database.sqlite.SQLiteException;
import android.net.Uri; import android.net.Uri;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.annimon.stream.Stream; import com.annimon.stream.Stream;
@ -27,6 +28,7 @@ import com.annimon.stream.Stream;
import net.sqlcipher.database.SQLiteDatabase; import net.sqlcipher.database.SQLiteDatabase;
import net.sqlcipher.database.SQLiteStatement; import net.sqlcipher.database.SQLiteStatement;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.recipients.RecipientId;
@ -231,7 +233,7 @@ public class SmsMigrator {
List<RecipientId> recipientIds = Stream.of(ourRecipients).map(Recipient::getId).toList(); 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); RecipientId ourGroupRecipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(ourGroupId);
Recipient ourGroupRecipient = Recipient.resolved(ourGroupRecipientId); Recipient ourGroupRecipient = Recipient.resolved(ourGroupRecipientId);
long ourThreadId = threadDatabase.getThreadIdFor(ourGroupRecipient, ThreadDatabase.DistributionTypes.CONVERSATION); 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.database.sqlite.SQLiteOpenHelper;
import android.net.Uri; import android.net.Uri;
import android.provider.ContactsContract; import android.provider.ContactsContract;
import androidx.annotation.Nullable;
import android.text.TextUtils; import android.text.TextUtils;
import org.thoughtcrime.securesms.logging.Log;
import androidx.annotation.Nullable;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.i18n.phonenumbers.NumberParseException; 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.RecipientDatabase;
import org.thoughtcrime.securesms.database.SmsDatabase; import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.database.ThreadDatabase; 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.migrations.LegacyMigrationJob;
import org.thoughtcrime.securesms.notifications.MessageNotifier; import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.permissions.Permissions; import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.phonenumbers.NumberUtil; import org.thoughtcrime.securesms.phonenumbers.NumberUtil;
import org.thoughtcrime.securesms.util.Base64; import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.DelimiterUtil; import org.thoughtcrime.securesms.util.DelimiterUtil;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.Hex; import org.thoughtcrime.securesms.util.Hex;
import org.thoughtcrime.securesms.util.JsonUtils; import org.thoughtcrime.securesms.util.JsonUtils;
import org.thoughtcrime.securesms.util.MediaUtil; import org.thoughtcrime.securesms.util.MediaUtil;
@ -1274,7 +1275,7 @@ public class ClassicOpenHelper extends SQLiteOpenHelper {
while (cursor != null && cursor.moveToNext()) { while (cursor != null && cursor.moveToNext()) {
String address = cursor.getString(cursor.getColumnIndexOrThrow("recipient_ids")); 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)); 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, 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 net.sqlcipher.database.SQLiteDatabase;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.phonenumbers.NumberUtil; import org.thoughtcrime.securesms.phonenumbers.NumberUtil;
import org.thoughtcrime.securesms.util.DelimiterUtil; import org.thoughtcrime.securesms.util.DelimiterUtil;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
import java.util.HashSet; import java.util.HashSet;
@ -153,7 +153,7 @@ public class RecipientIdMigrationHelper {
try (Cursor cursor = db.query("recipient_preferences", null, null, null, null, null, null)) { try (Cursor cursor = db.query("recipient_preferences", null, null, null, null, null, null)) {
while (cursor != null && cursor.moveToNext()) { while (cursor != null && cursor.moveToNext()) {
String address = cursor.getString(cursor.getColumnIndexOrThrow("recipient_ids")); 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 isEmail = !isGroup && NumberUtil.isValidEmail(address);
boolean isPhone = !isGroup && !isEmail; boolean isPhone = !isGroup && !isEmail;

View file

@ -21,7 +21,6 @@ import net.sqlcipher.database.SQLiteDatabase;
import net.sqlcipher.database.SQLiteDatabaseHook; import net.sqlcipher.database.SQLiteDatabaseHook;
import net.sqlcipher.database.SQLiteOpenHelper; import net.sqlcipher.database.SQLiteOpenHelper;
import org.thoughtcrime.securesms.storage.StorageSyncHelper;
import org.thoughtcrime.securesms.crypto.DatabaseSecret; import org.thoughtcrime.securesms.crypto.DatabaseSecret;
import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.AttachmentDatabase; 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.StorageKeyDatabase;
import org.thoughtcrime.securesms.database.ThreadDatabase; import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.jobs.RefreshPreKeysJob; import org.thoughtcrime.securesms.jobs.RefreshPreKeysJob;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.notifications.NotificationChannels; import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter; import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
import org.thoughtcrime.securesms.service.KeyCachingService; import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.storage.StorageSyncHelper;
import org.thoughtcrime.securesms.util.Base64; import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.ServiceUtil; import org.thoughtcrime.securesms.util.ServiceUtil;
import org.thoughtcrime.securesms.util.SqlUtil; import org.thoughtcrime.securesms.util.SqlUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
@ -349,7 +349,7 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
String displayName = NotificationChannels.getChannelDisplayNameFor(context, systemName, profileName, null, address); String displayName = NotificationChannels.getChannelDisplayNameFor(context, systemName, profileName, null, address);
boolean vibrateEnabled = vibrateState == 0 ? TextSecurePreferences.isNotificationVibrateEnabled(context) : vibrateState == 1; 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 })) { try(Cursor groupCursor = db.rawQuery("SELECT title FROM groups WHERE group_id = ?", new String[] { address })) {
if (groupCursor != null && groupCursor.moveToFirst()) { if (groupCursor != null && groupCursor.moveToFirst()) {
String title = groupCursor.getString(groupCursor.getColumnIndexOrThrow("title")); 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, public static GroupActionResult updateGroup(@NonNull Context context,
@NonNull String groupId, @NonNull GroupId groupId,
@NonNull Set<Recipient> members, @NonNull Set<Recipient> members,
@Nullable Bitmap avatar, @Nullable Bitmap avatar,
@Nullable String name) @Nullable String name)
@ -51,7 +51,7 @@ public final class GroupManager {
@WorkerThread @WorkerThread
public static boolean leaveGroup(@NonNull Context context, @NonNull Recipient groupRecipient) { public static boolean leaveGroup(@NonNull Context context, @NonNull Recipient groupRecipient) {
String groupId = groupRecipient.requireGroupId(); GroupId groupId = groupRecipient.requireGroupId();
return V1GroupManager.leaveGroup(context, groupId, groupRecipient); 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.sms.IncomingTextMessage;
import org.thoughtcrime.securesms.util.Base64; import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.FeatureFlags; import org.thoughtcrime.securesms.util.FeatureFlags;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
import org.whispersystems.signalservice.api.messages.SignalServiceContent; import org.whispersystems.signalservice.api.messages.SignalServiceContent;
@ -63,7 +62,7 @@ public class GroupMessageProcessor {
GroupDatabase database = DatabaseFactory.getGroupDatabase(context); GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
SignalServiceGroup group = message.getGroupInfo().get(); SignalServiceGroup group = message.getGroupInfo().get();
String id = GroupUtil.getEncodedId(group.getGroupId(), false); GroupId id = GroupId.v1(group.getGroupId());
Optional<GroupRecord> record = database.getGroup(id); Optional<GroupRecord> record = database.getGroup(id);
if (record.isPresent() && group.getType() == Type.UPDATE) { if (record.isPresent() && group.getType() == Type.UPDATE) {
@ -73,7 +72,7 @@ public class GroupMessageProcessor {
} else if (record.isPresent() && group.getType() == Type.QUIT) { } else if (record.isPresent() && group.getType() == Type.QUIT) {
return handleGroupLeave(context, content, group, record.get(), outgoing); return handleGroupLeave(context, content, group, record.get(), outgoing);
} else if (record.isPresent() && group.getType() == Type.REQUEST_INFO) { } else if (record.isPresent() && group.getType() == Type.REQUEST_INFO) {
return handleGroupInfoRequest(context, content, group, record.get()); return handleGroupInfoRequest(context, content, record.get());
} else { } else {
Log.w(TAG, "Received unknown type, ignoring..."); Log.w(TAG, "Received unknown type, ignoring...");
return null; return null;
@ -86,7 +85,7 @@ public class GroupMessageProcessor {
boolean outgoing) boolean outgoing)
{ {
GroupDatabase database = DatabaseFactory.getGroupDatabase(context); GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
String id = GroupUtil.getEncodedId(group.getGroupId(), false); GroupId id = GroupId.v1(group.getGroupId());
GroupContext.Builder builder = createGroupContext(group); GroupContext.Builder builder = createGroupContext(group);
builder.setType(GroupContext.Type.UPDATE); builder.setType(GroupContext.Type.UPDATE);
@ -106,7 +105,7 @@ public class GroupMessageProcessor {
if (FeatureFlags.messageRequests() && (sender.isSystemContact() || sender.isProfileSharing())) { if (FeatureFlags.messageRequests() && (sender.isSystemContact() || sender.isProfileSharing())) {
Log.i(TAG, "Auto-enabling profile sharing because 'adder' is trusted. contact: " + sender.isSystemContact() + ", profileSharing: " + 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); return storeMessage(context, content, group, builder.build(), outgoing);
@ -120,7 +119,7 @@ public class GroupMessageProcessor {
{ {
GroupDatabase database = DatabaseFactory.getGroupDatabase(context); 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> recordMembers = new HashSet<>(groupRecord.getMembers());
Set<RecipientId> messageMembers = new HashSet<>(); Set<RecipientId> messageMembers = new HashSet<>();
@ -178,13 +177,12 @@ public class GroupMessageProcessor {
private static Long handleGroupInfoRequest(@NonNull Context context, private static Long handleGroupInfoRequest(@NonNull Context context,
@NonNull SignalServiceContent content, @NonNull SignalServiceContent content,
@NonNull SignalServiceGroup group,
@NonNull GroupRecord record) @NonNull GroupRecord record)
{ {
Recipient sender = Recipient.externalPush(context, content.getSender()); Recipient sender = Recipient.externalPush(context, content.getSender());
if (record.getMembers().contains(sender.getId())) { 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; return null;
@ -197,7 +195,7 @@ public class GroupMessageProcessor {
boolean outgoing) boolean outgoing)
{ {
GroupDatabase database = DatabaseFactory.getGroupDatabase(context); GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
String id = GroupUtil.getEncodedId(group.getGroupId(), false); GroupId id = GroupId.v1(group.getGroupId());
List<RecipientId> members = record.getMembers(); List<RecipientId> members = record.getMembers();
GroupContext.Builder builder = createGroupContext(group); GroupContext.Builder builder = createGroupContext(group);
@ -222,13 +220,13 @@ public class GroupMessageProcessor {
{ {
if (group.getAvatar().isPresent()) { if (group.getAvatar().isPresent()) {
ApplicationDependencies.getJobManager() ApplicationDependencies.getJobManager()
.add(new AvatarDownloadJob(group.getGroupId())); .add(new AvatarDownloadJob(GroupId.v1(group.getGroupId())));
} }
try { try {
if (outgoing) { if (outgoing) {
MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(context); 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); Recipient recipient = Recipient.resolved(recipientId);
OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(recipient, storage, null, content.getTimestamp(), 0, false, null, Collections.emptyList(), Collections.emptyList()); OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(recipient, storage, null, content.getTimestamp(), 0, false, null, Collections.emptyList(), Collections.emptyList());
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient); long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
@ -240,7 +238,7 @@ public class GroupMessageProcessor {
} else { } else {
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context); SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
String body = Base64.encodeBytes(storage.toByteArray()); 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); IncomingGroupMessage groupMessage = new IncomingGroupMessage(incoming, storage, body);
Optional<InsertResult> insertResult = smsDatabase.insertMessageInbox(groupMessage); Optional<InsertResult> insertResult = smsDatabase.insertMessageInbox(groupMessage);

View file

@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.groups;
import android.content.Context; import android.content.Context;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.net.Uri; import android.net.Uri;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread; import androidx.annotation.WorkerThread;
@ -11,7 +12,6 @@ import com.google.protobuf.ByteString;
import org.thoughtcrime.securesms.attachments.Attachment; import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.attachments.UriAttachment; import org.thoughtcrime.securesms.attachments.UriAttachment;
import org.thoughtcrime.securesms.blurhash.BlurHash;
import org.thoughtcrime.securesms.database.AttachmentDatabase; import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase; 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.BitmapUtil;
import org.thoughtcrime.securesms.util.GroupUtil; import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.MediaUtil; import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.util.InvalidNumberException; import org.whispersystems.signalservice.api.util.InvalidNumberException;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext; import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext;
import java.io.IOException;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@ -49,7 +47,7 @@ final class V1GroupManager {
{ {
final byte[] avatarBytes = BitmapUtil.toByteArray(avatar); final byte[] avatarBytes = BitmapUtil.toByteArray(avatar);
final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); 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 RecipientId groupRecipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(groupId);
final Recipient groupRecipient = Recipient.resolved(groupRecipientId); final Recipient groupRecipient = Recipient.resolved(groupRecipientId);
@ -67,7 +65,7 @@ final class V1GroupManager {
} }
static GroupActionResult updateGroup(@NonNull Context context, static GroupActionResult updateGroup(@NonNull Context context,
@NonNull String groupId, @NonNull GroupId groupId,
@NonNull Set<RecipientId> memberAddresses, @NonNull Set<RecipientId> memberAddresses,
@Nullable Bitmap avatar, @Nullable Bitmap avatar,
@Nullable String name) @Nullable String name)
@ -81,7 +79,7 @@ final class V1GroupManager {
groupDatabase.updateTitle(groupId, name); groupDatabase.updateTitle(groupId, name);
groupDatabase.updateAvatar(groupId, avatarBytes); groupDatabase.updateAvatar(groupId, avatarBytes);
if (!GroupUtil.isMmsGroup(groupId)) { if (!groupId.isMmsGroup()) {
return sendGroupUpdate(context, groupId, memberAddresses, name, avatarBytes); return sendGroupUpdate(context, groupId, memberAddresses, name, avatarBytes);
} else { } else {
RecipientId groupRecipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(groupId); RecipientId groupRecipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(groupId);
@ -92,48 +90,44 @@ final class V1GroupManager {
} }
private static GroupActionResult sendGroupUpdate(@NonNull Context context, private static GroupActionResult sendGroupUpdate(@NonNull Context context,
@NonNull String groupId, @NonNull GroupId groupId,
@NonNull Set<RecipientId> members, @NonNull Set<RecipientId> members,
@Nullable String groupName, @Nullable String groupName,
@Nullable byte[] avatar) @Nullable byte[] avatar)
{ {
try { Attachment avatarAttachment = null;
Attachment avatarAttachment = null; RecipientId groupRecipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(groupId);
RecipientId groupRecipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(groupId); Recipient groupRecipient = Recipient.resolved(groupRecipientId);
Recipient groupRecipient = Recipient.resolved(groupRecipientId);
List<GroupContext.Member> uuidMembers = new LinkedList<>(); List<GroupContext.Member> uuidMembers = new LinkedList<>();
List<String> e164Members = new LinkedList<>(); List<String> e164Members = new LinkedList<>();
for (RecipientId member : members) { for (RecipientId member : members) {
Recipient recipient = Recipient.resolved(member); Recipient recipient = Recipient.resolved(member);
uuidMembers.add(GroupMessageProcessor.createMember(RecipientUtil.toSignalServiceAddress(context, recipient))); 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);
} }
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 @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); long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipient);
Optional<OutgoingGroupMediaMessage> leaveMessage = GroupUtil.createGroupLeaveMessage(context, groupRecipient); Optional<OutgoingGroupMediaMessage> leaveMessage = GroupUtil.createGroupLeaveMessage(context, groupRecipient);

View file

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

View file

@ -3,12 +3,12 @@ package org.thoughtcrime.securesms.jobs;
import android.content.Context; import android.content.Context;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.WorkerThread;
import com.annimon.stream.Stream; import com.annimon.stream.Stream;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.jobmanager.Data; import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job; import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; 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.recipients.RecipientUtil;
import org.thoughtcrime.securesms.transport.RetryLaterException; import org.thoughtcrime.securesms.transport.RetryLaterException;
import org.thoughtcrime.securesms.util.Base64; import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceMessageSender; import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair; 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.SignalServiceDataMessage;
import org.whispersystems.signalservice.api.messages.SignalServiceGroup; import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
import org.whispersystems.signalservice.api.push.SignalServiceAddress; 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.io.IOException;
import java.util.Collections; 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_MEMBERS = "members";
private static final String KEY_RECIPIENTS = "recipients"; private static final String KEY_RECIPIENTS = "recipients";
private final byte[] groupId; private final GroupId groupId;
private final String name; private final String name;
private final List<RecipientId> members; private final List<RecipientId> members;
private final List<RecipientId> recipients; 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(); List<RecipientId> members = Stream.of(group.resolve().getParticipants()).map(Recipient::getId).toList();
members.remove(Recipient.self().getId()); members.remove(Recipient.self().getId());
return new LeaveGroupJob(GroupUtil.getDecodedIdOrThrow(group.getGroupId().get()), return new LeaveGroupJob(group.getGroupId().get(),
group.resolve().getDisplayName(ApplicationDependencies.getApplication()), group.resolve().getDisplayName(ApplicationDependencies.getApplication()),
members, members,
members, members,
@ -75,7 +72,7 @@ public class LeaveGroupJob extends BaseJob {
.build()); .build());
} }
private LeaveGroupJob(@NonNull byte[] groupId, private LeaveGroupJob(@NonNull GroupId groupId,
@NonNull String name, @NonNull String name,
@NonNull List<RecipientId> members, @NonNull List<RecipientId> members,
@NonNull List<RecipientId> recipients, @NonNull List<RecipientId> recipients,
@ -90,7 +87,7 @@ public class LeaveGroupJob extends BaseJob {
@Override @Override
public @NonNull Data serialize() { 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_GROUP_NAME, name)
.putString(KEY_MEMBERS, RecipientId.toSerializedList(members)) .putString(KEY_MEMBERS, RecipientId.toSerializedList(members))
.putString(KEY_RECIPIENTS, RecipientId.toSerializedList(recipients)) .putString(KEY_RECIPIENTS, RecipientId.toSerializedList(recipients))
@ -128,7 +125,7 @@ public class LeaveGroupJob extends BaseJob {
} }
private static @NonNull List<Recipient> deliver(@NonNull Context context, private static @NonNull List<Recipient> deliver(@NonNull Context context,
@NonNull byte[] groupId, @NonNull GroupId groupId,
@NonNull String name, @NonNull String name,
@NonNull List<RecipientId> members, @NonNull List<RecipientId> members,
@NonNull List<RecipientId> destinations) @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> 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<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(); 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() SignalServiceDataMessage.Builder dataMessage = SignalServiceDataMessage.newBuilder()
.withTimestamp(System.currentTimeMillis()) .withTimestamp(System.currentTimeMillis())
.asGroupMessage(serviceGroup); .asGroupMessage(serviceGroup);
@ -169,7 +166,7 @@ public class LeaveGroupJob extends BaseJob {
public static class Factory implements Job.Factory<LeaveGroupJob> { public static class Factory implements Job.Factory<LeaveGroupJob> {
@Override @Override
public @NonNull LeaveGroupJob create(@NonNull Parameters parameters, @NonNull Data data) { 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), data.getString(KEY_GROUP_NAME),
RecipientId.fromSerializedList(data.getString(KEY_MEMBERS)), RecipientId.fromSerializedList(data.getString(KEY_MEMBERS)),
RecipientId.fromSerializedList(data.getString(KEY_RECIPIENTS)), RecipientId.fromSerializedList(data.getString(KEY_RECIPIENTS)),

View file

@ -1,6 +1,7 @@
package org.thoughtcrime.securesms.jobs; package org.thoughtcrime.securesms.jobs;
import android.net.Uri; import android.net.Uri;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; 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.DatabaseFactory;
import org.thoughtcrime.securesms.database.MessagingDatabase.InsertResult; import org.thoughtcrime.securesms.database.MessagingDatabase.InsertResult;
import org.thoughtcrime.securesms.database.MmsDatabase; import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.jobmanager.Data; import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job; import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
@ -177,11 +179,11 @@ public class MmsDownloadJob extends BaseJob {
int subscriptionId, @Nullable RecipientId notificationFrom) int subscriptionId, @Nullable RecipientId notificationFrom)
throws MmsException throws MmsException
{ {
MmsDatabase database = DatabaseFactory.getMmsDatabase(context); MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
Optional<String> group = Optional.absent(); Optional<GroupId> group = Optional.absent();
Set<RecipientId> members = new HashSet<>(); Set<RecipientId> members = new HashSet<>();
String body = null; String body = null;
List<Attachment> attachments = new LinkedList<>(); List<Attachment> attachments = new LinkedList<>();
RecipientId from = null; 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.logging.Log;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientUtil; import org.thoughtcrime.securesms.recipients.RecipientUtil;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.signalservice.api.SignalServiceMessageSender; import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
@ -76,7 +75,7 @@ public class MultiDeviceBlockedUpdateJob extends BaseJob {
while ((recipient = reader.getNext()) != null) { while ((recipient = reader.getNext()) != null) {
if (recipient.isPushGroup()) { if (recipient.isPushGroup()) {
blockedGroups.add(GroupUtil.getDecodedId(recipient.requireGroupId())); blockedGroups.add(recipient.requireGroupId().getDecodedId());
} else if (recipient.hasServiceIdentifier()) { } else if (recipient.hasServiceIdentifier()) {
blockedIndividuals.add(RecipientUtil.toSignalServiceAddress(context, recipient)); 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.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.recipients.RecipientUtil; import org.thoughtcrime.securesms.recipients.RecipientUtil;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceMessageSender; import org.whispersystems.signalservice.api.SignalServiceMessageSender;
@ -92,13 +91,13 @@ public class MultiDeviceGroupUpdateJob extends BaseJob {
members.add(RecipientUtil.toSignalServiceAddress(context, Recipient.resolved(member))); 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); Recipient recipient = Recipient.resolved(recipientId);
Optional<Integer> expirationTimer = recipient.getExpireMessages() > 0 ? Optional.of(recipient.getExpireMessages()) : Optional.absent(); Optional<Integer> expirationTimer = recipient.getExpireMessages() > 0 ? Optional.of(recipient.getExpireMessages()) : Optional.absent();
Map<RecipientId, Integer> inboxPositions = DatabaseFactory.getThreadDatabase(context).getInboxPositions(); Map<RecipientId, Integer> inboxPositions = DatabaseFactory.getThreadDatabase(context).getInboxPositions();
Set<RecipientId> archived = DatabaseFactory.getThreadDatabase(context).getArchivedRecipients(); Set<RecipientId> archived = DatabaseFactory.getThreadDatabase(context).getArchivedRecipients();
out.write(new DeviceGroup(record.getId(), out.write(new DeviceGroup(record.getId().getDecodedId(),
Optional.fromNullable(record.getTitle()), Optional.fromNullable(record.getTitle()),
members, members,
getAvatar(record.getAvatar()), 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.Data;
import org.thoughtcrime.securesms.jobmanager.Job; import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.recipients.RecipientUtil; import org.thoughtcrime.securesms.recipients.RecipientUtil;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceMessageSender; import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; 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.MessageRequestResponseMessage;
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage; import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
@ -100,7 +95,7 @@ public class MultiDeviceMessageRequestResponseJob extends BaseJob {
MessageRequestResponseMessage response; MessageRequestResponseMessage response;
if (recipient.isGroup()) { if (recipient.isGroup()) {
response = MessageRequestResponseMessage.forGroup(GroupUtil.getDecodedId(recipient.getGroupId().get()), localToRemoteType(type)); response = MessageRequestResponseMessage.forGroup(recipient.getGroupId().get().getDecodedId(), localToRemoteType(type));
} else { } else {
response = MessageRequestResponseMessage.forIndividual(RecipientUtil.toSignalServiceAddress(context, recipient), localToRemoteType(type)); 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.NoSuchMessageException;
import org.thoughtcrime.securesms.database.PushDatabase; import org.thoughtcrime.securesms.database.PushDatabase;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.jobmanager.Data; import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job; import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.JobManager; import org.thoughtcrime.securesms.jobmanager.JobManager;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.notifications.NotificationChannels; import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.thoughtcrime.securesms.transport.RetryLaterException; import org.thoughtcrime.securesms.transport.RetryLaterException;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.state.SignalProtocolStore; import org.whispersystems.libsignal.state.SignalProtocolStore;
import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.libsignal.util.guava.Optional;
@ -233,7 +233,7 @@ public final class PushDecryptMessageJob extends BaseJob {
return new PushProcessMessageJob.ExceptionMetadata(sender, return new PushProcessMessageJob.ExceptionMetadata(sender,
e.getSenderDevice(), 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 { private static PushProcessMessageJob.ExceptionMetadata toExceptionMetadata(@NonNull ProtocolException e) throws NoSenderException {

View file

@ -1,6 +1,7 @@
package org.thoughtcrime.securesms.jobs; package org.thoughtcrime.securesms.jobs;
import android.content.Context; import android.content.Context;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread; import androidx.annotation.WorkerThread;
@ -18,6 +19,7 @@ import org.thoughtcrime.securesms.database.NoSuchMessageException;
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch; import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
import org.thoughtcrime.securesms.database.documents.NetworkFailure; import org.thoughtcrime.securesms.database.documents.NetworkFailure;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.jobmanager.Data; import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job; import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.JobManager; 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.recipients.RecipientUtil;
import org.thoughtcrime.securesms.transport.RetryLaterException; import org.thoughtcrime.securesms.transport.RetryLaterException;
import org.thoughtcrime.securesms.transport.UndeliverableMessageException; 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.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceMessageSender; import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair; import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
@ -242,7 +242,7 @@ public class PushGroupSendJob extends PushSendJob {
rotateSenderCertificateIfNecessary(); rotateSenderCertificateIfNecessary();
SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender(); SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
String groupId = groupRecipient.requireGroupId(); GroupId groupId = groupRecipient.requireGroupId();
Optional<byte[]> profileKey = getProfileKey(groupRecipient); Optional<byte[]> profileKey = getProfileKey(groupRecipient);
Optional<Quote> quote = getQuoteFor(message); Optional<Quote> quote = getQuoteFor(message);
Optional<SignalServiceDataMessage.Sticker> sticker = getStickerFor(message); Optional<SignalServiceDataMessage.Sticker> sticker = getStickerFor(message);
@ -266,7 +266,7 @@ public class PushGroupSendJob extends PushSendJob {
List<SignalServiceAddress> members = Stream.of(groupContext.getMembersList()) List<SignalServiceAddress> members = Stream.of(groupContext.getMembersList())
.map(m -> new SignalServiceAddress(UuidUtil.parseOrNull(m.getUuid()), m.getE164())) .map(m -> new SignalServiceAddress(UuidUtil.parseOrNull(m.getUuid()), m.getE164()))
.toList(); .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() SignalServiceDataMessage groupDataMessage = SignalServiceDataMessage.newBuilder()
.withTimestamp(message.getSentTimeMillis()) .withTimestamp(message.getSentTimeMillis())
.withExpiration(groupRecipient.getExpireMessages()) .withExpiration(groupRecipient.getExpireMessages())
@ -275,7 +275,7 @@ public class PushGroupSendJob extends PushSendJob {
return messageSender.sendMessage(addresses, unidentifiedAccess, isRecipientUpdate, groupDataMessage); return messageSender.sendMessage(addresses, unidentifiedAccess, isRecipientUpdate, groupDataMessage);
} else { } else {
SignalServiceGroup group = new SignalServiceGroup(GroupUtil.getDecodedId(groupId)); SignalServiceGroup group = new SignalServiceGroup(groupId.getDecodedId());
SignalServiceDataMessage groupMessage = SignalServiceDataMessage.newBuilder() SignalServiceDataMessage groupMessage = SignalServiceDataMessage.newBuilder()
.withTimestamp(message.getSentTimeMillis()) .withTimestamp(message.getSentTimeMillis())
.asGroupMessage(group) .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); List<GroupReceiptInfo> destinations = DatabaseFactory.getGroupReceiptDatabase(context).getGroupReceiptInfo(messageId);
if (!destinations.isEmpty()) return Stream.of(destinations).map(GroupReceiptInfo::getRecipientId).toList(); 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;
import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord; import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.jobmanager.Data; import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job; import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; 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.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.recipients.RecipientUtil; import org.thoughtcrime.securesms.recipients.RecipientUtil;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceMessageSender; import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; 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.messages.SignalServiceGroup.Type;
import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; 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.ByteArrayInputStream;
import java.io.IOException; 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_SOURCE = "source";
private static final String KEY_GROUP_ID = "group_id"; private static final String KEY_GROUP_ID = "group_id";
private RecipientId source; private final RecipientId source;
private byte[] groupId; private final GroupId groupId;
public PushGroupUpdateJob(@NonNull RecipientId source, byte[] groupId) { public PushGroupUpdateJob(@NonNull RecipientId source, @NonNull GroupId groupId) {
this(new Job.Parameters.Builder() this(new Job.Parameters.Builder()
.addConstraint(NetworkConstraint.KEY) .addConstraint(NetworkConstraint.KEY)
.setLifespan(TimeUnit.DAYS.toMillis(1)) .setLifespan(TimeUnit.DAYS.toMillis(1))
@ -57,7 +55,7 @@ public class PushGroupUpdateJob extends BaseJob {
groupId); groupId);
} }
private PushGroupUpdateJob(@NonNull Job.Parameters parameters, RecipientId source, byte[] groupId) { private PushGroupUpdateJob(@NonNull Job.Parameters parameters, RecipientId source, @NonNull GroupId groupId) {
super(parameters); super(parameters);
this.source = source; this.source = source;
@ -67,7 +65,7 @@ public class PushGroupUpdateJob extends BaseJob {
@Override @Override
public @NonNull Data serialize() { public @NonNull Data serialize() {
return new Data.Builder().putString(KEY_SOURCE, source.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(); .build();
} }
@ -79,11 +77,11 @@ public class PushGroupUpdateJob extends BaseJob {
@Override @Override
public void onRun() throws IOException, UntrustedIdentityException { public void onRun() throws IOException, UntrustedIdentityException {
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
Optional<GroupRecord> record = groupDatabase.getGroup(GroupUtil.getEncodedId(groupId, false)); Optional<GroupRecord> record = groupDatabase.getGroup(groupId);
SignalServiceAttachment avatar = null; SignalServiceAttachment avatar = null;
if (record == 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; return;
} }
@ -104,12 +102,12 @@ public class PushGroupUpdateJob extends BaseJob {
SignalServiceGroup groupContext = SignalServiceGroup.newBuilder(Type.UPDATE) SignalServiceGroup groupContext = SignalServiceGroup.newBuilder(Type.UPDATE)
.withAvatar(avatar) .withAvatar(avatar)
.withId(groupId) .withId(groupId.getDecodedId())
.withMembers(members) .withMembers(members)
.withName(record.get().getTitle()) .withName(record.get().getTitle())
.build(); .build();
RecipientId groupRecipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(GroupUtil.getEncodedId(groupId, false)); RecipientId groupRecipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(groupId);
Recipient groupRecipient = Recipient.resolved(groupRecipientId); Recipient groupRecipient = Recipient.resolved(groupRecipientId);
SignalServiceDataMessage message = SignalServiceDataMessage.newBuilder() SignalServiceDataMessage message = SignalServiceDataMessage.newBuilder()
@ -139,13 +137,9 @@ public class PushGroupUpdateJob extends BaseJob {
public static final class Factory implements Job.Factory<PushGroupUpdateJob> { public static final class Factory implements Job.Factory<PushGroupUpdateJob> {
@Override @Override
public @NonNull PushGroupUpdateJob create(@NonNull Parameters parameters, @NonNull org.thoughtcrime.securesms.jobmanager.Data data) { public @NonNull PushGroupUpdateJob create(@NonNull Parameters parameters, @NonNull org.thoughtcrime.securesms.jobmanager.Data data) {
try { return new PushGroupUpdateJob(parameters,
return new PushGroupUpdateJob(parameters, RecipientId.from(data.getString(KEY_SOURCE)),
RecipientId.from(data.getString(KEY_SOURCE)), GroupId.parse(data.getString(KEY_GROUP_ID)));
GroupUtil.getDecodedId(data.getString(KEY_GROUP_ID)));
} catch (IOException e) {
throw new AssertionError(e);
}
} }
} }
} }

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.ReactionRecord;
import org.thoughtcrime.securesms.database.model.StickerRecord; import org.thoughtcrime.securesms.database.model.StickerRecord;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.groups.GroupMessageProcessor; import org.thoughtcrime.securesms.groups.GroupMessageProcessor;
import org.thoughtcrime.securesms.jobmanager.Data; import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job; 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.notifications.MessageNotifier;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.service.WebRtcCallService;
import org.thoughtcrime.securesms.ringrtc.IceCandidateParcel; import org.thoughtcrime.securesms.ringrtc.IceCandidateParcel;
import org.thoughtcrime.securesms.ringrtc.RemotePeer; import org.thoughtcrime.securesms.ringrtc.RemotePeer;
import org.thoughtcrime.securesms.service.WebRtcCallService;
import org.thoughtcrime.securesms.sms.IncomingEncryptedMessage; import org.thoughtcrime.securesms.sms.IncomingEncryptedMessage;
import org.thoughtcrime.securesms.sms.IncomingEndSessionMessage; import org.thoughtcrime.securesms.sms.IncomingEndSessionMessage;
import org.thoughtcrime.securesms.sms.IncomingTextMessage; 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.stickers.StickerLocator;
import org.thoughtcrime.securesms.storage.StorageSyncHelper; import org.thoughtcrime.securesms.storage.StorageSyncHelper;
import org.thoughtcrime.securesms.util.Base64; import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.Hex; import org.thoughtcrime.securesms.util.Hex;
import org.thoughtcrime.securesms.util.IdentityUtil; import org.thoughtcrime.securesms.util.IdentityUtil;
import org.thoughtcrime.securesms.util.MediaUtil; import org.thoughtcrime.securesms.util.MediaUtil;
@ -217,7 +217,7 @@ public final class PushProcessMessageJob extends BaseJob {
//noinspection ConstantConditions //noinspection ConstantConditions
dataBuilder.putString(KEY_EXCEPTION_SENDER, exceptionMetadata.sender) dataBuilder.putString(KEY_EXCEPTION_SENDER, exceptionMetadata.sender)
.putInt(KEY_EXCEPTION_DEVICE, exceptionMetadata.senderDevice) .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(); return dataBuilder.build();
@ -272,7 +272,7 @@ public final class PushProcessMessageJob extends BaseJob {
else if (isMediaMessage) handleMediaMessage(content, message, smsMessageId); else if (isMediaMessage) handleMediaMessage(content, message, smsMessageId);
else if (message.getBody().isPresent()) handleTextMessage(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()); 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) { private static @NonNull Optional<GroupId> toEncodedId(@NonNull Optional<SignalServiceGroup> groupInfo) {
return groupInfo.transform(g -> GroupUtil.getEncodedId(g.getGroupId(), false)); return groupInfo.transform(g -> GroupId.v1(g.getGroupId()));
} }
private void handleExceptionMessage(@NonNull ExceptionMetadata e, @NonNull Optional<Long> smsMessageId) { private void handleExceptionMessage(@NonNull ExceptionMetadata e, @NonNull Optional<Long> smsMessageId) {
@ -546,7 +546,7 @@ public final class PushProcessMessageJob extends BaseJob {
@NonNull SignalServiceGroup group) @NonNull SignalServiceGroup group)
{ {
if (group.getType() != SignalServiceGroup.Type.REQUEST_INFO) { 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 { } else {
Log.w(TAG, "Received a REQUEST_INFO message for a group we don't know about. Ignoring."); 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()) { if (response.getPerson().isPresent()) {
recipient = Recipient.externalPush(context, response.getPerson().get()); recipient = Recipient.externalPush(context, response.getPerson().get());
} else if (response.getGroupId().isPresent()) { } 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); recipient = Recipient.externalGroup(context, groupId);
} else { } else {
Log.w(TAG, "Message request response was missing a thread recipient! Skipping."); 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); 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()); handleUnknownGroupMessage(content, message.getMessage().getGroupInfo().get());
} }
@ -1017,7 +1017,7 @@ public final class PushProcessMessageJob extends BaseJob {
updateGroupReceiptStatus(message, record.getId(), recipient.requireGroupId()); 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); GroupReceiptDatabase receiptDatabase = DatabaseFactory.getGroupReceiptDatabase(context);
List<Recipient> messageRecipients = Stream.of(message.getRecipients()).map(address -> Recipient.externalPush(context, address)).toList(); List<Recipient> messageRecipients = Stream.of(message.getRecipients()).map(address -> Recipient.externalPush(context, address)).toList();
List<Recipient> members = DatabaseFactory.getGroupDatabase(context).getGroupMembers(groupString, false); 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, private void handleUnsupportedDataMessage(@NonNull String sender,
int senderDevice, int senderDevice,
@NonNull Optional<String> groupId, @NonNull Optional<GroupId> groupId,
long timestamp, long timestamp,
@NonNull Optional<Long> smsMessageId) @NonNull Optional<Long> smsMessageId)
{ {
@ -1203,7 +1203,7 @@ public final class PushProcessMessageJob extends BaseJob {
private void handleInvalidMessage(@NonNull SignalServiceAddress sender, private void handleInvalidMessage(@NonNull SignalServiceAddress sender,
int senderDevice, int senderDevice,
@NonNull Optional<String> groupId, @NonNull Optional<GroupId> groupId,
long timestamp, long timestamp,
@NonNull Optional<Long> smsMessageId) @NonNull Optional<Long> smsMessageId)
{ {
@ -1313,7 +1313,7 @@ public final class PushProcessMessageJob extends BaseJob {
long threadId; long threadId;
if (typingMessage.getGroupId().isPresent()) { 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); Recipient groupRecipient = Recipient.resolved(recipientId);
threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipient); threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipient);
@ -1478,7 +1478,7 @@ public final class PushProcessMessageJob extends BaseJob {
return insertPlaceholder(sender, senderDevice, timestamp, Optional.absent()); 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); SmsDatabase database = DatabaseFactory.getSmsDatabase(context);
IncomingTextMessage textMessage = new IncomingTextMessage(Recipient.external(context, sender).getId(), IncomingTextMessage textMessage = new IncomingTextMessage(Recipient.external(context, sender).getId(),
senderDevice, timestamp, "", senderDevice, timestamp, "",
@ -1490,7 +1490,7 @@ public final class PushProcessMessageJob extends BaseJob {
private Recipient getSyncMessageDestination(SentTranscriptMessage message) { private Recipient getSyncMessageDestination(SentTranscriptMessage message) {
if (message.getMessage().getGroupInfo().isPresent()) { 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 { } else {
return Recipient.externalPush(context, message.getDestination().get()); return Recipient.externalPush(context, message.getDestination().get());
} }
@ -1498,7 +1498,7 @@ public final class PushProcessMessageJob extends BaseJob {
private Recipient getMessageDestination(SignalServiceContent content, SignalServiceDataMessage message) { private Recipient getMessageDestination(SignalServiceContent content, SignalServiceDataMessage message) {
if (message.getGroupInfo().isPresent()) { 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 { } else {
return Recipient.externalPush(context, content.getSender()); return Recipient.externalPush(context, content.getSender());
} }
@ -1529,9 +1529,9 @@ public final class PushProcessMessageJob extends BaseJob {
if (conversation.isGroup() && conversation.isBlocked()) { if (conversation.isGroup() && conversation.isBlocked()) {
return true; return true;
} else if (conversation.isGroup()) { } else if (conversation.isGroup()) {
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
Optional<String> groupId = message.getGroupInfo().isPresent() ? Optional.of(GroupUtil.getEncodedId(message.getGroupInfo().get().getGroupId(), false)) Optional<GroupId> groupId = message.getGroupInfo().isPresent() ? Optional.of(GroupId.v1(message.getGroupInfo().get().getGroupId()))
: Optional.absent(); : Optional.absent();
if (groupId.isPresent() && groupDatabase.isUnknownGroup(groupId.get())) { if (groupId.isPresent() && groupDatabase.isUnknownGroup(groupId.get())) {
return false; return false;
@ -1616,7 +1616,7 @@ public final class PushProcessMessageJob extends BaseJob {
} else { } else {
ExceptionMetadata exceptionMetadata = new ExceptionMetadata(data.getString(KEY_EXCEPTION_SENDER), ExceptionMetadata exceptionMetadata = new ExceptionMetadata(data.getString(KEY_EXCEPTION_SENDER),
data.getInt(KEY_EXCEPTION_DEVICE), 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, return new PushProcessMessageJob(parameters,
state, state,
@ -1643,11 +1643,11 @@ public final class PushProcessMessageJob extends BaseJob {
} }
static class ExceptionMetadata { static class ExceptionMetadata {
@NonNull private final String sender; @NonNull private final String sender;
private final int senderDevice; private final int senderDevice;
@Nullable private final String groupId; @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.sender = sender;
this.senderDevice = senderDevice; this.senderDevice = senderDevice;
this.groupId = groupId; 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.RecipientId;
import org.thoughtcrime.securesms.recipients.RecipientUtil; import org.thoughtcrime.securesms.recipients.RecipientUtil;
import org.thoughtcrime.securesms.transport.RetryLaterException; import org.thoughtcrime.securesms.transport.RetryLaterException;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceMessageSender; import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair; import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
@ -217,7 +216,7 @@ public class ReactionSendJob extends BaseJob {
.withReaction(buildReaction(context, reaction, remove, targetAuthor, targetSentTimestamp)); .withReaction(buildReaction(context, reaction, remove, targetAuthor, targetSentTimestamp));
if (conversationRecipient.isGroup()) { 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.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.jobmanager.Data; import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job; import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.recipients.RecipientUtil; 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.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
import org.whispersystems.signalservice.api.messages.SignalServiceGroup; import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
import org.whispersystems.signalservice.api.messages.SignalServiceGroup.Type; 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.api.push.exceptions.PushNetworkException;
import java.io.IOException; 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_SOURCE = "source";
private static final String KEY_GROUP_ID = "group_id"; private static final String KEY_GROUP_ID = "group_id";
private RecipientId source; private final RecipientId source;
private byte[] groupId; 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() this(new Job.Parameters.Builder()
.addConstraint(NetworkConstraint.KEY) .addConstraint(NetworkConstraint.KEY)
.setLifespan(TimeUnit.DAYS.toMillis(1)) .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); super(parameters);
this.source = source; this.source = source;
@ -57,7 +55,7 @@ public class RequestGroupInfoJob extends BaseJob {
@Override @Override
public @NonNull Data serialize() { public @NonNull Data serialize() {
return new Data.Builder().putString(KEY_SOURCE, source.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(); .build();
} }
@ -69,7 +67,7 @@ public class RequestGroupInfoJob extends BaseJob {
@Override @Override
public void onRun() throws IOException, UntrustedIdentityException { public void onRun() throws IOException, UntrustedIdentityException {
SignalServiceGroup group = SignalServiceGroup.newBuilder(Type.REQUEST_INFO) SignalServiceGroup group = SignalServiceGroup.newBuilder(Type.REQUEST_INFO)
.withId(groupId) .withId(groupId.getDecodedId())
.build(); .build();
SignalServiceDataMessage message = SignalServiceDataMessage.newBuilder() SignalServiceDataMessage message = SignalServiceDataMessage.newBuilder()
@ -99,13 +97,9 @@ public class RequestGroupInfoJob extends BaseJob {
@Override @Override
public @NonNull RequestGroupInfoJob create(@NonNull Parameters parameters, @NonNull Data data) { public @NonNull RequestGroupInfoJob create(@NonNull Parameters parameters, @NonNull Data data) {
try { return new RequestGroupInfoJob(parameters,
return new RequestGroupInfoJob(parameters, RecipientId.from(data.getString(KEY_SOURCE)),
RecipientId.from(data.getString(KEY_SOURCE)), GroupId.parse(data.getString(KEY_GROUP_ID)));
GroupUtil.getDecodedId(data.getString(KEY_GROUP_ID)));
} catch (IOException e) {
throw new AssertionError(e);
}
} }
} }
} }

View file

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

View file

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

View file

@ -2,14 +2,13 @@ package org.thoughtcrime.securesms.migrations;
import androidx.annotation.NonNull; 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.Data;
import org.thoughtcrime.securesms.jobmanager.Job; import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.phonenumbers.NumberUtil; import org.thoughtcrime.securesms.phonenumbers.NumberUtil;
import org.thoughtcrime.securesms.profiles.AvatarHelper; import org.thoughtcrime.securesms.profiles.AvatarHelper;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
import java.io.File; import java.io.File;
@ -83,7 +82,7 @@ public class AvatarMigrationJob extends MigrationJob {
} }
private static boolean isValidFileName(@NonNull String name) { 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> { 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.Attachment;
import org.thoughtcrime.securesms.attachments.PointerAttachment; import org.thoughtcrime.securesms.attachments.PointerAttachment;
import org.thoughtcrime.securesms.contactshare.Contact; import org.thoughtcrime.securesms.contactshare.Contact;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.linkpreview.LinkPreview; import org.thoughtcrime.securesms.linkpreview.LinkPreview;
import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
import org.whispersystems.signalservice.api.messages.SignalServiceGroup; import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
@ -19,7 +19,7 @@ import java.util.List;
public class IncomingMediaMessage { public class IncomingMediaMessage {
private final RecipientId from; private final RecipientId from;
private final String groupId; private final GroupId groupId;
private final String body; private final String body;
private final boolean push; private final boolean push;
private final long sentTimeMillis; private final long sentTimeMillis;
@ -35,7 +35,7 @@ public class IncomingMediaMessage {
private final List<LinkPreview> linkPreviews = new LinkedList<>(); private final List<LinkPreview> linkPreviews = new LinkedList<>();
public IncomingMediaMessage(@NonNull RecipientId from, public IncomingMediaMessage(@NonNull RecipientId from,
Optional<String> groupId, Optional<GroupId> groupId,
String body, String body,
long sentTimeMillis, long sentTimeMillis,
List<Attachment> attachments, List<Attachment> attachments,
@ -86,7 +86,7 @@ public class IncomingMediaMessage {
this.quote = quote.orNull(); this.quote = quote.orNull();
this.unidentified = unidentified; 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; else this.groupId = null;
this.attachments.addAll(PointerAttachment.forPointers(attachments)); this.attachments.addAll(PointerAttachment.forPointers(attachments));
@ -114,7 +114,7 @@ public class IncomingMediaMessage {
return from; return from;
} }
public String getGroupId() { public GroupId getGroupId() {
return groupId; 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.Phonenumber;
import com.google.i18n.phonenumbers.ShortNumberInfo; import com.google.i18n.phonenumbers.ShortNumberInfo;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.util.Pair; import org.whispersystems.libsignal.util.Pair;
@ -83,7 +83,7 @@ public class PhoneNumberFormatter {
public String format(@Nullable String number) { public String format(@Nullable String number) {
if (number == null) return "Unknown"; 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(); if (ALPHA_PATTERN.matcher(number).find()) return number.trim();
String bareNumber = number.replaceAll("[^0-9+]", ""); 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;
import org.thoughtcrime.securesms.database.RecipientDatabase.RecipientSettings; import org.thoughtcrime.securesms.database.RecipientDatabase.RecipientSettings;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.util.guava.Optional; 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(); List<Recipient> members = Stream.of(groupRecord.get().getMembers()).filterNot(RecipientId::isUnknown).map(this::fetchRecipientFromDisk).toList();
Optional<Long> avatarId = Optional.absent(); 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; 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.UnidentifiedAccessMode;
import org.thoughtcrime.securesms.database.RecipientDatabase.VibrateState; import org.thoughtcrime.securesms.database.RecipientDatabase.VibrateState;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob; import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.notifications.NotificationChannels; 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.phonenumbers.PhoneNumberFormatter;
import org.thoughtcrime.securesms.profiles.ProfileName; import org.thoughtcrime.securesms.profiles.ProfileName;
import org.thoughtcrime.securesms.util.FeatureFlags; import org.thoughtcrime.securesms.util.FeatureFlags;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.libsignal.util.guava.Preconditions; import org.whispersystems.libsignal.util.guava.Preconditions;
@ -64,7 +64,7 @@ public class Recipient {
private final String username; private final String username;
private final String e164; private final String e164;
private final String email; private final String email;
private final String groupId; private final GroupId groupId;
private final List<Recipient> participants; private final List<Recipient> participants;
private final Optional<Long> groupAvatarId; private final Optional<Long> groupAvatarId;
private final boolean localNumber; private final boolean localNumber;
@ -236,11 +236,7 @@ public class Recipient {
* identifier is a groupId. * identifier is a groupId.
*/ */
@WorkerThread @WorkerThread
public static @NonNull Recipient externalGroup(@NonNull Context context, @NonNull String groupId) { public static @NonNull Recipient externalGroup(@NonNull Context context, @NonNull GroupId groupId) {
if (!GroupUtil.isEncodedGroup(groupId)) {
throw new IllegalArgumentException("Invalid groupId!");
}
return Recipient.resolved(DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(groupId)); return Recipient.resolved(DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(groupId));
} }
@ -274,8 +270,8 @@ public class Recipient {
throw new UuidRecipientError(); throw new UuidRecipientError();
} }
} }
} else if (GroupUtil.isEncodedGroup(identifier)) { } else if (GroupId.isEncodedGroup(identifier)) {
id = db.getOrInsertFromGroupId(identifier); id = db.getOrInsertFromGroupId(GroupId.parse(identifier));
} else if (NumberUtil.isValidEmail(identifier)) { } else if (NumberUtil.isValidEmail(identifier)) {
id = db.getOrInsertFromEmail(identifier); id = db.getOrInsertFromEmail(identifier);
} else { } else {
@ -385,7 +381,7 @@ public class Recipient {
} }
public @Nullable String getName(@NonNull Context context) { 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<>(); List<String> names = new LinkedList<>();
for (Recipient recipient : participants) { for (Recipient recipient : participants) {
@ -443,7 +439,7 @@ public class Recipient {
return Optional.fromNullable(email); return Optional.fromNullable(email);
} }
public @NonNull Optional<String> getGroupId() { public @NonNull Optional<GroupId> getGroupId() {
return Optional.fromNullable(groupId); return Optional.fromNullable(groupId);
} }
@ -495,8 +491,8 @@ public class Recipient {
return getUuid().isPresent(); return getUuid().isPresent();
} }
public @NonNull String requireGroupId() { public @NonNull GroupId requireGroupId() {
String resolved = resolving ? resolve().groupId : groupId; GroupId resolved = resolving ? resolve().groupId : groupId;
if (resolved == null) { if (resolved == null) {
throw new MissingAddressError(); throw new MissingAddressError();
@ -532,7 +528,7 @@ public class Recipient {
Recipient resolved = resolving ? resolve() : this; Recipient resolved = resolving ? resolve() : this;
if (resolved.isGroup()) { if (resolved.isGroup()) {
return resolved.requireGroupId(); return resolved.requireGroupId().toString();
} else if (resolved.getUuid().isPresent()) { } else if (resolved.getUuid().isPresent()) {
return resolved.getUuid().get().toString(); return resolved.getUuid().get().toString();
} }
@ -570,13 +566,13 @@ public class Recipient {
} }
public boolean isMmsGroup() { public boolean isMmsGroup() {
String groupId = resolve().groupId; GroupId groupId = resolve().groupId;
return groupId != null && GroupUtil.isMmsGroup(groupId); return groupId != null && groupId.isMmsGroup();
} }
public boolean isPushGroup() { public boolean isPushGroup() {
String groupId = resolve().groupId; GroupId groupId = resolve().groupId;
return groupId != null && !GroupUtil.isMmsGroup(groupId); return groupId != null && !groupId.isMmsGroup();
} }
public @NonNull List<Recipient> getParticipants() { 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.RegisteredState;
import org.thoughtcrime.securesms.database.RecipientDatabase.UnidentifiedAccessMode; import org.thoughtcrime.securesms.database.RecipientDatabase.UnidentifiedAccessMode;
import org.thoughtcrime.securesms.database.RecipientDatabase.VibrateState; import org.thoughtcrime.securesms.database.RecipientDatabase.VibrateState;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.profiles.ProfileName; import org.thoughtcrime.securesms.profiles.ProfileName;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
@ -28,7 +29,7 @@ public class RecipientDetails {
final String username; final String username;
final String e164; final String e164;
final String email; final String email;
final String groupId; final GroupId groupId;
final String name; final String name;
final String customLabel; final String customLabel;
final Uri systemContactPhoto; 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.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase; import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.RecipientDatabase.RegisteredState; import org.thoughtcrime.securesms.database.RecipientDatabase.RegisteredState;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.database.ThreadDatabase; 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.DirectoryRefreshJob;
import org.thoughtcrime.securesms.jobs.LeaveGroupJob; import org.thoughtcrime.securesms.jobs.LeaveGroupJob;
import org.thoughtcrime.securesms.jobs.MultiDeviceBlockedUpdateJob; 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.jobs.RotateProfileKeyJob;
import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
import org.thoughtcrime.securesms.storage.StorageSyncHelper; import org.thoughtcrime.securesms.storage.StorageSyncHelper;
import org.thoughtcrime.securesms.util.FeatureFlags; import org.thoughtcrime.securesms.util.FeatureFlags;
import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
import org.thoughtcrime.securesms.util.GroupUtil; import org.thoughtcrime.securesms.util.GroupUtil;
import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.SignalServiceAddress;
@ -123,7 +124,7 @@ public class RecipientUtil {
ApplicationDependencies.getJobManager().add(LeaveGroupJob.create(recipient)); ApplicationDependencies.getJobManager().add(LeaveGroupJob.create(recipient));
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
String groupId = resolved.requireGroupId(); GroupId groupId = resolved.requireGroupId();
groupDatabase.setActive(groupId, false); groupDatabase.setActive(groupId, false);
groupDatabase.remove(groupId, Recipient.self().getId()); groupDatabase.remove(groupId, Recipient.self().getId());
} else { } else {

View file

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

View file

@ -5,7 +5,7 @@ import androidx.annotation.NonNull;
import com.annimon.stream.Collectors; import com.annimon.stream.Collectors;
import com.annimon.stream.Stream; 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.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.storage.SignalGroupV1Record; import org.whispersystems.signalservice.api.storage.SignalGroupV1Record;
@ -15,15 +15,15 @@ import java.util.Map;
class GroupV1ConflictMerger implements StorageSyncHelper.ConflictMerger<SignalGroupV1Record> { class GroupV1ConflictMerger implements StorageSyncHelper.ConflictMerger<SignalGroupV1Record> {
private final Map<String, SignalGroupV1Record> localByGroupId; private final Map<GroupId, SignalGroupV1Record> localByGroupId;
GroupV1ConflictMerger(@NonNull Collection<SignalGroupV1Record> localOnly) { 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 @Override
public @NonNull Optional<SignalGroupV1Record> getMatching(@NonNull SignalGroupV1Record record) { 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 @Override

View file

@ -5,7 +5,6 @@ import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.database.IdentityDatabase; import org.thoughtcrime.securesms.database.IdentityDatabase;
import org.thoughtcrime.securesms.database.RecipientDatabase.RecipientSettings; import org.thoughtcrime.securesms.database.RecipientDatabase.RecipientSettings;
import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.storage.SignalContactRecord; import org.whispersystems.signalservice.api.storage.SignalContactRecord;
import org.whispersystems.signalservice.api.storage.SignalGroupV1Record; import org.whispersystems.signalservice.api.storage.SignalGroupV1Record;
@ -56,7 +55,7 @@ public final class StorageSyncModels {
throw new AssertionError("Must have a groupId!"); 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()) .setBlocked(recipient.isBlocked())
.setProfileSharingEnabled(recipient.isProfileSharing()) .setProfileSharingEnabled(recipient.isProfileSharing())
.setArchived(archived.contains(recipient.getId())) .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.R;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase; import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage; import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
@ -26,43 +27,16 @@ import java.util.List;
import static org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext; 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 GroupUtil() {
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);
} }
public static byte[] getDecodedId(String groupId) throws IOException { private static final String TAG = Log.tag(GroupUtil.class);
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);
}
@WorkerThread @WorkerThread
public static Optional<OutgoingGroupMediaMessage> createGroupLeaveMessage(@NonNull Context context, @NonNull Recipient groupRecipient) { public static Optional<OutgoingGroupMediaMessage> createGroupLeaveMessage(@NonNull Context context, @NonNull Recipient groupRecipient) {
String encodedGroupId = groupRecipient.requireGroupId(); GroupId encodedGroupId = groupRecipient.requireGroupId();
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
if (!groupDatabase.isActive(encodedGroupId)) { if (!groupDatabase.isActive(encodedGroupId)) {
@ -70,13 +44,7 @@ public class GroupUtil {
return Optional.absent(); return Optional.absent();
} }
ByteString decodedGroupId; ByteString decodedGroupId = ByteString.copyFrom(encodedGroupId.getDecodedId());
try {
decodedGroupId = ByteString.copyFrom(getDecodedId(encodedGroupId));
} catch (IOException e) {
Log.w(TAG, "Failed to decode group ID.", e);
return Optional.absent();
}
GroupContext groupContext = GroupContext.newBuilder() GroupContext groupContext = GroupContext.newBuilder()
.setId(decodedGroupId) .setId(decodedGroupId)

View file

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