Add report spam in message request state.

This commit is contained in:
Cody Henthorne
2021-05-17 09:43:37 -04:00
committed by Alex Hart
parent c47dcd5720
commit ef5b68eb35
31 changed files with 393 additions and 145 deletions

View File

@@ -9,6 +9,8 @@ import androidx.annotation.WorkerThread;
import androidx.appcompat.app.AlertDialog;
import androidx.lifecycle.Lifecycle;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
@@ -30,15 +32,15 @@ public final class BlockUnblockDialog {
AlertDialog.Builder::show);
}
public static void showBlockAndDeleteFor(@NonNull Context context,
@NonNull Lifecycle lifecycle,
@NonNull Recipient recipient,
@NonNull Runnable onBlock,
@NonNull Runnable onBlockAndDelete)
public static void showBlockAndReportSpamFor(@NonNull Context context,
@NonNull Lifecycle lifecycle,
@NonNull Recipient recipient,
@NonNull Runnable onBlock,
@NonNull Runnable onBlockAndReportSpam)
{
SimpleTask.run(lifecycle,
() -> buildBlockFor(context, recipient, onBlock, onBlockAndDelete),
AlertDialog.Builder::show);
() -> buildBlockFor(context, recipient, onBlock, onBlockAndReportSpam),
AlertDialog.Builder::show);
}
public static void showUnblockFor(@NonNull Context context,
@@ -55,11 +57,11 @@ public final class BlockUnblockDialog {
private static AlertDialog.Builder buildBlockFor(@NonNull Context context,
@NonNull Recipient recipient,
@NonNull Runnable onBlock,
@Nullable Runnable onBlockAndDelete)
@Nullable Runnable onBlockAndReportSpam)
{
recipient = recipient.resolve();
AlertDialog.Builder builder = new AlertDialog.Builder(context);
AlertDialog.Builder builder = new MaterialAlertDialogBuilder(context);
Resources resources = context.getResources();
if (recipient.isGroup()) {
@@ -78,10 +80,10 @@ public final class BlockUnblockDialog {
builder.setTitle(resources.getString(R.string.BlockUnblockDialog_block_s, recipient.getDisplayName(context)));
builder.setMessage(R.string.BlockUnblockDialog_blocked_people_wont_be_able_to_call_you_or_send_you_messages);
if (onBlockAndDelete != null) {
if (onBlockAndReportSpam != null) {
builder.setNeutralButton(android.R.string.cancel, null);
builder.setPositiveButton(R.string.BlockUnblockDialog_block_and_delete, (d, w) -> onBlockAndDelete.run());
builder.setNegativeButton(R.string.BlockUnblockDialog_block, (d, w) -> onBlock.run());
builder.setNegativeButton(R.string.BlockUnblockDialog_report_spam_and_block, (d, w) -> onBlockAndReportSpam.run());
builder.setPositiveButton(R.string.BlockUnblockDialog_block, (d, w) -> onBlock.run());
} else {
builder.setPositiveButton(R.string.BlockUnblockDialog_block, ((dialog, which) -> onBlock.run()));
builder.setNegativeButton(android.R.string.cancel, null);
@@ -98,7 +100,7 @@ public final class BlockUnblockDialog {
{
recipient = recipient.resolve();
AlertDialog.Builder builder = new AlertDialog.Builder(context);
AlertDialog.Builder builder = new MaterialAlertDialogBuilder(context);
Resources resources = context.getResources();
if (recipient.isGroup()) {

View File

@@ -249,7 +249,6 @@ import org.thoughtcrime.securesms.stickers.StickerLocator;
import org.thoughtcrime.securesms.stickers.StickerManagementActivity;
import org.thoughtcrime.securesms.stickers.StickerPackInstallEvent;
import org.thoughtcrime.securesms.stickers.StickerSearchRepository;
import org.thoughtcrime.securesms.storage.StorageSyncHelper;
import org.thoughtcrime.securesms.util.AsynchronousCallback;
import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.BitmapUtil;
@@ -3369,6 +3368,10 @@ public class ConversationActivity extends PassphraseRequiredActivity
case ACCEPTED:
hideMessageRequestBusy();
break;
case BLOCKED_AND_REPORTED:
hideMessageRequestBusy();
Toast.makeText(this, R.string.ConversationActivity__reported_as_spam_and_blocked, Toast.LENGTH_SHORT).show();
break;
case DELETED:
case BLOCKED:
hideMessageRequestBusy();
@@ -3625,7 +3628,7 @@ public class ConversationActivity extends PassphraseRequiredActivity
return;
}
BlockUnblockDialog.showBlockAndDeleteFor(this, getLifecycle(), recipient, requestModel::onBlock, requestModel::onBlockAndDelete);
BlockUnblockDialog.showBlockAndReportSpamFor(this, getLifecycle(), recipient, requestModel::onBlock, requestModel::onBlockAndReportSpam);
}
private void onMessageRequestUnblockClicked(@NonNull MessageRequestViewModel requestModel) {

View File

@@ -34,6 +34,7 @@ import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.revealable.ViewOnceExpirationInfo;
import org.thoughtcrime.securesms.sms.IncomingTextMessage;
import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
import org.thoughtcrime.securesms.util.CursorUtil;
import org.thoughtcrime.securesms.util.JsonUtils;
import org.thoughtcrime.securesms.util.SqlUtil;
import org.thoughtcrime.securesms.util.Util;
@@ -403,6 +404,25 @@ public abstract class MessageDatabase extends Database implements MmsSmsColumns
}
}
public @NonNull List<ReportSpamData> getReportSpamMessageServerGuids(long threadId, long timestamp) {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
String query = THREAD_ID + " = ? AND " + getDateReceivedColumnName() + " <= ?";
String[] args = SqlUtil.buildArgs(threadId, timestamp);
List<ReportSpamData> data = new ArrayList<>();
try (Cursor cursor = db.query(getTableName(), new String[] { RECIPIENT_ID, SERVER_GUID, getDateReceivedColumnName() }, query, args, null, null, getDateReceivedColumnName() + " DESC", "3")) {
while (cursor.moveToNext()) {
RecipientId id = RecipientId.from(CursorUtil.requireLong(cursor, RECIPIENT_ID));
String serverGuid = CursorUtil.requireString(cursor, SERVER_GUID);
long dateReceived = CursorUtil.requireLong(cursor, getDateReceivedColumnName());
if (!Util.isEmpty(serverGuid)) {
data.add(new ReportSpamData(id, serverGuid, dateReceived));
}
}
}
return data;
}
protected static List<ReactionRecord> parseReactions(@NonNull Cursor cursor) {
byte[] raw = cursor.getBlob(cursor.getColumnIndexOrThrow(REACTIONS));
@@ -770,4 +790,28 @@ public abstract class MessageDatabase extends Database implements MmsSmsColumns
MessageRecord getCurrent();
void close();
}
public static class ReportSpamData {
private final RecipientId recipientId;
private final String serverGuid;
private final long dateReceived;
public ReportSpamData(RecipientId recipientId, String serverGuid, long dateReceived) {
this.recipientId = recipientId;
this.serverGuid = serverGuid;
this.dateReceived = dateReceived;
}
public @NonNull RecipientId getRecipientId() {
return recipientId;
}
public @NonNull String getServerGuid() {
return serverGuid;
}
public long getDateReceived() {
return dateReceived;
}
}
}

View File

@@ -185,7 +185,8 @@ public class MmsDatabase extends MessageDatabase {
REMOTE_DELETED + " INTEGER DEFAULT 0, " +
MENTIONS_SELF + " INTEGER DEFAULT 0, " +
NOTIFIED_TIMESTAMP + " INTEGER DEFAULT 0, " +
VIEWED_RECEIPT_COUNT + " INTEGER DEFAULT 0);";
VIEWED_RECEIPT_COUNT + " INTEGER DEFAULT 0, " +
SERVER_GUID + " TEXT DEFAULT NULL);";
public static final String[] CREATE_INDEXS = {
"CREATE INDEX IF NOT EXISTS mms_thread_id_index ON " + TABLE_NAME + " (" + THREAD_ID + ");",
@@ -1317,6 +1318,7 @@ public class MmsDatabase extends MessageDatabase {
contentValues.put(VIEW_ONCE, retrieved.isViewOnce() ? 1 : 0);
contentValues.put(READ, retrieved.isExpirationUpdate() ? 1 : 0);
contentValues.put(UNIDENTIFIED, retrieved.isUnidentified());
contentValues.put(SERVER_GUID, retrieved.getServerGuid());
if (!contentValues.containsKey(DATE_SENT)) {
contentValues.put(DATE_SENT, contentValues.getAsLong(DATE_RECEIVED));

View File

@@ -27,6 +27,7 @@ public interface MmsSmsColumns {
public static final String REACTIONS_UNREAD = "reactions_unread";
public static final String REACTIONS_LAST_SEEN = "reactions_last_seen";
public static final String REMOTE_DELETED = "remote_deleted";
public static final String SERVER_GUID = "server_guid";
/**
* For storage efficiency, all types are stored within a single 64-bit integer column in the

View File

@@ -38,11 +38,13 @@ import org.thoughtcrime.securesms.util.CursorUtil;
import org.whispersystems.libsignal.util.Pair;
import java.io.Closeable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
public class MmsSmsDatabase extends Database {
@@ -564,6 +566,16 @@ public class MmsSmsDatabase extends Database {
DatabaseFactory.getMmsDatabase(context).deleteAbandonedMessages();
}
public @NonNull List<MessageDatabase.ReportSpamData> getReportSpamMessageServerData(long threadId, long timestamp, int limit) {
List<MessageDatabase.ReportSpamData> data = new ArrayList<>();
data.addAll(DatabaseFactory.getSmsDatabase(context).getReportSpamMessageServerGuids(threadId, timestamp));
data.addAll(DatabaseFactory.getMmsDatabase(context).getReportSpamMessageServerGuids(threadId, timestamp));
return data.stream()
.sorted((l, r) -> -Long.compare(l.getDateReceived(), r.getDateReceived()))
.limit(limit)
.collect(Collectors.toList());
}
private Cursor queryTables(String[] projection, String selection, String order, String limit) {
String[] mmsProjection = {MmsDatabase.DATE_SENT + " AS " + MmsSmsColumns.NORMALIZED_DATE_SENT,
MmsDatabase.DATE_RECEIVED + " AS " + MmsSmsColumns.NORMALIZED_DATE_RECEIVED,

View File

@@ -9,7 +9,6 @@ import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
import org.thoughtcrime.securesms.util.Base64;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
@@ -68,7 +67,7 @@ public class PushDatabase extends Database {
values.put(TIMESTAMP, envelope.getTimestamp());
values.put(SERVER_RECEIVED_TIMESTAMP, envelope.getServerReceivedTimestamp());
values.put(SERVER_DELIVERED_TIMESTAMP, envelope.getServerDeliveredTimestamp());
values.put(SERVER_GUID, envelope.getUuid());
values.put(SERVER_GUID, envelope.getServerGuid());
return databaseHelper.getWritableDatabase().insert(TABLE_NAME, null, values);
}

View File

@@ -123,7 +123,8 @@ public class SmsDatabase extends MessageDatabase {
REACTIONS_UNREAD + " INTEGER DEFAULT 0, " +
REACTIONS_LAST_SEEN + " INTEGER DEFAULT -1, " +
REMOTE_DELETED + " INTEGER DEFAULT 0, " +
NOTIFIED_TIMESTAMP + " INTEGER DEFAULT 0);";
NOTIFIED_TIMESTAMP + " INTEGER DEFAULT 0," +
SERVER_GUID + " TEXT DEFAULT NULL);";
public static final String[] CREATE_INDEXS = {
"CREATE INDEX IF NOT EXISTS sms_thread_id_index ON " + TABLE_NAME + " (" + THREAD_ID + ");",
@@ -1105,6 +1106,7 @@ public class SmsDatabase extends MessageDatabase {
values.put(BODY, message.getMessageBody());
values.put(TYPE, type);
values.put(THREAD_ID, threadId);
values.put(SERVER_GUID, message.getServerGuid());
if (message.isPush() && isDuplicate(message, threadId)) {
Log.w(TAG, "Duplicate message (" + message.getSentTimestampMillis() + "), ignoring...");

View File

@@ -181,8 +181,9 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper implements SignalDatab
private static final int CLEAN_REACTION_NOTIFICATIONS = 96;
private static final int STORAGE_SERVICE_REFACTOR = 97;
private static final int CLEAR_MMS_STORAGE_IDS = 98;
private static final int SERVER_GUID = 99;
private static final int DATABASE_VERSION = 98;
private static final int DATABASE_VERSION = 99;
private static final String DATABASE_NAME = "signal.db";
private final Context context;
@@ -1457,6 +1458,11 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper implements SignalDatab
Log.d(TAG, "Cleared storageIds from " + deleteCount + " rows. They were either MMS groups or empty contacts.");
}
if (oldVersion < SERVER_GUID) {
db.execSQL("ALTER TABLE sms ADD COLUMN server_guid TEXT DEFAULT NULL");
db.execSQL("ALTER TABLE mms ADD COLUMN server_guid TEXT DEFAULT NULL");
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();

View File

@@ -248,7 +248,7 @@ public final class GroupV1MessageProcessor {
} else {
MessageDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
String body = Base64.encodeBytes(storage.toByteArray());
IncomingTextMessage incoming = new IncomingTextMessage(Recipient.externalHighTrustPush(context, content.getSender()).getId(), content.getSenderDevice(), content.getTimestamp(), content.getServerReceivedTimestamp(), body, Optional.of(GroupId.v1orThrow(group.getGroupId())), 0, content.isNeedsReceipt());
IncomingTextMessage incoming = new IncomingTextMessage(Recipient.externalHighTrustPush(context, content.getSender()).getId(), content.getSenderDevice(), content.getTimestamp(), content.getServerReceivedTimestamp(), body, Optional.of(GroupId.v1orThrow(group.getGroupId())), 0, content.isNeedsReceipt(), content.getServerUuid());
IncomingGroupUpdateMessage groupMessage = new IncomingGroupUpdateMessage(incoming, storage, body);
Optional<InsertResult> insertResult = smsDatabase.insertMessageInbox(groupMessage);

View File

@@ -521,7 +521,7 @@ public final class GroupsV2StateProcessor {
} else {
MessageDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
RecipientId sender = RecipientId.from(editor.get(), null);
IncomingTextMessage incoming = new IncomingTextMessage(sender, -1, timestamp, timestamp, "", Optional.of(groupId), 0, false);
IncomingTextMessage incoming = new IncomingTextMessage(sender, -1, timestamp, timestamp, "", Optional.of(groupId), 0, false, null);
IncomingGroupUpdateMessage groupMessage = new IncomingGroupUpdateMessage(incoming, decryptedGroupV2Context);
if (!smsDatabase.insertMessageInbox(groupMessage).isPresent()) {

View File

@@ -156,6 +156,7 @@ public final class JobManagerFactories {
put(PaymentSendJob.KEY, new PaymentSendJob.Factory());
put(PaymentTransactionCheckJob.KEY, new PaymentTransactionCheckJob.Factory());
put(ProfileUploadJob.KEY, new ProfileUploadJob.Factory());
put(ReportSpamJob.KEY, new ReportSpamJob.Factory());
// Migrations
put(AccountRecordMigrationJob.KEY, new AccountRecordMigrationJob.Factory());

View File

@@ -51,14 +51,18 @@ public class MultiDeviceMessageRequestResponseJob extends BaseJob {
return new MultiDeviceMessageRequestResponseJob(threadRecipient, Type.BLOCK_AND_DELETE);
}
private MultiDeviceMessageRequestResponseJob(@NonNull RecipientId threadRecipient, @NonNull Type type) {
this(new Parameters.Builder()
.setQueue("MultiDeviceMessageRequestResponseJob")
.addConstraint(NetworkConstraint.KEY)
.setMaxAttempts(Parameters.UNLIMITED)
.setLifespan(TimeUnit.DAYS.toMillis(1))
.build(), threadRecipient, type);
public static @NonNull MultiDeviceMessageRequestResponseJob forBlockAndReportSpam(@NonNull RecipientId threadRecipient) {
return new MultiDeviceMessageRequestResponseJob(threadRecipient, Type.BLOCK);
}
private MultiDeviceMessageRequestResponseJob(@NonNull RecipientId threadRecipient, @NonNull Type type) {
this(new Parameters.Builder().setQueue("MultiDeviceMessageRequestResponseJob")
.addConstraint(NetworkConstraint.KEY)
.setMaxAttempts(Parameters.UNLIMITED)
.setLifespan(TimeUnit.DAYS.toMillis(1))
.build(),
threadRecipient,
type);
}
private MultiDeviceMessageRequestResponseJob(@NonNull Parameters parameters,
@@ -111,11 +115,16 @@ public class MultiDeviceMessageRequestResponseJob extends BaseJob {
private static MessageRequestResponseMessage.Type localToRemoteType(@NonNull Type type) {
switch (type) {
case ACCEPT: return MessageRequestResponseMessage.Type.ACCEPT;
case DELETE: return MessageRequestResponseMessage.Type.DELETE;
case BLOCK: return MessageRequestResponseMessage.Type.BLOCK;
case BLOCK_AND_DELETE: return MessageRequestResponseMessage.Type.BLOCK_AND_DELETE;
default: return MessageRequestResponseMessage.Type.UNKNOWN;
case ACCEPT:
return MessageRequestResponseMessage.Type.ACCEPT;
case DELETE:
return MessageRequestResponseMessage.Type.DELETE;
case BLOCK:
return MessageRequestResponseMessage.Type.BLOCK;
case BLOCK_AND_DELETE:
return MessageRequestResponseMessage.Type.BLOCK_AND_DELETE;
default:
return MessageRequestResponseMessage.Type.UNKNOWN;
}
}

View File

@@ -0,0 +1,107 @@
package org.thoughtcrime.securesms.jobs;
import androidx.annotation.NonNull;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MessageDatabase.ReportSpamData;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException;
import org.whispersystems.signalservice.api.push.exceptions.ServerRejectedException;
import java.io.IOException;
import java.util.List;
/**
* Report 1 to {@link #MAX_MESSAGE_COUNT} message guids received prior to {@link #timestamp} in {@link #threadId} to the server as spam.
*/
public class ReportSpamJob extends BaseJob {
public static final String KEY = "ReportSpamJob";
private static final String TAG = Log.tag(ReportSpamJob.class);
private static final String KEY_THREAD_ID = "thread_id";
private static final String KEY_TIMESTAMP = "timestamp";
private static final int MAX_MESSAGE_COUNT = 3;
private final long threadId;
private final long timestamp;
public ReportSpamJob(long threadId, long timestamp) {
this(new Parameters.Builder().addConstraint(NetworkConstraint.KEY)
.setMaxAttempts(5)
.setQueue("ReportSpamJob")
.build(),
threadId,
timestamp);
}
private ReportSpamJob(@NonNull Parameters parameters, long threadId, long timestamp) {
super(parameters);
this.threadId = threadId;
this.timestamp = timestamp;
}
@Override
public @NonNull Data serialize() {
return new Data.Builder().putLong(KEY_THREAD_ID, threadId)
.putLong(KEY_TIMESTAMP, timestamp)
.build();
}
@Override
public @NonNull String getFactoryKey() {
return KEY;
}
@Override
public void onRun() throws IOException {
if (!TextSecurePreferences.isPushRegistered(context)) {
return;
}
int count = 0;
List<ReportSpamData> reportSpamData = DatabaseFactory.getMmsSmsDatabase(context).getReportSpamMessageServerData(threadId, timestamp, MAX_MESSAGE_COUNT);
SignalServiceAccountManager signalServiceAccountManager = ApplicationDependencies.getSignalServiceAccountManager();
for (ReportSpamData data : reportSpamData) {
Optional<String> e164 = Recipient.resolved(data.getRecipientId()).getE164();
if (e164.isPresent()) {
signalServiceAccountManager.reportSpam(e164.get(), data.getServerGuid());
count++;
} else {
Log.w(TAG, "Unable to report spam without an e164 for " + data.getRecipientId());
}
}
Log.i(TAG, "Reported " + count + " out of " + reportSpamData.size() + " messages in thread " + threadId + " as spam");
}
@Override
public boolean onShouldRetry(@NonNull Exception exception) {
if (exception instanceof ServerRejectedException) {
return false;
} else if (exception instanceof NonSuccessfulResponseCodeException) {
return ((NonSuccessfulResponseCodeException) exception).is5xx();
}
return exception instanceof IOException;
}
@Override
public void onFailure() {
Log.w(TAG, "Canceling report spam for thread " + threadId);
}
public static final class Factory implements Job.Factory<ReportSpamJob> {
@Override
public @NonNull ReportSpamJob create(@NonNull Parameters parameters, @NonNull Data data) {
return new ReportSpamJob(parameters, data.getLong(KEY_THREAD_ID), data.getLong(KEY_TIMESTAMP));
}
}
}

View File

@@ -22,6 +22,7 @@ import org.thoughtcrime.securesms.groups.GroupManager;
import org.thoughtcrime.securesms.groups.ui.GroupChangeErrorCallback;
import org.thoughtcrime.securesms.groups.ui.GroupChangeFailureReason;
import org.thoughtcrime.securesms.jobs.MultiDeviceMessageRequestResponseJob;
import org.thoughtcrime.securesms.jobs.ReportSpamJob;
import org.thoughtcrime.securesms.jobs.SendViewedReceiptJob;
import org.thoughtcrime.securesms.notifications.MarkReadReceiver;
import org.thoughtcrime.securesms.recipients.LiveRecipient;
@@ -226,10 +227,10 @@ final class MessageRequestRepository {
});
}
void blockAndDeleteMessageRequest(@NonNull LiveRecipient liveRecipient,
long threadId,
@NonNull Runnable onMessageRequestBlocked,
@NonNull GroupChangeErrorCallback error)
void blockAndReportSpamMessageRequest(@NonNull LiveRecipient liveRecipient,
long threadId,
@NonNull Runnable onMessageRequestBlocked,
@NonNull GroupChangeErrorCallback error)
{
executor.execute(() -> {
Recipient recipient = liveRecipient.resolve();
@@ -242,10 +243,10 @@ final class MessageRequestRepository {
}
liveRecipient.refresh();
DatabaseFactory.getThreadDatabase(context).deleteConversation(threadId);
ApplicationDependencies.getJobManager().add(new ReportSpamJob(threadId, System.currentTimeMillis()));
if (TextSecurePreferences.isMultiDevice(context)) {
ApplicationDependencies.getJobManager().add(MultiDeviceMessageRequestResponseJob.forBlockAndDelete(liveRecipient.getId()));
ApplicationDependencies.getJobManager().add(MultiDeviceMessageRequestResponseJob.forBlockAndReportSpam(liveRecipient.getId()));
}
onMessageRequestBlocked.run();

View File

@@ -8,7 +8,6 @@ import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.Transformations;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;
@@ -20,7 +19,6 @@ import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientForeverObserver;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.SingleLiveEvent;
import org.thoughtcrime.securesms.util.livedata.LiveDataTriple;
import org.thoughtcrime.securesms.util.livedata.LiveDataUtil;
import org.thoughtcrime.securesms.util.livedata.Store;
@@ -29,16 +27,16 @@ import java.util.List;
public class MessageRequestViewModel extends ViewModel {
private final SingleLiveEvent<Status> status = new SingleLiveEvent<>();
private final SingleLiveEvent<GroupChangeFailureReason> failures = new SingleLiveEvent<>();
private final MutableLiveData<Recipient> recipient = new MutableLiveData<>();
private final LiveData<MessageData> messageData;
private final MutableLiveData<List<String>> groups = new MutableLiveData<>(Collections.emptyList());
private final MutableLiveData<GroupInfo> groupInfo = new MutableLiveData<>(GroupInfo.ZERO);
private final LiveData<RequestReviewDisplayState> requestReviewDisplayState;
private final SingleLiveEvent<Status> status = new SingleLiveEvent<>();
private final SingleLiveEvent<GroupChangeFailureReason> failures = new SingleLiveEvent<>();
private final MutableLiveData<Recipient> recipient = new MutableLiveData<>();
private final MutableLiveData<List<String>> groups = new MutableLiveData<>(Collections.emptyList());
private final MutableLiveData<GroupInfo> groupInfo = new MutableLiveData<>(GroupInfo.ZERO);
private final Store<RecipientInfo> recipientInfoStore = new Store<>(new RecipientInfo(null, null, null, null));
private final MessageRequestRepository repository;
private final LiveData<MessageData> messageData;
private final LiveData<RequestReviewDisplayState> requestReviewDisplayState;
private final MessageRequestRepository repository;
private LiveRecipient liveRecipient;
private long threadId;
@@ -142,11 +140,11 @@ public class MessageRequestViewModel extends ViewModel {
}
@MainThread
public void onBlockAndDelete() {
repository.blockAndDeleteMessageRequest(liveRecipient,
threadId,
() -> status.postValue(Status.BLOCKED),
this::onGroupChangeError);
public void onBlockAndReportSpam() {
repository.blockAndReportSpamMessageRequest(liveRecipient,
threadId,
() -> status.postValue(Status.BLOCKED_AND_REPORTED),
this::onGroupChangeError);
}
private void onGroupChangeError(@NonNull GroupChangeFailureReason error) {
@@ -187,8 +185,8 @@ public class MessageRequestViewModel extends ViewModel {
public static class RecipientInfo {
@Nullable private final Recipient recipient;
@NonNull private final GroupInfo groupInfo;
@NonNull private final List<String> sharedGroups;
@NonNull private final GroupInfo groupInfo;
@NonNull private final List<String> sharedGroups;
@Nullable private final MessageRequestState messageRequestState;
private RecipientInfo(@Nullable Recipient recipient, @Nullable GroupInfo groupInfo, @Nullable List<String> sharedGroups, @Nullable MessageRequestState messageRequestState) {
@@ -230,6 +228,7 @@ public class MessageRequestViewModel extends ViewModel {
IDLE,
BLOCKING,
BLOCKED,
BLOCKED_AND_REPORTED,
DELETING,
DELETED,
ACCEPTING,
@@ -243,7 +242,7 @@ public class MessageRequestViewModel extends ViewModel {
}
public static final class MessageData {
private final Recipient recipient;
private final Recipient recipient;
private final MessageRequestState messageState;
public MessageData(@NonNull Recipient recipient, @NonNull MessageRequestState messageState) {

View File

@@ -576,11 +576,14 @@ public final class MessageContentProcessor {
{
MessageDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
IncomingTextMessage incomingTextMessage = new IncomingTextMessage(Recipient.externalHighTrustPush(context, content.getSender()).getId(),
content.getSenderDevice(),
content.getTimestamp(),
content.getServerReceivedTimestamp(),
"", Optional.absent(), 0,
content.isNeedsReceipt());
content.getSenderDevice(),
content.getTimestamp(),
content.getServerReceivedTimestamp(),
"",
Optional.absent(),
0,
content.isNeedsReceipt(),
content.getServerUuid());
Long threadId;
@@ -686,21 +689,22 @@ public final class MessageContentProcessor {
MessageDatabase database = DatabaseFactory.getMmsDatabase(context);
Recipient sender = Recipient.externalHighTrustPush(context, content.getSender());
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(sender.getId(),
content.getTimestamp(),
content.getServerReceivedTimestamp(),
-1,
expiresInSeconds * 1000L,
true,
false,
content.isNeedsReceipt(),
Optional.absent(),
groupContext,
Optional.absent(),
Optional.absent(),
Optional.absent(),
Optional.absent(),
Optional.absent(),
Optional.absent());
content.getTimestamp(),
content.getServerReceivedTimestamp(),
-1,
expiresInSeconds * 1000L,
true,
false,
content.isNeedsReceipt(),
Optional.absent(),
groupContext,
Optional.absent(),
Optional.absent(),
Optional.absent(),
Optional.absent(),
Optional.absent(),
Optional.absent(),
content.getServerUuid());
database.insertSecureDecryptedMessageInbox(mediaMessage, -1);
@@ -1104,22 +1108,24 @@ public final class MessageContentProcessor {
Optional<List<LinkPreview>> linkPreviews = getLinkPreviews(message.getPreviews(), message.getBody().or(""));
Optional<List<Mention>> mentions = getMentions(message.getMentions());
Optional<Attachment> sticker = getStickerAttachment(message.getSticker());
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(RecipientId.fromHighTrust(content.getSender()),
message.getTimestamp(),
content.getServerReceivedTimestamp(),
-1,
message.getExpiresInSeconds() * 1000L,
false,
message.isViewOnce(),
content.isNeedsReceipt(),
message.getBody(),
message.getGroupContext(),
message.getAttachments(),
quote,
sharedContacts,
linkPreviews,
mentions,
sticker);
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(RecipientId.fromHighTrust(content.getSender()),
message.getTimestamp(),
content.getServerReceivedTimestamp(),
-1,
message.getExpiresInSeconds() * 1000L,
false,
message.isViewOnce(),
content.isNeedsReceipt(),
message.getBody(),
message.getGroupContext(),
message.getAttachments(),
quote,
sharedContacts,
linkPreviews,
mentions,
sticker,
content.getServerUuid());
insertResult = database.insertSecureDecryptedMessageInbox(mediaMessage, -1);
@@ -1328,13 +1334,14 @@ public final class MessageContentProcessor {
notifyTypingStoppedFromIncomingMessage(recipient, content.getSender(), content.getSenderDevice());
IncomingTextMessage textMessage = new IncomingTextMessage(RecipientId.fromHighTrust(content.getSender()),
content.getSenderDevice(),
message.getTimestamp(),
content.getServerReceivedTimestamp(),
body,
groupId,
message.getExpiresInSeconds() * 1000L,
content.isNeedsReceipt());
content.getSenderDevice(),
message.getTimestamp(),
content.getServerReceivedTimestamp(),
body,
groupId,
message.getExpiresInSeconds() * 1000L,
content.isNeedsReceipt(),
content.getServerUuid());
textMessage = new IncomingEncryptedMessage(textMessage, body);
Optional<InsertResult> insertResult = database.insertMessageInbox(textMessage);
@@ -1803,8 +1810,8 @@ public final class MessageContentProcessor {
private Optional<InsertResult> insertPlaceholder(@NonNull String sender, int senderDevice, long timestamp, Optional<GroupId> groupId) {
MessageDatabase database = DatabaseFactory.getSmsDatabase(context);
IncomingTextMessage textMessage = new IncomingTextMessage(Recipient.external(context, sender).getId(),
senderDevice, timestamp, -1, "",
groupId, 0, false);
senderDevice, timestamp, -1, "",
groupId, 0, false, null);
textMessage = new IncomingEncryptedMessage(textMessage, "");
return database.insertMessageInbox(textMessage);

View File

@@ -1,6 +1,7 @@
package org.thoughtcrime.securesms.mms;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.attachments.PointerAttachment;
@@ -32,6 +33,7 @@ public class IncomingMediaMessage {
private final QuoteModel quote;
private final boolean unidentified;
private final boolean viewOnce;
private final String serverGuid;
private final List<Attachment> attachments = new LinkedList<>();
private final List<Contact> sharedContacts = new LinkedList<>();
@@ -63,6 +65,7 @@ public class IncomingMediaMessage {
this.viewOnce = viewOnce;
this.quote = null;
this.unidentified = unidentified;
this.serverGuid = null;
this.attachments.addAll(attachments);
this.sharedContacts.addAll(sharedContacts.or(Collections.emptyList()));
@@ -84,7 +87,8 @@ public class IncomingMediaMessage {
Optional<List<Contact>> sharedContacts,
Optional<List<LinkPreview>> linkPreviews,
Optional<List<Mention>> mentions,
Optional<Attachment> sticker)
Optional<Attachment> sticker,
@Nullable String serverGuid)
{
this.push = true;
this.from = from;
@@ -109,6 +113,8 @@ public class IncomingMediaMessage {
if (sticker.isPresent()) {
this.attachments.add(sticker.get());
}
this.serverGuid = serverGuid;
}
public int getSubscriptionId() {
@@ -178,4 +184,8 @@ public class IncomingMediaMessage {
public boolean isUnidentified() {
return unidentified;
}
public @Nullable String getServerGuid() {
return serverGuid;
}
}

View File

@@ -6,7 +6,7 @@ import org.whispersystems.libsignal.util.guava.Optional;
public class IncomingJoinedMessage extends IncomingTextMessage {
public IncomingJoinedMessage(RecipientId sender) {
super(sender, 1, System.currentTimeMillis(), -1, null, Optional.absent(), 0, false);
super(sender, 1, System.currentTimeMillis(), -1, null, Optional.absent(), 0, false, null);
}
@Override

View File

@@ -44,6 +44,7 @@ public class IncomingTextMessage implements Parcelable {
private final int subscriptionId;
private final long expiresInMillis;
private final boolean unidentified;
@Nullable private final String serverGuid;
public IncomingTextMessage(@NonNull RecipientId sender, @NonNull SmsMessage message, int subscriptionId) {
this.message = message.getDisplayMessageBody();
@@ -60,6 +61,7 @@ public class IncomingTextMessage implements Parcelable {
this.groupId = null;
this.push = false;
this.unidentified = false;
this.serverGuid = null;
}
public IncomingTextMessage(@NonNull RecipientId sender,
@@ -69,7 +71,8 @@ public class IncomingTextMessage implements Parcelable {
String encodedBody,
Optional<GroupId> groupId,
long expiresInMillis,
boolean unidentified)
boolean unidentified,
String serverGuid)
{
this.message = encodedBody;
this.sender = sender;
@@ -85,6 +88,7 @@ public class IncomingTextMessage implements Parcelable {
this.expiresInMillis = expiresInMillis;
this.unidentified = unidentified;
this.groupId = groupId.orNull();
this.serverGuid = serverGuid;
}
public IncomingTextMessage(Parcel in) {
@@ -102,6 +106,7 @@ public class IncomingTextMessage implements Parcelable {
this.subscriptionId = in.readInt();
this.expiresInMillis = in.readLong();
this.unidentified = in.readInt() == 1;
this.serverGuid = in.readString();
}
public IncomingTextMessage(IncomingTextMessage base, String newBody) {
@@ -119,6 +124,7 @@ public class IncomingTextMessage implements Parcelable {
this.subscriptionId = base.getSubscriptionId();
this.expiresInMillis = base.getExpiresIn();
this.unidentified = base.isUnidentified();
this.serverGuid = base.getServerGuid();
}
public IncomingTextMessage(List<IncomingTextMessage> fragments) {
@@ -142,6 +148,7 @@ public class IncomingTextMessage implements Parcelable {
this.subscriptionId = fragments.get(0).getSubscriptionId();
this.expiresInMillis = fragments.get(0).getExpiresIn();
this.unidentified = fragments.get(0).isUnidentified();
this.serverGuid = fragments.get(0).getServerGuid();
}
protected IncomingTextMessage(@NonNull RecipientId sender, @Nullable GroupId groupId)
@@ -160,6 +167,7 @@ public class IncomingTextMessage implements Parcelable {
this.subscriptionId = -1;
this.expiresInMillis = 0;
this.unidentified = false;
this.serverGuid = null;
}
public int getSubscriptionId() {
@@ -265,6 +273,10 @@ public class IncomingTextMessage implements Parcelable {
return unidentified;
}
public @Nullable String getServerGuid() {
return serverGuid;
}
@Override
public int describeContents() {
return 0;
@@ -285,5 +297,6 @@ public class IncomingTextMessage implements Parcelable {
out.writeInt(subscriptionId);
out.writeLong(expiresInMillis);
out.writeInt(unidentified ? 1 : 0);
out.writeString(serverGuid);
}
}

View File

@@ -74,7 +74,7 @@ public final class IdentityUtil {
if (groupRecord.getMembers().contains(recipient.getId()) && groupRecord.isActive() && !groupRecord.isMms()) {
if (remote) {
IncomingTextMessage incoming = new IncomingTextMessage(recipient.getId(), 1, time, -1, null, Optional.of(groupRecord.getId()), 0, false);
IncomingTextMessage incoming = new IncomingTextMessage(recipient.getId(), 1, time, -1, null, Optional.of(groupRecord.getId()), 0, false, null);
if (verified) incoming = new IncomingIdentityVerifiedMessage(incoming);
else incoming = new IncomingIdentityDefaultMessage(incoming);
@@ -96,7 +96,7 @@ public final class IdentityUtil {
}
if (remote) {
IncomingTextMessage incoming = new IncomingTextMessage(recipient.getId(), 1, time, -1, null, Optional.absent(), 0, false);
IncomingTextMessage incoming = new IncomingTextMessage(recipient.getId(), 1, time, -1, null, Optional.absent(), 0, false, null);
if (verified) incoming = new IncomingIdentityVerifiedMessage(incoming);
else incoming = new IncomingIdentityDefaultMessage(incoming);
@@ -125,7 +125,7 @@ public final class IdentityUtil {
while ((groupRecord = reader.getNext()) != null) {
if (groupRecord.getMembers().contains(recipientId) && groupRecord.isActive()) {
IncomingTextMessage incoming = new IncomingTextMessage(recipientId, 1, time, time, null, Optional.of(groupRecord.getId()), 0, false);
IncomingTextMessage incoming = new IncomingTextMessage(recipientId, 1, time, time, null, Optional.of(groupRecord.getId()), 0, false, null);
IncomingIdentityUpdateMessage groupUpdate = new IncomingIdentityUpdateMessage(incoming);
smsDatabase.insertMessageInbox(groupUpdate);
@@ -133,7 +133,7 @@ public final class IdentityUtil {
}
}
IncomingTextMessage incoming = new IncomingTextMessage(recipientId, 1, time, -1, null, Optional.absent(), 0, false);
IncomingTextMessage incoming = new IncomingTextMessage(recipientId, 1, time, -1, null, Optional.absent(), 0, false, null);
IncomingIdentityUpdateMessage individualUpdate = new IncomingIdentityUpdateMessage(incoming);
Optional<InsertResult> insertResult = smsDatabase.insertMessageInbox(individualUpdate);