Show Profile Name Change update messages.

master
Cody Henthorne 2020-07-15 16:15:15 -04:00 committed by GitHub
parent 6d035c6888
commit 64420ead7c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 150 additions and 13 deletions

View File

@ -134,6 +134,7 @@ public class ConversationUpdateItem extends LinearLayout
else if (messageRecord.isIdentityUpdate()) setIdentityRecord(messageRecord);
else if (messageRecord.isIdentityVerified() ||
messageRecord.isIdentityDefault()) setIdentityVerifyUpdate(messageRecord);
else if (messageRecord.isProfileChange()) setProfileNameChangeRecord(messageRecord);
else throw new AssertionError("Neither group nor log nor joined.");
if (batchSelected.contains(messageRecord)) setSelected(true);
@ -195,6 +196,16 @@ public class ConversationUpdateItem extends LinearLayout
date.setVisibility(GONE);
}
private void setProfileNameChangeRecord(MessageRecord messageRecord) {
icon.setImageDrawable(ContextCompat.getDrawable(getContext(), R.drawable.ic_profile_outline_20));
icon.setColorFilter(getIconTintFilter());
body.setText(messageRecord.getDisplayBody(getContext()));
title.setVisibility(GONE);
body.setVisibility(VISIBLE);
date.setVisibility(GONE);
}
private void setGroupRecord(MessageRecord messageRecord) {
icon.setImageDrawable(ThemeUtil.getThemedDrawable(getContext(), R.attr.menu_group_icon));
icon.clearColorFilter();

View File

@ -46,7 +46,6 @@ import org.thoughtcrime.securesms.database.MmsSmsColumns;
import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.recipients.LiveRecipient;
import org.thoughtcrime.securesms.recipients.Recipient;

View File

@ -27,6 +27,7 @@ import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.SqlUtil;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupUtil;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer;
@ -224,12 +225,22 @@ public final class GroupDatabase extends Database {
@WorkerThread
public @NonNull List<GroupRecord> getPushGroupsContainingMember(@NonNull RecipientId recipientId) {
return getGroupsContainingMember(recipientId, true);
}
@WorkerThread
public @NonNull List<GroupRecord> getGroupsContainingMember(@NonNull RecipientId recipientId, boolean pushOnly) {
SQLiteDatabase database = databaseHelper.getReadableDatabase();
String table = TABLE_NAME + " INNER JOIN " + ThreadDatabase.TABLE_NAME + " ON " + TABLE_NAME + "." + RECIPIENT_ID + " = " + ThreadDatabase.TABLE_NAME + "." + ThreadDatabase.RECIPIENT_ID;
String query = MEMBERS + " LIKE ? AND " + MMS + " = ?";
String[] args = new String[]{"%" + recipientId.serialize() + "%", "0"};
String query = MEMBERS + " LIKE ?";
String[] args = new String[]{"%" + recipientId.serialize() + "%"};
String orderBy = ThreadDatabase.TABLE_NAME + "." + ThreadDatabase.DATE + " DESC";
if (pushOnly) {
query += " AND " + MMS + " = ?";
args = SqlUtil.appendArg(args, "0");
}
List<GroupRecord> groups = new LinkedList<>();
try (Cursor cursor = database.query(table, null, query, args, null, null, orderBy)) {

View File

@ -38,6 +38,7 @@ public interface MmsSmsColumns {
protected static final long JOINED_TYPE = 4;
protected static final long UNSUPPORTED_MESSAGE_TYPE = 5;
protected static final long INVALID_MESSAGE_TYPE = 6;
protected static final long PROFILE_CHANGE_TYPE = 7;
protected static final long BASE_INBOX_TYPE = 20;
protected static final long BASE_OUTBOX_TYPE = 21;
@ -255,6 +256,10 @@ public interface MmsSmsColumns {
(type & ENCRYPTION_REMOTE_BIT) != 0;
}
public static boolean isProfileChange(long type) {
return type == PROFILE_CHANGE_TYPE;
}
public static long translateFromSystemBaseType(long theirType) {
// public static final int NONE_TYPE = 0;
// public static final int INBOX_TYPE = 1;

View File

@ -37,6 +37,7 @@ import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.ReactionRecord;
import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
import org.thoughtcrime.securesms.database.model.databaseprotos.ProfileChangeDetails;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobs.TrimThreadJob;
import org.thoughtcrime.securesms.logging.Log;
@ -45,6 +46,7 @@ import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.sms.IncomingGroupUpdateMessage;
import org.thoughtcrime.securesms.sms.IncomingTextMessage;
import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.JsonUtils;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.util.guava.Optional;
@ -631,6 +633,57 @@ public class SmsDatabase extends MessagingDatabase {
return new Pair<>(messageId, threadId);
}
public void insertProfileNameChangeMessages(@NonNull Recipient recipient, @NonNull String newProfileName, @NonNull String previousProfileName) {
ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context);
List<GroupDatabase.GroupRecord> groupRecords = DatabaseFactory.getGroupDatabase(context).getGroupsContainingMember(recipient.getId(), false);
List<Long> threadIdsToUpdate = new LinkedList<>();
byte[] profileChangeDetails = ProfileChangeDetails.newBuilder()
.setProfileNameChange(ProfileChangeDetails.StringChange.newBuilder()
.setNew(newProfileName)
.setPrevious(previousProfileName))
.build()
.toByteArray();
String body = Base64.encodeBytes(profileChangeDetails);
SQLiteDatabase db = databaseHelper.getWritableDatabase();
db.beginTransaction();
try {
threadIdsToUpdate.add(threadDatabase.getThreadIdFor(recipient.getId()));
for (GroupDatabase.GroupRecord groupRecord : groupRecords) {
threadIdsToUpdate.add(threadDatabase.getThreadIdFor(groupRecord.getRecipientId()));
}
Stream.of(threadIdsToUpdate)
.withoutNulls()
.forEach(threadId -> {
ContentValues values = new ContentValues();
values.put(RECIPIENT_ID, recipient.getId().serialize());
values.put(ADDRESS_DEVICE_ID, 1);
values.put(DATE_RECEIVED, System.currentTimeMillis());
values.put(DATE_SENT, System.currentTimeMillis());
values.put(READ, 1);
values.put(TYPE, Types.PROFILE_CHANGE_TYPE);
values.put(THREAD_ID, threadId);
values.put(BODY, body);
db.insert(TABLE_NAME, null, values);
});
for (long threadId : threadIdsToUpdate) {
DatabaseFactory.getThreadDatabase(context).update(threadId, true);
notifyConversationListeners(threadId);
ApplicationDependencies.getJobManager().add(new TrimThreadJob(threadId));
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
}
protected Optional<InsertResult> insertMessageInbox(IncomingTextMessage message, long type) {
if (message.isJoined()) {
type = (type & (Types.TOTAL_MASK - Types.BASE_TYPE_MASK)) | Types.JOINED_TYPE;

View File

@ -175,14 +175,16 @@ public class ThreadDatabase extends Database {
}
}
ContentValues contentValues = new ContentValues(7);
contentValues.put(DATE, date - date % 1000);
ContentValues contentValues = new ContentValues();
if (!MmsSmsColumns.Types.isProfileChange(type)) {
contentValues.put(DATE, date - date % 1000);
contentValues.put(SNIPPET, body);
contentValues.put(SNIPPET_URI, attachment == null ? null : attachment.toString());
contentValues.put(SNIPPET_TYPE, type);
contentValues.put(SNIPPET_CONTENT_TYPE, contentType);
contentValues.put(SNIPPET_EXTRAS, extraSerialized);
}
contentValues.put(MESSAGE_COUNT, count);
contentValues.put(SNIPPET, body);
contentValues.put(SNIPPET_URI, attachment == null ? null : attachment.toString());
contentValues.put(SNIPPET_TYPE, type);
contentValues.put(SNIPPET_CONTENT_TYPE, contentType);
contentValues.put(SNIPPET_EXTRAS, extraSerialized);
contentValues.put(STATUS, status);
contentValues.put(DELIVERY_RECEIPT_COUNT, deliveryReceiptCount);
contentValues.put(READ_RECEIPT_COUNT, readReceiptCount);
@ -202,8 +204,11 @@ public class ThreadDatabase extends Database {
}
public void updateSnippet(long threadId, String snippet, @Nullable Uri attachment, long date, long type, boolean unarchive) {
ContentValues contentValues = new ContentValues(4);
if (MmsSmsColumns.Types.isProfileChange(type)) {
return;
}
ContentValues contentValues = new ContentValues();
contentValues.put(DATE, date - date % 1000);
contentValues.put(SNIPPET, snippet);
contentValues.put(SNIPPET_TYPE, type);

View File

@ -156,6 +156,10 @@ public abstract class DisplayRecord {
return SmsDatabase.Types.isIdentityDefault(type) || SmsDatabase.Types.isIdentityVerified(type);
}
public boolean isProfileChange() {
return SmsDatabase.Types.isProfileChange(type);
}
public int getDeliveryStatus() {
return deliveryStatus;
}

View File

@ -31,7 +31,9 @@ import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
import org.thoughtcrime.securesms.database.documents.NetworkFailure;
import org.thoughtcrime.securesms.database.model.databaseprotos.DecryptedGroupV2Context;
import org.thoughtcrime.securesms.database.model.databaseprotos.ProfileChangeDetails;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.profiles.ProfileName;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.Base64;
@ -142,6 +144,8 @@ public abstract class MessageRecord extends DisplayRecord {
} else if (isIdentityDefault()) {
if (isOutgoing()) return new SpannableString(context.getString(R.string.MessageRecord_you_marked_your_safety_number_with_s_unverified, getIndividualRecipient().getDisplayName(context)));
else return new SpannableString(context.getString(R.string.MessageRecord_you_marked_your_safety_number_with_s_unverified_from_another_device, getIndividualRecipient().getDisplayName(context)));
} else if (isProfileChange()) {
return new SpannableString(getProfileChangeDescription(context));
}
return new SpannableString(getBody());
@ -177,6 +181,29 @@ public abstract class MessageRecord extends DisplayRecord {
}
}
private @NonNull String getProfileChangeDescription(@NonNull Context context) {
try {
byte[] decoded = Base64.decode(getBody());
ProfileChangeDetails profileChangeDetails = ProfileChangeDetails.parseFrom(decoded);
if (profileChangeDetails.hasProfileNameChange()) {
String displayName = getIndividualRecipient().getDisplayName(context);
String newName = ProfileName.fromSerialized(profileChangeDetails.getProfileNameChange().getNew()).toString();
String previousName = ProfileName.fromSerialized(profileChangeDetails.getProfileNameChange().getPrevious()).toString();
if (getIndividualRecipient().isSystemContact()) {
return context.getString(R.string.MessageRecord_changed_their_profile_name_from_to, displayName, previousName, newName);
} else {
return context.getString(R.string.MessageRecord_changed_their_profile_name_to, previousName, newName);
}
}
} catch (IOException e) {
Log.w(TAG, "Profile name change details could not be read", e);
}
return context.getString(R.string.MessageRecord_changed_their_profile, getIndividualRecipient().getDisplayName(context));
}
/**
* Describes a UUID by it's corresponding recipient's {@link Recipient#getDisplayName(Context)}.
*/
@ -254,7 +281,7 @@ public abstract class MessageRecord extends DisplayRecord {
public boolean isUpdate() {
return isGroupAction() || isJoined() || isExpirationTimerUpdate() || isCallLog() ||
isEndSession() || isIdentityUpdate() || isIdentityVerified() || isIdentityDefault();
isEndSession() || isIdentityUpdate() || isIdentityVerified() || isIdentityDefault() || isProfileChange();
}
public boolean isMediaPending() {

View File

@ -377,8 +377,16 @@ public class RetrieveProfileJob extends BaseJob {
String plaintextProfileName = ProfileUtil.decryptName(profileKey, profileName);
if (!Objects.equals(plaintextProfileName, recipient.getProfileName().serialize())) {
String newProfileName = TextUtils.isEmpty(plaintextProfileName) ? ProfileName.EMPTY.serialize() : plaintextProfileName;
String previousProfileName = recipient.getProfileName().serialize();
Log.i(TAG, "Profile name updated. Writing new value.");
DatabaseFactory.getRecipientDatabase(context).setProfileName(recipient.getId(), ProfileName.fromSerialized(plaintextProfileName));
if (!recipient.isGroup() && !recipient.isLocalNumber()) {
//noinspection ConstantConditions
DatabaseFactory.getSmsDatabase(context).insertProfileNameChangeMessages(recipient, newProfileName, previousProfileName);
}
}
if (TextUtils.isEmpty(plaintextProfileName)) {

View File

@ -46,3 +46,12 @@ message AudioWaveFormData {
int64 durationUs = 1;
bytes waveForm = 2;
}
message ProfileChangeDetails {
message StringChange {
string previous = 1;
string new = 2;
}
StringChange profileNameChange = 1;
}

View File

@ -368,7 +368,7 @@
<string name="CreateProfileActivity_problem_setting_profile">Problem setting profile</string>
<string name="CreateProfileActivity_profile_photo">Profile photo</string>
<string name="CreateProfileActivity_set_up_your_profile">Set up your profile</string>
<string name="CreateProfileActivity_signal_profiles_are_end_to_end_encrypted">Your profile is end-to-end encrypted. It will be visible to your contacts, when you initiate or accept new conversations, and when you join new groups.</string>
<string name="CreateProfileActivity_signal_profiles_are_end_to_end_encrypted">Your profile is end-to-end encrypted. Your profile and changes to it will be visible to your contacts, when you initiate or accept new conversations, and when you join new groups.</string>
<string name="CreateProfileActivity_set_avatar_description">Set avatar</string>
<!-- CustomDefaultPreference -->
@ -762,6 +762,11 @@
<string name="MessageRecord_s_set_disappearing_message_time_to_s">%1$s set the disappearing message timer to %2$s.</string>
<string name="MessageRecord_disappearing_message_time_set_to_s">The disappearing message timer has been set to %1$s.</string>
<!-- Profile change updates -->
<string name="MessageRecord_changed_their_profile_name_to">%1$s changed their profile name to %2$s.</string>
<string name="MessageRecord_changed_their_profile_name_from_to">%1$s changed their profile name from %2$s to %3$s.</string>
<string name="MessageRecord_changed_their_profile">%1$s changed their profile.</string>
<!-- GV2 specific -->
<string name="MessageRecord_you_created_the_group">You created the group.</string>
<string name="MessageRecord_group_updated">Group updated.</string>