mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-19 16:19:33 +01:00
Show a chat event when two threads are merged.
* Add internal button to split contacts for debugging. * Show a chat event when two threads are merged.
This commit is contained in:
committed by
Cody Henthorne
parent
bc7b0b40b0
commit
80a6e0f781
@@ -22,8 +22,10 @@ import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.recipients.RecipientForeverObserver
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
import org.thoughtcrime.securesms.sms.OutgoingEncryptedMessage
|
||||
import org.thoughtcrime.securesms.subscription.Subscriber
|
||||
import org.thoughtcrime.securesms.util.Base64
|
||||
import org.thoughtcrime.securesms.util.FeatureFlags
|
||||
import org.thoughtcrime.securesms.util.SpanUtil
|
||||
import org.thoughtcrime.securesms.util.Util
|
||||
import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter
|
||||
@@ -213,6 +215,48 @@ class InternalConversationSettingsFragment : DSLSettingsFragment(
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
sectionHeaderPref(DSLSettingsText.from("PNP"))
|
||||
|
||||
clickPref(
|
||||
title = DSLSettingsText.from("Split contact"),
|
||||
summary = DSLSettingsText.from("Splits this contact into two recipients and two threads so that you can test merging them together. This will remain the 'primary' recipient."),
|
||||
onClick = {
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle("Are you sure?")
|
||||
.setNegativeButton(android.R.string.cancel) { d, _ -> d.dismiss() }
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
if (!recipient.hasE164()) {
|
||||
Toast.makeText(context, "Recipient doesn't have an E164! Can't split.", Toast.LENGTH_SHORT).show()
|
||||
return@setPositiveButton
|
||||
}
|
||||
|
||||
SignalDatabase.recipients.debugClearE164AndPni(recipient.id)
|
||||
|
||||
val splitRecipientId: RecipientId = if (FeatureFlags.phoneNumberPrivacy()) {
|
||||
SignalDatabase.recipients.getAndPossiblyMergePnpVerified(recipient.pni.orElse(null), recipient.pni.orElse(null), recipient.requireE164())
|
||||
} else {
|
||||
SignalDatabase.recipients.getAndPossiblyMerge(recipient.pni.orElse(null), recipient.requireE164())
|
||||
}
|
||||
val splitRecipient: Recipient = Recipient.resolved(splitRecipientId)
|
||||
val splitThreadId: Long = SignalDatabase.threads.getOrCreateThreadIdFor(splitRecipient)
|
||||
|
||||
val messageId: Long = SignalDatabase.sms.insertMessageOutbox(
|
||||
splitThreadId,
|
||||
OutgoingEncryptedMessage(splitRecipient, "Test Message ${System.currentTimeMillis()}", 0),
|
||||
false,
|
||||
System.currentTimeMillis(),
|
||||
null
|
||||
)
|
||||
SignalDatabase.sms.markAsSent(messageId, true)
|
||||
|
||||
SignalDatabase.threads.update(splitThreadId, true)
|
||||
|
||||
Toast.makeText(context, "Done! We split the E164/PNI from this contact into $splitRecipientId", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
.show()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
|
||||
import org.thoughtcrime.securesms.database.model.StoryResult;
|
||||
import org.thoughtcrime.securesms.database.model.StoryViewState;
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.MessageExportState;
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.ThreadMergeEvent;
|
||||
import org.thoughtcrime.securesms.groups.GroupMigrationMembershipChange;
|
||||
import org.thoughtcrime.securesms.insights.InsightsConstants;
|
||||
import org.thoughtcrime.securesms.mms.IncomingMediaMessage;
|
||||
@@ -175,6 +176,7 @@ public abstract class MessageDatabase extends Database implements MmsSmsColumns
|
||||
public abstract void insertGroupV1MigrationEvents(@NonNull RecipientId recipientId, long threadId, @NonNull GroupMigrationMembershipChange membershipChange);
|
||||
public abstract void insertNumberChangeMessages(@NonNull Recipient recipient);
|
||||
public abstract void insertBoostRequestMessage(@NonNull RecipientId recipientId, long threadId);
|
||||
public abstract void insertThreadMergeEvent(@NonNull RecipientId recipientId, long threadId, @NonNull ThreadMergeEvent event);
|
||||
|
||||
public abstract boolean deleteMessage(long messageId);
|
||||
abstract void deleteThread(long threadId);
|
||||
|
||||
@@ -65,6 +65,7 @@ import org.thoughtcrime.securesms.database.model.StoryViewState;
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList;
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.GiftBadge;
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.MessageExportState;
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.ThreadMergeEvent;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.groups.GroupMigrationMembershipChange;
|
||||
import org.thoughtcrime.securesms.jobs.TrimThreadJob;
|
||||
@@ -573,6 +574,11 @@ public class MmsDatabase extends MessageDatabase {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertThreadMergeEvent(@NonNull RecipientId recipientId, long threadId, @NonNull ThreadMergeEvent event) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endTransaction(SQLiteDatabase database) {
|
||||
database.endTransaction();
|
||||
|
||||
@@ -81,6 +81,7 @@ public interface MmsSmsColumns {
|
||||
protected static final long BAD_DECRYPT_TYPE = 13;
|
||||
protected static final long CHANGE_NUMBER_TYPE = 14;
|
||||
protected static final long BOOST_REQUEST_TYPE = 15;
|
||||
protected static final long THREAD_MERGE_TYPE = 16;
|
||||
|
||||
protected static final long BASE_INBOX_TYPE = 20;
|
||||
protected static final long BASE_OUTBOX_TYPE = 21;
|
||||
@@ -222,6 +223,10 @@ public interface MmsSmsColumns {
|
||||
return (type & BASE_TYPE_MASK) == BAD_DECRYPT_TYPE;
|
||||
}
|
||||
|
||||
public static boolean isThreadMergeType(long type) {
|
||||
return (type & BASE_TYPE_MASK) == THREAD_MERGE_TYPE;
|
||||
}
|
||||
|
||||
public static boolean isSecureType(long type) {
|
||||
return (type & SECURE_MESSAGE_BIT) != 0;
|
||||
}
|
||||
|
||||
@@ -70,6 +70,7 @@ import org.thoughtcrime.securesms.database.model.databaseprotos.ChatColor
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.DeviceLastResetTime
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.ExpiringProfileKeyCredentialColumnData
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.RecipientExtras
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.ThreadMergeEvent
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.Wallpaper
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||
import org.thoughtcrime.securesms.groups.BadGroupIdException
|
||||
@@ -3570,6 +3571,17 @@ open class RecipientDatabase(context: Context, databaseHelper: SignalDatabase) :
|
||||
db.update(MmsDatabase.TABLE_NAME, values, MmsDatabase.THREAD_ID + " = ?", SqlUtil.buildArgs(threadMerge.previousThreadId))
|
||||
}
|
||||
|
||||
// Thread Merge event
|
||||
if (threadMerge.neededMerge) {
|
||||
val mergeEvent: ThreadMergeEvent.Builder = ThreadMergeEvent.newBuilder()
|
||||
|
||||
if (secondaryRecord.e164 != null) {
|
||||
mergeEvent.previousE164 = secondaryRecord.e164
|
||||
}
|
||||
|
||||
SignalDatabase.sms.insertThreadMergeEvent(primaryRecord.id, threadMerge.threadId, mergeEvent.build())
|
||||
}
|
||||
|
||||
// MSL
|
||||
messageLog.remapRecipient(secondaryId, primaryId)
|
||||
|
||||
@@ -3823,6 +3835,22 @@ open class RecipientDatabase(context: Context, databaseHelper: SignalDatabase) :
|
||||
RecipientId.clearCache()
|
||||
}
|
||||
|
||||
/**
|
||||
* Should only be used for debugging! Clears the E164 and PNI from a recipient.
|
||||
*/
|
||||
fun debugClearE164AndPni(recipientId: RecipientId) {
|
||||
writableDatabase
|
||||
.update(TABLE_NAME)
|
||||
.values(
|
||||
PHONE to null,
|
||||
PNI_COLUMN to null
|
||||
)
|
||||
.where(ID_WHERE, recipientId)
|
||||
.run()
|
||||
|
||||
Recipient.live(recipientId).refresh()
|
||||
}
|
||||
|
||||
fun getRecord(context: Context, cursor: Cursor): RecipientRecord {
|
||||
return getRecord(context, cursor, ID)
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@ import org.thoughtcrime.securesms.database.model.StoryViewState;
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.GroupCallUpdateDetails;
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.MessageExportState;
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.ProfileChangeDetails;
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.ThreadMergeEvent;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.groups.GroupMigrationMembershipChange;
|
||||
import org.thoughtcrime.securesms.jobs.TrimThreadJob;
|
||||
@@ -1121,6 +1122,23 @@ public class SmsDatabase extends MessageDatabase {
|
||||
getWritableDatabase().insert(TABLE_NAME, null, values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertThreadMergeEvent(@NonNull RecipientId recipientId, long threadId, @NonNull ThreadMergeEvent event) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(RECIPIENT_ID, recipientId.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.THREAD_MERGE_TYPE);
|
||||
values.put(THREAD_ID, threadId);
|
||||
values.put(BODY, Base64.encodeBytes(event.toByteArray()));
|
||||
|
||||
getWritableDatabase().insert(TABLE_NAME, null, values);
|
||||
|
||||
ApplicationDependencies.getDatabaseObserver().notifyConversationListeners(threadId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<InsertResult> insertMessageInbox(IncomingTextMessage message, long type) {
|
||||
boolean tryToCollapseJoinRequestEvents = false;
|
||||
|
||||
@@ -1621,7 +1621,8 @@ public class ThreadDatabase extends Database {
|
||||
MmsSmsColumns.Types.isGroupV1MigrationEvent(type) ||
|
||||
MmsSmsColumns.Types.isChangeNumber(type) ||
|
||||
MmsSmsColumns.Types.isBoostRequest(type) ||
|
||||
MmsSmsColumns.Types.isGroupV2LeaveOnly(type);
|
||||
MmsSmsColumns.Types.isGroupV2LeaveOnly(type) ||
|
||||
MmsSmsColumns.Types.isThreadMergeType(type);
|
||||
}
|
||||
|
||||
public Reader readerFor(Cursor cursor) {
|
||||
|
||||
@@ -32,6 +32,7 @@ import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.annimon.stream.Stream;
|
||||
import com.google.protobuf.ByteString;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
|
||||
import org.signal.core.util.StringUtil;
|
||||
import org.signal.core.util.logging.Log;
|
||||
@@ -48,10 +49,12 @@ import org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList;
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.DecryptedGroupV2Context;
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.GroupCallUpdateDetails;
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.ProfileChangeDetails;
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.ThreadMergeEvent;
|
||||
import org.thoughtcrime.securesms.emoji.EmojiSource;
|
||||
import org.thoughtcrime.securesms.emoji.JumboEmoji;
|
||||
import org.thoughtcrime.securesms.groups.GroupMigrationMembershipChange;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
|
||||
import org.thoughtcrime.securesms.profiles.ProfileName;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
@@ -221,6 +224,18 @@ public abstract class MessageRecord extends DisplayRecord {
|
||||
return staticUpdateDescription(context.getString(R.string.MessageRecord_chat_session_refreshed), R.drawable.ic_refresh_16);
|
||||
} else if (isBadDecryptType()) {
|
||||
return fromRecipient(getIndividualRecipient(), r -> context.getString(R.string.MessageRecord_a_message_from_s_couldnt_be_delivered, r.getDisplayName(context)), R.drawable.ic_error_outline_14);
|
||||
} else if (isThreadMergeEventType()) {
|
||||
try {
|
||||
ThreadMergeEvent event = ThreadMergeEvent.parseFrom(Base64.decodeOrThrow(getBody()));
|
||||
|
||||
if (event.getPreviousE164().isEmpty()) {
|
||||
return fromRecipient(getIndividualRecipient(), r -> context.getString(R.string.MessageRecord_your_message_history_with_s_and_another_chat_has_been_merged, r.getDisplayName(context)), R.drawable.ic_thread_merge_16);
|
||||
} else {
|
||||
return fromRecipient(getIndividualRecipient(), r -> context.getString(R.string.MessageRecord_your_message_history_with_s_and_their_number_s_has_been_merged, r.getDisplayName(context), PhoneNumberFormatter.prettyPrint(event.getPreviousE164())), R.drawable.ic_thread_merge_16);
|
||||
}
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -523,6 +538,10 @@ public abstract class MessageRecord extends DisplayRecord {
|
||||
return MmsSmsColumns.Types.isBadDecryptType(type);
|
||||
}
|
||||
|
||||
public boolean isThreadMergeEventType() {
|
||||
return MmsSmsColumns.Types.isThreadMergeType(type);
|
||||
}
|
||||
|
||||
public boolean isInvalidVersionKeyExchange() {
|
||||
return SmsDatabase.Types.isInvalidVersionKeyExchange(type);
|
||||
}
|
||||
@@ -543,7 +562,7 @@ public abstract class MessageRecord extends DisplayRecord {
|
||||
return isGroupAction() || isJoined() || isExpirationTimerUpdate() || isCallLog() ||
|
||||
isEndSession() || isIdentityUpdate() || isIdentityVerified() || isIdentityDefault() ||
|
||||
isProfileChange() || isGroupV1MigrationEvent() || isChatSessionRefresh() || isBadDecryptType() ||
|
||||
isChangeNumber() || isBoostRequest();
|
||||
isChangeNumber() || isBoostRequest() || isThreadMergeEventType();
|
||||
}
|
||||
|
||||
public boolean isMediaPending() {
|
||||
|
||||
Reference in New Issue
Block a user