Merge pull request #508 from Brice-W/data-extraction-2

Data extraction notifications
This commit is contained in:
Niels Andriesse 2021-04-21 13:44:46 +10:00 committed by GitHub
commit 9f26436041
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 239 additions and 36 deletions

View File

@ -52,6 +52,8 @@ import androidx.viewpager.widget.ViewPager;
import com.codewaves.stickyheadergrid.StickyHeaderGridLayoutManager; import com.codewaves.stickyheadergrid.StickyHeaderGridLayoutManager;
import com.google.android.material.tabs.TabLayout; import com.google.android.material.tabs.TabLayout;
import org.session.libsession.messaging.messages.control.DataExtractionNotification;
import org.session.libsession.messaging.sending_receiving.MessageSender;
import org.session.libsession.messaging.threads.Address; import org.session.libsession.messaging.threads.Address;
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter; import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter;
import org.thoughtcrime.securesms.database.MediaDatabase; import org.thoughtcrime.securesms.database.MediaDatabase;
@ -351,6 +353,12 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity {
saveTask.executeOnExecutor(THREAD_POOL_EXECUTOR, saveTask.executeOnExecutor(THREAD_POOL_EXECUTOR,
attachments.toArray(new SaveAttachmentTask.Attachment[attachments.size()])); attachments.toArray(new SaveAttachmentTask.Attachment[attachments.size()]));
actionMode.finish(); actionMode.finish();
// Sending a Data extraction notification (for incoming attachments only)
boolean containsIncoming = mediaRecords.parallelStream().anyMatch(m -> !m.isOutgoing());
if (containsIncoming) {
//TODO uncomment line below when Data extraction will be activated
//sendMediaSavedNotificationIfNeeded();
}
} }
}.execute(); }.execute();
}) })
@ -358,6 +366,16 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity {
}, mediaRecords.size()); }, mediaRecords.size());
} }
/**
* Send a MediaSaved notification to the recipient
*/
private void sendMediaSavedNotificationIfNeeded() {
// we don't send media saved notification for groups
if (recipient.isGroupRecipient()) return;
DataExtractionNotification message = new DataExtractionNotification(new DataExtractionNotification.Kind.MediaSaved(System.currentTimeMillis()));
MessageSender.send(message, recipient.getAddress());
}
@SuppressLint("StaticFieldLeak") @SuppressLint("StaticFieldLeak")
private void handleDeleteMedia(@NonNull Collection<MediaDatabase.MediaRecord> mediaRecords) { private void handleDeleteMedia(@NonNull Collection<MediaDatabase.MediaRecord> mediaRecords) {
int recordCount = mediaRecords.size(); int recordCount = mediaRecords.size();

View File

@ -39,7 +39,8 @@ import android.view.Window;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import org.session.libsession.messaging.messages.control.DataExtractionNotification;
import org.session.libsession.messaging.sending_receiving.MessageSender;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.ActionBar;
@ -52,7 +53,6 @@ import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import androidx.viewpager.widget.PagerAdapter; import androidx.viewpager.widget.PagerAdapter;
import androidx.viewpager.widget.ViewPager; import androidx.viewpager.widget.ViewPager;
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment; import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment;
import org.session.libsession.messaging.threads.Address; import org.session.libsession.messaging.threads.Address;
import org.session.libsession.messaging.threads.recipients.Recipient; import org.session.libsession.messaging.threads.recipients.Recipient;
@ -352,11 +352,26 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
saveTask.executeOnExecutor( saveTask.executeOnExecutor(
AsyncTask.THREAD_POOL_EXECUTOR, AsyncTask.THREAD_POOL_EXECUTOR,
new Attachment(mediaItem.uri, mediaItem.type, saveDate, null)); new Attachment(mediaItem.uri, mediaItem.type, saveDate, null));
// Sending a Data extraction notification (for incoming attachments only)
if(!mediaItem.outgoing) {
//TODO uncomment line below when Data extraction will be activated
//sendMediaSavedNotificationIfNeeded();
}
}) })
.execute(); .execute();
}); });
} }
/**
* Send a MediaSaved notification to the recipient
*/
private void sendMediaSavedNotificationIfNeeded() {
// we don't send media saved notification for groups
if (conversationRecipient.isGroupRecipient()) return;
DataExtractionNotification message = new DataExtractionNotification(new DataExtractionNotification.Kind.MediaSaved(System.currentTimeMillis()));
MessageSender.send(message, conversationRecipient.getAddress());
}
@SuppressLint("StaticFieldLeak") @SuppressLint("StaticFieldLeak")
private void deleteMedia() { private void deleteMedia() {
MediaItem mediaItem = getCurrentMediaItem(); MediaItem mediaItem = getCurrentMediaItem();

View File

@ -57,6 +57,7 @@ import androidx.recyclerview.widget.RecyclerView.OnScrollListener;
import com.annimon.stream.Stream; import com.annimon.stream.Stream;
import org.session.libsession.messaging.messages.control.DataExtractionNotification;
import org.session.libsession.messaging.messages.visible.Quote; import org.session.libsession.messaging.messages.visible.Quote;
import org.session.libsession.messaging.messages.visible.VisibleMessage; import org.session.libsession.messaging.messages.visible.VisibleMessage;
import org.session.libsession.messaging.opengroups.OpenGroupAPI; import org.session.libsession.messaging.opengroups.OpenGroupAPI;
@ -745,6 +746,11 @@ public class ConversationFragment extends Fragment
if (!Util.isEmpty(attachments)) { if (!Util.isEmpty(attachments)) {
SaveAttachmentTask saveTask = new SaveAttachmentTask(getActivity()); SaveAttachmentTask saveTask = new SaveAttachmentTask(getActivity());
saveTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, attachments.toArray(new SaveAttachmentTask.Attachment[0])); saveTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, attachments.toArray(new SaveAttachmentTask.Attachment[0]));
// Sending a Data extraction notification (for incoming attachments only)
if(!message.isOutgoing()) {
//TODO uncomment line below when Data extraction will be activated
//sendMediaSavedNotificationIfNeeded();
}
return; return;
} }
@ -757,6 +763,16 @@ public class ConversationFragment extends Fragment
}); });
} }
/**
* Send a MediaSaved notification to the recipient
*/
private void sendMediaSavedNotificationIfNeeded() {
// we don't send media saved notification for groups
if (recipient.isGroupRecipient()) return;
DataExtractionNotification message = new DataExtractionNotification(new DataExtractionNotification.Kind.MediaSaved(System.currentTimeMillis()));
MessageSender.send(message, recipient.getAddress());
}
@Override @Override
public @NonNull Loader<Cursor> onCreateLoader(int id, Bundle args) { public @NonNull Loader<Cursor> onCreateLoader(int id, Bundle args) {
Log.i(TAG, "onCreateLoader"); Log.i(TAG, "onCreateLoader");

View File

@ -14,6 +14,7 @@ import androidx.annotation.ColorInt;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import org.session.libsession.messaging.sending_receiving.dataextraction.DataExtractionNotificationInfoMessage;
import org.thoughtcrime.securesms.BindableConversationItem; import org.thoughtcrime.securesms.BindableConversationItem;
import org.thoughtcrime.securesms.database.model.MessageRecord; import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.loki.utilities.GeneralUtilitiesKt; import org.thoughtcrime.securesms.loki.utilities.GeneralUtilitiesKt;
@ -104,6 +105,8 @@ public class ConversationUpdateItem extends LinearLayout
else if (messageRecord.isCallLog()) setCallRecord(messageRecord); else if (messageRecord.isCallLog()) setCallRecord(messageRecord);
else if (messageRecord.isJoined()) setJoinedRecord(messageRecord); else if (messageRecord.isJoined()) setJoinedRecord(messageRecord);
else if (messageRecord.isExpirationTimerUpdate()) setTimerRecord(messageRecord); else if (messageRecord.isExpirationTimerUpdate()) setTimerRecord(messageRecord);
else if (messageRecord.isScreenshotExtraction()) setDataExtractionRecord(messageRecord, DataExtractionNotificationInfoMessage.Kind.SCREENSHOT);
else if (messageRecord.isMediaSavedExtraction()) setDataExtractionRecord(messageRecord, DataExtractionNotificationInfoMessage.Kind.MEDIA_SAVED);
else if (messageRecord.isEndSession()) setEndSessionRecord(messageRecord); else if (messageRecord.isEndSession()) setEndSessionRecord(messageRecord);
else if (messageRecord.isIdentityUpdate()) setIdentityRecord(messageRecord); else if (messageRecord.isIdentityUpdate()) setIdentityRecord(messageRecord);
else if (messageRecord.isIdentityVerified() || else if (messageRecord.isIdentityVerified() ||
@ -147,6 +150,22 @@ public class ConversationUpdateItem extends LinearLayout
date.setVisibility(GONE); date.setVisibility(GONE);
} }
private void setDataExtractionRecord(final MessageRecord messageRecord, DataExtractionNotificationInfoMessage.Kind kind) {
@ColorInt int color = GeneralUtilitiesKt.getColorWithID(getResources(), R.color.text, getContext().getTheme());
if (kind == DataExtractionNotificationInfoMessage.Kind.SCREENSHOT) {
icon.setImageResource(R.drawable.quick_camera_dark);
} else if (kind == DataExtractionNotificationInfoMessage.Kind.MEDIA_SAVED) {
icon.setImageResource(R.drawable.ic_file_download_white_36dp);
}
icon.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.MULTIPLY));
body.setText(messageRecord.getDisplayBody(getContext()));
title.setVisibility(VISIBLE);
body.setVisibility(VISIBLE);
date.setVisibility(GONE);
}
private void setIdentityRecord(final MessageRecord messageRecord) { private void setIdentityRecord(final MessageRecord messageRecord) {
icon.setImageResource(R.drawable.ic_security_white_24dp); icon.setImageResource(R.drawable.ic_security_white_24dp);
icon.setColorFilter(new PorterDuffColorFilter(Color.parseColor("#757575"), PorterDuff.Mode.MULTIPLY)); icon.setColorFilter(new PorterDuffColorFilter(Color.parseColor("#757575"), PorterDuff.Mode.MULTIPLY));

View File

@ -719,6 +719,14 @@ public class MmsDatabase extends MessagingDatabase {
type |= Types.EXPIRATION_TIMER_UPDATE_BIT; type |= Types.EXPIRATION_TIMER_UPDATE_BIT;
} }
if (retrieved.isScreenshotDataExtraction()) {
type |= Types.SCREENSHOT_EXTRACTION_BIT;
}
if (retrieved.isMediaSavedDataExtraction()) {
type |= Types.MEDIA_SAVED_EXTRACTION_BIT;
}
return insertMessageInbox(retrieved, "", threadId, type, serverTimestamp); return insertMessageInbox(retrieved, "", threadId, type, serverTimestamp);
} }

View File

@ -72,6 +72,10 @@ public interface MmsSmsColumns {
protected static final long EXPIRATION_TIMER_UPDATE_BIT = 0x40000; protected static final long EXPIRATION_TIMER_UPDATE_BIT = 0x40000;
protected static final long GROUP_UPDATE_MESSAGE_BIT = 0x80000; protected static final long GROUP_UPDATE_MESSAGE_BIT = 0x80000;
// Data Extraction Information
protected static final long MEDIA_SAVED_EXTRACTION_BIT = 0x01000;
protected static final long SCREENSHOT_EXTRACTION_BIT = 0x02000;
// Encrypted Storage Information XXX // Encrypted Storage Information XXX
public static final long ENCRYPTION_MASK = 0xFF000000; public static final long ENCRYPTION_MASK = 0xFF000000;
// public static final long ENCRYPTION_SYMMETRIC_BIT = 0x80000000; Deprecated // public static final long ENCRYPTION_SYMMETRIC_BIT = 0x80000000; Deprecated
@ -198,6 +202,14 @@ public interface MmsSmsColumns {
return (type & EXPIRATION_TIMER_UPDATE_BIT) != 0; return (type & EXPIRATION_TIMER_UPDATE_BIT) != 0;
} }
public static boolean isMediaSavedExtraction(long type) {
return (type & MEDIA_SAVED_EXTRACTION_BIT) != 0;
}
public static boolean isScreenshotExtraction(long type) {
return (type & SCREENSHOT_EXTRACTION_BIT) != 0;
}
public static boolean isIncomingCall(long type) { public static boolean isIncomingCall(long type) {
return type == INCOMING_CALL_TYPE; return type == INCOMING_CALL_TYPE;
} }

View File

@ -16,9 +16,11 @@ import org.session.libsession.messaging.messages.visible.VisibleMessage
import org.session.libsession.messaging.opengroups.OpenGroup import org.session.libsession.messaging.opengroups.OpenGroup
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentId import org.session.libsession.messaging.sending_receiving.attachments.AttachmentId
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment
import org.session.libsession.messaging.sending_receiving.dataextraction.DataExtractionNotificationInfoMessage
import org.session.libsession.messaging.sending_receiving.linkpreview.LinkPreview import org.session.libsession.messaging.sending_receiving.linkpreview.LinkPreview
import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel
import org.session.libsession.messaging.threads.Address import org.session.libsession.messaging.threads.Address
import org.session.libsession.messaging.threads.Address.Companion.fromSerialized
import org.session.libsession.messaging.threads.GroupRecord import org.session.libsession.messaging.threads.GroupRecord
import org.session.libsession.messaging.threads.recipients.Recipient import org.session.libsession.messaging.threads.recipients.Recipient
import org.session.libsession.messaging.utilities.UpdateMessageBuilder import org.session.libsession.messaging.utilities.UpdateMessageBuilder
@ -146,7 +148,6 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
val linkPreviews: Optional<List<LinkPreview>> = if (linkPreview.isEmpty()) Optional.absent() else Optional.of(linkPreview.mapNotNull { it!! }) val linkPreviews: Optional<List<LinkPreview>> = if (linkPreview.isEmpty()) Optional.absent() else Optional.of(linkPreview.mapNotNull { it!! })
val mmsDatabase = DatabaseFactory.getMmsDatabase(context) val mmsDatabase = DatabaseFactory.getMmsDatabase(context)
val insertResult = if (message.sender == getUserPublicKey()) { val insertResult = if (message.sender == getUserPublicKey()) {
val mediaMessage = OutgoingMediaMessage.from(message, targetRecipient, pointerAttachments, quote.orNull(), linkPreviews.orNull()?.firstOrNull()) val mediaMessage = OutgoingMediaMessage.from(message, targetRecipient, pointerAttachments, quote.orNull(), linkPreviews.orNull()?.firstOrNull())
mmsDatabase.beginTransaction() mmsDatabase.beginTransaction()
mmsDatabase.insertSecureDecryptedMessageOutbox(mediaMessage, message.threadID ?: -1, message.sentTimestamp!!) mmsDatabase.insertSecureDecryptedMessageOutbox(mediaMessage, message.threadID ?: -1, message.sentTimestamp!!)
@ -419,7 +420,7 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
val infoMessage = OutgoingGroupMediaMessage(recipient, updateData, groupID, null, sentTimestamp, 0, true, null, listOf(), listOf()) val infoMessage = OutgoingGroupMediaMessage(recipient, updateData, groupID, null, sentTimestamp, 0, true, null, listOf(), listOf())
val mmsDB = DatabaseFactory.getMmsDatabase(context) val mmsDB = DatabaseFactory.getMmsDatabase(context)
val mmsSmsDB = DatabaseFactory.getMmsSmsDatabase(context) val mmsSmsDB = DatabaseFactory.getMmsSmsDatabase(context)
if (mmsSmsDB.getMessageFor(sentTimestamp,userPublicKey) != null) return if (mmsSmsDB.getMessageFor(sentTimestamp, userPublicKey) != null) return
val infoMessageID = mmsDB.insertMessageOutbox(infoMessage, threadID, false, null) val infoMessageID = mmsDB.insertMessageOutbox(infoMessage, threadID, false, null)
mmsDB.markAsSent(infoMessageID, true) mmsDB.markAsSent(infoMessageID, true)
} }
@ -577,4 +578,26 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
override fun getAttachmentThumbnailUri(attachmentId: AttachmentId): Uri { override fun getAttachmentThumbnailUri(attachmentId: AttachmentId): Uri {
return PartAuthority.getAttachmentThumbnailUri(attachmentId) return PartAuthority.getAttachmentThumbnailUri(attachmentId)
} }
// Data Extraction Notification
override fun insertDataExtractionNotificationMessage(senderPublicKey: String, message: DataExtractionNotificationInfoMessage, sentTimestamp: Long) {
val database = DatabaseFactory.getMmsDatabase(context)
val address = fromSerialized(senderPublicKey)
val recipient = Recipient.from(context, address, false)
if (recipient.isBlocked) return
val mediaMessage = IncomingMediaMessage(address, sentTimestamp, -1,
0, false,
false,
Optional.absent(),
Optional.absent(),
Optional.absent(),
Optional.absent(),
Optional.absent(),
Optional.absent(),
Optional.of(message))
database.insertSecureDecryptedMessageInbox(mediaMessage, -1)
}
} }

View File

@ -131,6 +131,20 @@ public abstract class DisplayRecord {
return SmsDatabase.Types.isExpirationTimerUpdate(type); return SmsDatabase.Types.isExpirationTimerUpdate(type);
} }
// Data extraction
public boolean isMediaSavedExtraction() {
return MmsSmsColumns.Types.isMediaSavedExtraction(type);
}
public boolean isScreenshotExtraction() {
return MmsSmsColumns.Types.isScreenshotExtraction(type);
}
public boolean isDataExtraction() {
return isMediaSavedExtraction() || isScreenshotExtraction();
}
public boolean isCallLog() { public boolean isCallLog() {
return SmsDatabase.Types.isCallLog(type); return SmsDatabase.Types.isCallLog(type);
} }

View File

@ -25,6 +25,7 @@ import android.text.style.StyleSpan;
import network.loki.messenger.R; import network.loki.messenger.R;
import org.session.libsession.messaging.sending_receiving.dataextraction.DataExtractionNotificationInfoMessage;
import org.session.libsession.messaging.utilities.UpdateMessageBuilder; import org.session.libsession.messaging.utilities.UpdateMessageBuilder;
import org.session.libsession.messaging.utilities.UpdateMessageData; import org.session.libsession.messaging.utilities.UpdateMessageData;
import org.thoughtcrime.securesms.database.MmsSmsColumns; import org.thoughtcrime.securesms.database.MmsSmsColumns;
@ -97,6 +98,9 @@ public abstract class MessageRecord extends DisplayRecord {
} else if (isExpirationTimerUpdate()) { } else if (isExpirationTimerUpdate()) {
int seconds = (int) (getExpiresIn() / 1000); int seconds = (int) (getExpiresIn() / 1000);
return new SpannableString(UpdateMessageBuilder.INSTANCE.buildExpirationTimerMessage(context, seconds, getIndividualRecipient().getAddress().serialize(), isOutgoing())); return new SpannableString(UpdateMessageBuilder.INSTANCE.buildExpirationTimerMessage(context, seconds, getIndividualRecipient().getAddress().serialize(), isOutgoing()));
} else if (isDataExtraction()) {
if (isScreenshotExtraction()) return new SpannableString((UpdateMessageBuilder.INSTANCE.buildDataExtractionMessage(context, DataExtractionNotificationInfoMessage.Kind.SCREENSHOT, getIndividualRecipient().getAddress().serialize())));
else if (isMediaSavedExtraction()) return new SpannableString((UpdateMessageBuilder.INSTANCE.buildDataExtractionMessage(context, DataExtractionNotificationInfoMessage.Kind.MEDIA_SAVED, getIndividualRecipient().getAddress().serialize())));
} }
// TODO below lines are left here for compatibility with older group update messages, it can be deleted later on // TODO below lines are left here for compatibility with older group update messages, it can be deleted later on
else if (isGroupUpdate() && isOutgoing()) { else if (isGroupUpdate() && isOutgoing()) {
@ -159,7 +163,7 @@ public abstract class MessageRecord extends DisplayRecord {
} }
public boolean isUpdate() { public boolean isUpdate() {
return isGroupAction() || isJoined() || isExpirationTimerUpdate() || isCallLog() || return isGroupAction() || isJoined() || isExpirationTimerUpdate() || isCallLog() || isDataExtraction() ||
isEndSession() || isIdentityUpdate() || isIdentityVerified() || isIdentityDefault() || isLokiSessionRestoreSent() || isLokiSessionRestoreDone(); isEndSession() || isIdentityUpdate() || isIdentityVerified() || isIdentityDefault() || isLokiSessionRestoreSent() || isLokiSessionRestoreDone();
} }

View File

@ -105,12 +105,16 @@ public class ThreadRecord extends DisplayRecord {
} else if (SmsDatabase.Types.isJoinedType(type)) { } else if (SmsDatabase.Types.isJoinedType(type)) {
return emphasisAdded(context.getString(R.string.ThreadRecord_s_is_on_signal, getRecipient().toShortString())); return emphasisAdded(context.getString(R.string.ThreadRecord_s_is_on_signal, getRecipient().toShortString()));
} else if (SmsDatabase.Types.isExpirationTimerUpdate(type)) { } else if (SmsDatabase.Types.isExpirationTimerUpdate(type)) {
int seconds = (int)(getExpiresIn() / 1000); int seconds = (int) (getExpiresIn() / 1000);
if (seconds <= 0) { if (seconds <= 0) {
return emphasisAdded(context.getString(R.string.ThreadRecord_disappearing_messages_disabled)); return emphasisAdded(context.getString(R.string.ThreadRecord_disappearing_messages_disabled));
} }
String time = ExpirationUtil.getExpirationDisplayValue(context, seconds); String time = ExpirationUtil.getExpirationDisplayValue(context, seconds);
return emphasisAdded(context.getString(R.string.ThreadRecord_disappearing_message_time_updated_to_s, time)); return emphasisAdded(context.getString(R.string.ThreadRecord_disappearing_message_time_updated_to_s, time));
} else if (MmsSmsColumns.Types.isMediaSavedExtraction(type)) {
return emphasisAdded(context.getString(R.string.ThreadRecord_media_saved_by_s, getRecipient().toShortString()));
} else if (MmsSmsColumns.Types.isScreenshotExtraction(type)) {
return emphasisAdded(context.getString(R.string.ThreadRecord_s_took_a_screenshot, getRecipient().toShortString()));
} else if (SmsDatabase.Types.isIdentityUpdate(type)) { } else if (SmsDatabase.Types.isIdentityUpdate(type)) {
if (getRecipient().isGroupRecipient()) return emphasisAdded(context.getString(R.string.ThreadRecord_safety_number_changed)); if (getRecipient().isGroupRecipient()) return emphasisAdded(context.getString(R.string.ThreadRecord_safety_number_changed));
else return emphasisAdded(context.getString(R.string.ThreadRecord_your_safety_number_with_s_has_changed, getRecipient().toShortString())); else return emphasisAdded(context.getString(R.string.ThreadRecord_your_safety_number_with_s_has_changed, getRecipient().toShortString()));

View File

@ -117,6 +117,7 @@ public class ExpiringMessageManager implements SSKEnvironment.MessageExpirationM
Optional.absent(), Optional.absent(),
Optional.absent(), Optional.absent(),
Optional.absent(), Optional.absent(),
Optional.absent(),
Optional.absent()); Optional.absent());
//insert the timer update message //insert the timer update message
database.insertSecureDecryptedMessageInbox(mediaMessage, -1); database.insertSecureDecryptedMessageInbox(mediaMessage, -1);

View File

@ -1,4 +0,0 @@
package org.thoughtcrime.securesms.sskenvironment
class DataExtractionNotificationManager {
}

View File

@ -440,6 +440,8 @@
<string name="MessageRecord_s_disabled_disappearing_messages">%1$s a désactivé les messages éphémères.</string> <string name="MessageRecord_s_disabled_disappearing_messages">%1$s a désactivé les messages éphémères.</string>
<string name="MessageRecord_you_set_disappearing_message_time_to_s">Vous avez défini lexpiration des messages éphémères à %1$s.</string> <string name="MessageRecord_you_set_disappearing_message_time_to_s">Vous avez défini lexpiration des messages éphémères à %1$s.</string>
<string name="MessageRecord_s_set_disappearing_message_time_to_s">%1$s a défini lexpiration des messages éphémères à %2$s.</string> <string name="MessageRecord_s_set_disappearing_message_time_to_s">%1$s a défini lexpiration des messages éphémères à %2$s.</string>
<string name="MessageRecord_s_took_a_screenshot">%1$s a pris une capture décran.</string>
<string name="MessageRecord_media_saved_by_s">%1$s a sauvegardé un media.</string>
<string name="MessageRecord_your_safety_number_with_s_has_changed">Votre numéro de sécurité avec %s a changé.</string> <string name="MessageRecord_your_safety_number_with_s_has_changed">Votre numéro de sécurité avec %s a changé.</string>
<string name="MessageRecord_you_marked_your_safety_number_with_s_verified">Vous avez marqué votre numéro de sécurité avec %s comme vérifié</string> <string name="MessageRecord_you_marked_your_safety_number_with_s_verified">Vous avez marqué votre numéro de sécurité avec %s comme vérifié</string>
<string name="MessageRecord_you_marked_your_safety_number_with_s_verified_from_another_device">Vous avez marqué votre numéro de sécurité avec %s comme vérifié à partir dun autre appareil</string> <string name="MessageRecord_you_marked_your_safety_number_with_s_verified_from_another_device">Vous avez marqué votre numéro de sécurité avec %s comme vérifié à partir dun autre appareil</string>

View File

@ -515,6 +515,8 @@
<string name="MessageRecord_s_disabled_disappearing_messages">%1$s disabled disappearing messages.</string> <string name="MessageRecord_s_disabled_disappearing_messages">%1$s disabled disappearing messages.</string>
<string name="MessageRecord_you_set_disappearing_message_time_to_s">You set the disappearing message timer to %1$s</string> <string name="MessageRecord_you_set_disappearing_message_time_to_s">You set the disappearing message timer to %1$s</string>
<string name="MessageRecord_s_set_disappearing_message_time_to_s">%1$s set the disappearing message timer to %2$s</string> <string name="MessageRecord_s_set_disappearing_message_time_to_s">%1$s set the disappearing message timer to %2$s</string>
<string name="MessageRecord_s_took_a_screenshot">%1$s took a screenshot.</string>
<string name="MessageRecord_media_saved_by_s">Media saved by %1$s.</string>
<string name="MessageRecord_your_safety_number_with_s_has_changed">Your safety number with %s has changed.</string> <string name="MessageRecord_your_safety_number_with_s_has_changed">Your safety number with %s has changed.</string>
<string name="MessageRecord_you_marked_your_safety_number_with_s_verified">You marked your safety number with %s verified</string> <string name="MessageRecord_you_marked_your_safety_number_with_s_verified">You marked your safety number with %s verified</string>
<string name="MessageRecord_you_marked_your_safety_number_with_s_verified_from_another_device">You marked your safety number with %s verified from another device</string> <string name="MessageRecord_you_marked_your_safety_number_with_s_verified_from_another_device">You marked your safety number with %s verified from another device</string>
@ -713,6 +715,8 @@
<string name="ThreadRecord_s_is_on_signal">%s is on Session!</string> <string name="ThreadRecord_s_is_on_signal">%s is on Session!</string>
<string name="ThreadRecord_disappearing_messages_disabled">Disappearing messages disabled</string> <string name="ThreadRecord_disappearing_messages_disabled">Disappearing messages disabled</string>
<string name="ThreadRecord_disappearing_message_time_updated_to_s">Disappearing message time set to %s</string> <string name="ThreadRecord_disappearing_message_time_updated_to_s">Disappearing message time set to %s</string>
<string name="ThreadRecord_s_took_a_screenshot">%s took a screenshot.</string>
<string name="ThreadRecord_media_saved_by_s">Media saved by %s.</string>
<string name="ThreadRecord_safety_number_changed">Safety number changed</string> <string name="ThreadRecord_safety_number_changed">Safety number changed</string>
<string name="ThreadRecord_your_safety_number_with_s_has_changed">Your safety number with %s has changed.</string> <string name="ThreadRecord_your_safety_number_with_s_has_changed">Your safety number with %s has changed.</string>
<string name="ThreadRecord_you_marked_verified">You marked verified</string> <string name="ThreadRecord_you_marked_verified">You marked verified</string>

View File

@ -11,6 +11,7 @@ import org.session.libsession.messaging.messages.visible.Attachment
import org.session.libsession.messaging.messages.visible.VisibleMessage import org.session.libsession.messaging.messages.visible.VisibleMessage
import org.session.libsession.messaging.opengroups.OpenGroup import org.session.libsession.messaging.opengroups.OpenGroup
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentId import org.session.libsession.messaging.sending_receiving.attachments.AttachmentId
import org.session.libsession.messaging.sending_receiving.dataextraction.DataExtractionNotificationInfoMessage
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment
import org.session.libsession.messaging.sending_receiving.linkpreview.LinkPreview import org.session.libsession.messaging.sending_receiving.linkpreview.LinkPreview
import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel
@ -159,4 +160,7 @@ interface StorageProtocol {
// Message Handling // Message Handling
/// Returns the ID of the `TSIncomingMessage` that was constructed. /// Returns the ID of the `TSIncomingMessage` that was constructed.
fun persist(message: VisibleMessage, quotes: QuoteModel?, linkPreview: List<LinkPreview?>, groupPublicKey: String?, openGroupID: String?, attachments: List<Attachment>): Long? fun persist(message: VisibleMessage, quotes: QuoteModel?, linkPreview: List<LinkPreview?>, groupPublicKey: String?, openGroupID: String?, attachments: List<Attachment>): Long?
// Data Extraction Notification
fun insertDataExtractionNotificationMessage(senderPublicKey: String, message: DataExtractionNotificationInfoMessage, sentTimestamp: Long)
} }

View File

@ -9,7 +9,7 @@ class DataExtractionNotification(): ControlMessage() {
// Kind enum // Kind enum
sealed class Kind { sealed class Kind {
class Screenshot() : Kind() class Screenshot() : Kind()
class MediaSaved(val timestanp: Long) : Kind() class MediaSaved(val timestamp: Long) : Kind()
val description: String = val description: String =
when(this) { when(this) {
@ -46,7 +46,7 @@ class DataExtractionNotification(): ControlMessage() {
val kind = kind ?: return false val kind = kind ?: return false
return when(kind) { return when(kind) {
is Kind.Screenshot -> true is Kind.Screenshot -> true
is Kind.MediaSaved -> kind.timestanp > 0 is Kind.MediaSaved -> kind.timestamp > 0
} }
} }
@ -62,7 +62,7 @@ class DataExtractionNotification(): ControlMessage() {
is Kind.Screenshot -> dataExtractionNotification.type = SignalServiceProtos.DataExtractionNotification.Type.SCREENSHOT is Kind.Screenshot -> dataExtractionNotification.type = SignalServiceProtos.DataExtractionNotification.Type.SCREENSHOT
is Kind.MediaSaved -> { is Kind.MediaSaved -> {
dataExtractionNotification.type = SignalServiceProtos.DataExtractionNotification.Type.MEDIA_SAVED dataExtractionNotification.type = SignalServiceProtos.DataExtractionNotification.Type.MEDIA_SAVED
dataExtractionNotification.timestamp = kind.timestanp dataExtractionNotification.timestamp = kind.timestamp
} }
} }
val contentProto = SignalServiceProtos.Content.newBuilder() val contentProto = SignalServiceProtos.Content.newBuilder()
@ -73,5 +73,4 @@ class DataExtractionNotification(): ControlMessage() {
return null return null
} }
} }
} }

View File

@ -3,10 +3,11 @@ package org.session.libsession.messaging.messages.signal;
import org.session.libsession.messaging.messages.visible.VisibleMessage; import org.session.libsession.messaging.messages.visible.VisibleMessage;
import org.session.libsession.messaging.sending_receiving.attachments.Attachment; import org.session.libsession.messaging.sending_receiving.attachments.Attachment;
import org.session.libsession.messaging.sending_receiving.attachments.PointerAttachment; import org.session.libsession.messaging.sending_receiving.attachments.PointerAttachment;
import org.session.libsession.messaging.sending_receiving.linkpreview.LinkPreview; import org.session.libsession.messaging.sending_receiving.dataextraction.DataExtractionNotificationInfoMessage;
import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel;
import org.session.libsession.messaging.sending_receiving.sharecontacts.Contact; import org.session.libsession.messaging.sending_receiving.sharecontacts.Contact;
import org.session.libsession.messaging.threads.Address; import org.session.libsession.messaging.threads.Address;
import org.session.libsession.messaging.sending_receiving.linkpreview.LinkPreview;
import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel;
import org.session.libsession.utilities.GroupUtil; import org.session.libsession.utilities.GroupUtil;
import org.session.libsignal.libsignal.util.guava.Optional; import org.session.libsignal.libsignal.util.guava.Optional;
import org.session.libsignal.service.api.messages.SignalServiceAttachment; import org.session.libsignal.service.api.messages.SignalServiceAttachment;
@ -26,9 +27,11 @@ public class IncomingMediaMessage {
private final int subscriptionId; private final int subscriptionId;
private final long expiresIn; private final long expiresIn;
private final boolean expirationUpdate; private final boolean expirationUpdate;
private final QuoteModel quote;
private final boolean unidentified; private final boolean unidentified;
private final DataExtractionNotificationInfoMessage dataExtractionNotification;
private final QuoteModel quote;
private final List<Attachment> attachments = new LinkedList<>(); private final List<Attachment> attachments = new LinkedList<>();
private final List<Contact> sharedContacts = new LinkedList<>(); private final List<Contact> sharedContacts = new LinkedList<>();
private final List<LinkPreview> linkPreviews = new LinkedList<>(); private final List<LinkPreview> linkPreviews = new LinkedList<>();
@ -44,7 +47,8 @@ public class IncomingMediaMessage {
Optional<List<SignalServiceAttachment>> attachments, Optional<List<SignalServiceAttachment>> attachments,
Optional<QuoteModel> quote, Optional<QuoteModel> quote,
Optional<List<Contact>> sharedContacts, Optional<List<Contact>> sharedContacts,
Optional<List<LinkPreview>> linkPreviews) Optional<List<LinkPreview>> linkPreviews,
Optional<DataExtractionNotificationInfoMessage> dataExtractionNotification)
{ {
this.push = true; this.push = true;
this.from = from; this.from = from;
@ -53,6 +57,7 @@ public class IncomingMediaMessage {
this.subscriptionId = subscriptionId; this.subscriptionId = subscriptionId;
this.expiresIn = expiresIn; this.expiresIn = expiresIn;
this.expirationUpdate = expirationUpdate; this.expirationUpdate = expirationUpdate;
this.dataExtractionNotification = dataExtractionNotification.orNull();
this.quote = quote.orNull(); this.quote = quote.orNull();
this.unidentified = unidentified; this.unidentified = unidentified;
@ -73,7 +78,7 @@ public class IncomingMediaMessage {
Optional<List<LinkPreview>> linkPreviews) Optional<List<LinkPreview>> linkPreviews)
{ {
return new IncomingMediaMessage(from, message.getSentTimestamp(), -1, expiresIn, false, return new IncomingMediaMessage(from, message.getSentTimestamp(), -1, expiresIn, false,
false, Optional.fromNullable(message.getText()), group, Optional.fromNullable(attachments), quote, Optional.absent(), linkPreviews); false, Optional.fromNullable(message.getText()), group, Optional.fromNullable(attachments), quote, Optional.absent(), linkPreviews, Optional.absent());
} }
public int getSubscriptionId() { public int getSubscriptionId() {
@ -116,6 +121,20 @@ public class IncomingMediaMessage {
return groupId != null; return groupId != null;
} }
public boolean isScreenshotDataExtraction() {
if (dataExtractionNotification == null) return false;
else {
return dataExtractionNotification.getKind() == DataExtractionNotificationInfoMessage.Kind.SCREENSHOT;
}
}
public boolean isMediaSavedDataExtraction() {
if (dataExtractionNotification == null) return false;
else {
return dataExtractionNotification.getKind() == DataExtractionNotificationInfoMessage.Kind.MEDIA_SAVED;
}
}
public QuoteModel getQuote() { public QuoteModel getQuote() {
return quote; return quote;
} }

View File

@ -9,6 +9,7 @@ import org.session.libsession.messaging.messages.control.*
import org.session.libsession.messaging.messages.visible.Attachment import org.session.libsession.messaging.messages.visible.Attachment
import org.session.libsession.messaging.messages.visible.VisibleMessage import org.session.libsession.messaging.messages.visible.VisibleMessage
import org.session.libsession.messaging.sending_receiving.attachments.PointerAttachment import org.session.libsession.messaging.sending_receiving.attachments.PointerAttachment
import org.session.libsession.messaging.sending_receiving.dataextraction.DataExtractionNotificationInfoMessage
import org.session.libsession.messaging.sending_receiving.linkpreview.LinkPreview import org.session.libsession.messaging.sending_receiving.linkpreview.LinkPreview
import org.session.libsession.messaging.sending_receiving.notifications.PushNotificationAPI import org.session.libsession.messaging.sending_receiving.notifications.PushNotificationAPI
import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel
@ -45,6 +46,7 @@ fun MessageReceiver.handle(message: Message, proto: SignalServiceProtos.Content,
is TypingIndicator -> handleTypingIndicator(message) is TypingIndicator -> handleTypingIndicator(message)
is ClosedGroupControlMessage -> handleClosedGroupControlMessage(message) is ClosedGroupControlMessage -> handleClosedGroupControlMessage(message)
is ExpirationTimerUpdate -> handleExpirationTimerUpdate(message) is ExpirationTimerUpdate -> handleExpirationTimerUpdate(message)
is DataExtractionNotification -> handleDataExtractionNotification(message)
is ConfigurationMessage -> handleConfigurationMessage(message) is ConfigurationMessage -> handleConfigurationMessage(message)
is VisibleMessage -> handleVisibleMessage(message, proto, openGroupID) is VisibleMessage -> handleVisibleMessage(message, proto, openGroupID)
} }
@ -91,6 +93,24 @@ private fun MessageReceiver.handleExpirationTimerUpdate(message: ExpirationTimer
} }
} }
// Data Extraction Notification handling
private fun MessageReceiver.handleDataExtractionNotification(message: DataExtractionNotification) {
// we don't handle data extraction messages for groups (they shouldn't be sent, but in case we filter them here too)
if (message.groupPublicKey != null) return
val storage = MessagingConfiguration.shared.storage
val senderPublicKey = message.sender!!
val notification: DataExtractionNotificationInfoMessage = when(message.kind) {
is DataExtractionNotification.Kind.Screenshot -> DataExtractionNotificationInfoMessage(DataExtractionNotificationInfoMessage.Kind.SCREENSHOT)
is DataExtractionNotification.Kind.MediaSaved -> DataExtractionNotificationInfoMessage(DataExtractionNotificationInfoMessage.Kind.MEDIA_SAVED)
else -> return
}
storage.insertDataExtractionNotificationMessage(senderPublicKey, notification, message.sentTimestamp!!)
}
// Configuration message handling
private fun MessageReceiver.handleConfigurationMessage(message: ConfigurationMessage) { private fun MessageReceiver.handleConfigurationMessage(message: ConfigurationMessage) {
val context = MessagingConfiguration.shared.context val context = MessagingConfiguration.shared.context
val storage = MessagingConfiguration.shared.storage val storage = MessagingConfiguration.shared.storage
@ -153,7 +173,8 @@ fun MessageReceiver.handleVisibleMessage(message: VisibleMessage, proto: SignalS
} }
} }
// Get or create thread // Get or create thread
val threadID = storage.getOrCreateThreadIdFor(message.syncTarget ?: message.sender!!, message.groupPublicKey, openGroupID) val threadID = storage.getOrCreateThreadIdFor(message.syncTarget
?: message.sender!!, message.groupPublicKey, openGroupID)
// Parse quote if needed // Parse quote if needed
var quoteModel: QuoteModel? = null var quoteModel: QuoteModel? = null
if (message.quote != null && proto.dataMessage.hasQuote()) { if (message.quote != null && proto.dataMessage.hasQuote()) {

View File

@ -0,0 +1,16 @@
package org.session.libsession.messaging.sending_receiving.dataextraction
class DataExtractionNotificationInfoMessage {
enum class Kind {
SCREENSHOT,
MEDIA_SAVED
}
var kind: Kind? = null
constructor(kind: Kind?) {
this.kind = kind
}
}

View File

@ -3,6 +3,7 @@ package org.session.libsession.messaging.utilities
import android.content.Context import android.content.Context
import org.session.libsession.R import org.session.libsession.R
import org.session.libsession.messaging.MessagingConfiguration import org.session.libsession.messaging.MessagingConfiguration
import org.session.libsession.messaging.sending_receiving.dataextraction.DataExtractionNotificationInfoMessage
import org.session.libsession.utilities.ExpirationUtil import org.session.libsession.utilities.ExpirationUtil
import org.session.libsignal.service.api.messages.SignalServiceGroup import org.session.libsignal.service.api.messages.SignalServiceGroup
@ -78,7 +79,7 @@ object UpdateMessageBuilder {
if (!isOutgoing && sender == null) return "" if (!isOutgoing && sender == null) return ""
val senderName: String? = if (!isOutgoing) { val senderName: String? = if (!isOutgoing) {
MessagingConfiguration.shared.storage.getDisplayNameForRecipient(sender!!) ?: sender MessagingConfiguration.shared.storage.getDisplayNameForRecipient(sender!!) ?: sender
} else { sender } } else { context.getString(R.string.MessageRecord_you) }
return if (duration <= 0) { return if (duration <= 0) {
if (isOutgoing) context.getString(R.string.MessageRecord_you_disabled_disappearing_messages) if (isOutgoing) context.getString(R.string.MessageRecord_you_disabled_disappearing_messages)
else context.getString(R.string.MessageRecord_s_disabled_disappearing_messages, senderName) else context.getString(R.string.MessageRecord_s_disabled_disappearing_messages, senderName)
@ -89,8 +90,13 @@ object UpdateMessageBuilder {
} }
} }
//TODO do this when the current update is merged fun buildDataExtractionMessage(context: Context, kind: DataExtractionNotificationInfoMessage.Kind, sender: String? = null): String {
fun buildDataExtractionMessage(): String { val senderName = MessagingConfiguration.shared.storage.getDisplayNameForRecipient(sender!!) ?: sender
return "" return when (kind) {
DataExtractionNotificationInfoMessage.Kind.SCREENSHOT ->
context.getString(R.string.MessageRecord_s_took_a_screenshot, senderName)
DataExtractionNotificationInfoMessage.Kind.MEDIA_SAVED ->
context.getString(R.string.MessageRecord_media_saved_by_s, senderName)
}
} }
} }

View File

@ -509,6 +509,8 @@
<string name="MessageRecord_s_disabled_disappearing_messages">%1$s disabled disappearing messages.</string> <string name="MessageRecord_s_disabled_disappearing_messages">%1$s disabled disappearing messages.</string>
<string name="MessageRecord_you_set_disappearing_message_time_to_s">You set the disappearing message timer to %1$s</string> <string name="MessageRecord_you_set_disappearing_message_time_to_s">You set the disappearing message timer to %1$s</string>
<string name="MessageRecord_s_set_disappearing_message_time_to_s">%1$s set the disappearing message timer to %2$s</string> <string name="MessageRecord_s_set_disappearing_message_time_to_s">%1$s set the disappearing message timer to %2$s</string>
<string name="MessageRecord_s_took_a_screenshot">%1$s took a screenshot.</string>
<string name="MessageRecord_media_saved_by_s">Media saved by %1$s.</string>
<string name="MessageRecord_your_safety_number_with_s_has_changed">Your safety number with %s has changed.</string> <string name="MessageRecord_your_safety_number_with_s_has_changed">Your safety number with %s has changed.</string>
<string name="MessageRecord_you_marked_your_safety_number_with_s_verified">You marked your safety number with %s verified</string> <string name="MessageRecord_you_marked_your_safety_number_with_s_verified">You marked your safety number with %s verified</string>
<string name="MessageRecord_you_marked_your_safety_number_with_s_verified_from_another_device">You marked your safety number with %s verified from another device</string> <string name="MessageRecord_you_marked_your_safety_number_with_s_verified_from_another_device">You marked your safety number with %s verified from another device</string>