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:
parent
bc7b0b40b0
commit
80a6e0f781
11 changed files with 142 additions and 2 deletions
|
@ -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() {
|
||||
|
|
|
@ -261,4 +261,8 @@ message MessageExportState {
|
|||
repeated string startedAttachments = 4;
|
||||
repeated string completedAttachments = 5;
|
||||
Progress progress = 6;
|
||||
}
|
||||
|
||||
message ThreadMergeEvent {
|
||||
string previousE164 = 1;
|
||||
}
|
9
app/src/main/res/drawable/ic_thread_merge_16.xml
Normal file
9
app/src/main/res/drawable/ic_thread_merge_16.xml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="16dp"
|
||||
android:height="16dp"
|
||||
android:viewportWidth="16"
|
||||
android:viewportHeight="16">
|
||||
<path
|
||||
android:pathData="M4.137,14C4.334,14 4.539,13.933 4.721,13.814L5.115,13.554C6.169,12.854 7.723,11.277 7.989,10.489H8.011C8.269,11.27 9.823,12.854 10.885,13.554L11.279,13.814C11.461,13.933 11.666,14 11.856,14C12.242,14 12.5,13.74 12.5,13.375C12.5,13.152 12.379,12.929 12.174,12.802L11.575,12.423C10.521,11.739 8.663,10.013 8.663,8.19V5.401H9.74C10.271,5.401 10.415,5.036 10.119,4.627L8.428,2.254C8.186,1.919 7.822,1.912 7.572,2.254L5.873,4.619C5.578,5.036 5.714,5.401 6.26,5.401H7.337V8.19C7.337,10.013 5.471,11.731 4.425,12.423L3.826,12.802C3.614,12.929 3.5,13.159 3.5,13.383C3.5,13.717 3.75,14 4.137,14Z"
|
||||
android:fillColor="#000000"/>
|
||||
</vector>
|
|
@ -1345,6 +1345,10 @@
|
|||
<string name="MessageRecord_s_changed_their_phone_number">%1$s changed their phone number.</string>
|
||||
<!-- Update item message shown in the release channel when someone is already a sustainer so we ask them if they want to boost. -->
|
||||
<string name="MessageRecord_like_this_new_feature_help_support_signal_with_a_one_time_donation">Like this new feature? Help support Signal with a one-time donation.</string>
|
||||
<!-- Update item message shown when we merge two threads together -->
|
||||
<string name="MessageRecord_your_message_history_with_s_and_their_number_s_has_been_merged">Your message history with %1$s and their number %2$s has been merged.</string>
|
||||
<!-- Update item message shown when we merge two threads together and we don't know the phone number of the other thread -->
|
||||
<string name="MessageRecord_your_message_history_with_s_and_another_chat_has_been_merged">Your message history with %1$s and another chat that belonged to them has been merged.</string>
|
||||
|
||||
<!-- Group Calling update messages -->
|
||||
<string name="MessageRecord_s_started_a_group_call_s">%1$s started a group call · %2$s</string>
|
||||
|
|
Loading…
Add table
Reference in a new issue