Show Profile Name Change update messages.
parent
6d035c6888
commit
64420ead7c
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue