diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/InputPanel.java b/app/src/main/java/org/thoughtcrime/securesms/components/InputPanel.java index 53245b6704..2a18665770 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/InputPanel.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/InputPanel.java @@ -154,10 +154,9 @@ public class InputPanel extends LinearLayout long id, @NonNull Recipient author, @NonNull String body, - @NonNull SlideDeck attachments, - boolean isViewOnce) + @NonNull SlideDeck attachments) { - this.quoteView.setQuote(glideRequests, id, author, body, false, attachments, isViewOnce); + this.quoteView.setQuote(glideRequests, id, author, body, false, attachments); this.quoteView.setVisibility(View.VISIBLE); if (this.linkPreview.getVisibility() == View.VISIBLE) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/QuoteView.java b/app/src/main/java/org/thoughtcrime/securesms/components/QuoteView.java index 13dae6b9df..3ebf699474 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/QuoteView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/QuoteView.java @@ -29,6 +29,7 @@ import org.thoughtcrime.securesms.mms.SlideDeck; import org.thoughtcrime.securesms.recipients.LiveRecipient; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientForeverObserver; +import org.thoughtcrime.securesms.util.MediaUtil; import org.thoughtcrime.securesms.util.ThemeUtil; import org.thoughtcrime.securesms.util.Util; @@ -149,8 +150,7 @@ public class QuoteView extends FrameLayout implements RecipientForeverObserver { @NonNull Recipient author, @Nullable String body, boolean originalMissing, - @NonNull SlideDeck attachments, - boolean isViewOnce) + @NonNull SlideDeck attachments) { if (this.author != null) this.author.removeForeverObserver(this); @@ -161,7 +161,7 @@ public class QuoteView extends FrameLayout implements RecipientForeverObserver { this.author.observeForever(this); setQuoteAuthor(author); - setQuoteText(body, attachments, isViewOnce); + setQuoteText(body, attachments); setQuoteAttachment(glideRequests, attachments); setQuoteMissingFooter(originalMissing); } @@ -197,7 +197,7 @@ public class QuoteView extends FrameLayout implements RecipientForeverObserver { mainView.setBackgroundColor(author.getColor().toQuoteBackgroundColor(getContext(), outgoing)); } - private void setQuoteText(@Nullable String body, @NonNull SlideDeck attachments, boolean isViewOnce) { + private void setQuoteText(@Nullable String body, @NonNull SlideDeck attachments) { if (!TextUtils.isEmpty(body) || !attachments.containsMediaSlide()) { bodyView.setVisibility(VISIBLE); bodyView.setText(body == null ? "" : body); @@ -213,9 +213,10 @@ public class QuoteView extends FrameLayout implements RecipientForeverObserver { List imageSlides = Stream.of(attachments.getSlides()).filter(Slide::hasImage).limit(1).toList(); List videoSlides = Stream.of(attachments.getSlides()).filter(Slide::hasVideo).limit(1).toList(); List stickerSlides = Stream.of(attachments.getSlides()).filter(Slide::hasSticker).limit(1).toList(); + List viewOnceSlides = Stream.of(attachments.getSlides()).filter(Slide::hasViewOnce).limit(1).toList(); // Given that most types have images, we specifically check images last - if (isViewOnce) { + if (!viewOnceSlides.isEmpty()) { mediaDescriptionText.setText(R.string.QuoteView_media); } else if (!audioSlides.isEmpty()) { mediaDescriptionText.setText(R.string.QuoteView_audio); @@ -233,10 +234,14 @@ public class QuoteView extends FrameLayout implements RecipientForeverObserver { private void setQuoteAttachment(@NonNull GlideRequests glideRequests, @NonNull SlideDeck slideDeck) { List imageVideoSlides = Stream.of(slideDeck.getSlides()).filter(s -> s.hasImage() || s.hasVideo() || s.hasSticker()).limit(1).toList(); List documentSlides = Stream.of(attachments.getSlides()).filter(Slide::hasDocument).limit(1).toList(); + List viewOnceSlides = Stream.of(attachments.getSlides()).filter(Slide::hasViewOnce).limit(1).toList(); attachmentVideoOverlayView.setVisibility(GONE); - if (!imageVideoSlides.isEmpty() && imageVideoSlides.get(0).getThumbnailUri() != null) { + if (!viewOnceSlides.isEmpty()) { + thumbnailView.setVisibility(GONE); + attachmentContainerView.setVisibility(GONE); + } else if (!imageVideoSlides.isEmpty() && imageVideoSlides.get(0).getThumbnailUri() != null) { thumbnailView.setVisibility(VISIBLE); attachmentContainerView.setVisibility(GONE); dismissView.setBackgroundResource(R.drawable.dismiss_background); diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java index 194d86d9f0..6a43c3fc68 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java @@ -2836,8 +2836,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity messageRecord.getDateSent(), author, body, - slideDeck, - messageRecord.isViewOnce()); + slideDeck); } else if (messageRecord.isMms() && !((MmsMessageRecord) messageRecord).getLinkPreviews().isEmpty()) { LinkPreview linkPreview = ((MmsMessageRecord) messageRecord).getLinkPreviews().get(0); @@ -2851,8 +2850,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity messageRecord.getDateSent(), author, messageRecord.getBody(), - slideDeck, - messageRecord.isViewOnce()); + slideDeck); } else { SlideDeck slideDeck = messageRecord.isMms() ? ((MmsMessageRecord) messageRecord).getSlideDeck() : new SlideDeck(); @@ -2866,8 +2864,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity messageRecord.getDateSent(), author, messageRecord.getBody(), - slideDeck, - messageRecord.isViewOnce()); + slideDeck); } inputPanel.clickOnComposeInput(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java index f88ecb6bcf..7889590994 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java @@ -1077,7 +1077,7 @@ public class ConversationFragment extends Fragment .withMimeType(thumbnailSlide.getContentType()) .createForSingleSessionOnDisk(requireContext()); - DatabaseFactory.getAttachmentDatabase(requireContext()).deleteAttachmentFilesForMessage(messageRecord.getId()); + DatabaseFactory.getAttachmentDatabase(requireContext()).deleteAttachmentFilesForViewOnceMessage(messageRecord.getId()); ApplicationContext.getInstance(requireContext()) .getViewOnceMessageManager() @@ -1095,7 +1095,7 @@ public class ConversationFragment extends Fragment } else { Log.w(TAG, "Failed to open view-once photo. Showing a toast and deleting the attachments for the message just in case."); Toast.makeText(requireContext(), R.string.ConversationFragment_failed_to_open_message, Toast.LENGTH_SHORT).show(); - SignalExecutors.BOUNDED.execute(() -> DatabaseFactory.getAttachmentDatabase(requireContext()).deleteAttachmentFilesForMessage(messageRecord.getId())); + SignalExecutors.BOUNDED.execute(() -> DatabaseFactory.getAttachmentDatabase(requireContext()).deleteAttachmentFilesForViewOnceMessage(messageRecord.getId())); } }); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java index 0ddae1f90e..3093ff1c36 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java @@ -855,7 +855,7 @@ public class ConversationItem extends LinearLayout implements BindableConversati if (current.isMms() && !current.isMmsNotification() && ((MediaMmsMessageRecord)current).getQuote() != null) { Quote quote = ((MediaMmsMessageRecord)current).getQuote(); //noinspection ConstantConditions - quoteView.setQuote(glideRequests, quote.getId(), Recipient.live(quote.getAuthor()).get(), quote.getText(), quote.isOriginalMissing(), quote.getAttachment(), messageRecord.isViewOnce()); + quoteView.setQuote(glideRequests, quote.getId(), Recipient.live(quote.getAuthor()).get(), quote.getText(), quote.isOriginalMissing(), quote.getAttachment()); quoteView.setVisibility(View.VISIBLE); quoteView.getLayoutParams().width = ViewGroup.LayoutParams.WRAP_CONTENT; diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentDatabase.java index e5df073c34..73cbaf0bb7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentDatabase.java @@ -320,8 +320,8 @@ public class AttachmentDatabase extends Database { notifyAttachmentListeners(); } - public void deleteAttachmentFilesForMessage(long mmsId) { - Log.d(TAG, "[deleteAttachmentFilesForMessage] mmsId: " + mmsId); + public void deleteAttachmentFilesForViewOnceMessage(long mmsId) { + Log.d(TAG, "[deleteAttachmentFilesForViewOnceMessage] mmsId: " + mmsId); SQLiteDatabase database = databaseHelper.getWritableDatabase(); Cursor cursor = null; @@ -355,6 +355,7 @@ public class AttachmentDatabase extends Database { values.put(HEIGHT, 0); values.put(TRANSFER_STATE, TRANSFER_PROGRESS_DONE); values.put(BLUR_HASH, (String) null); + values.put(CONTENT_TYPE, MediaUtil.VIEW_ONCE); database.update(TABLE_NAME, values, MMS_ID + " = ?", new String[] {mmsId + ""}); notifyAttachmentListeners(); @@ -365,7 +366,6 @@ public class AttachmentDatabase extends Database { } } - public void deleteAttachment(@NonNull AttachmentId id) { Log.d(TAG, "[deleteAttachment] attachmentId: " + id); diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/ThreadRecord.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/ThreadRecord.java index b586dc31de..07a49d5c92 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/ThreadRecord.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/ThreadRecord.java @@ -145,7 +145,9 @@ public class ThreadRecord extends DisplayRecord { } private String getViewOnceDescription(@NonNull Context context, @Nullable String contentType) { - if (MediaUtil.isVideoType(contentType)) { + if (MediaUtil.isViewOnceType(contentType)) { + return context.getString(R.string.ThreadRecord_disappearing_media); + } else if (MediaUtil.isVideoType(contentType)) { return context.getString(R.string.ThreadRecord_disappearing_video); } else { return context.getString(R.string.ThreadRecord_disappearing_photo); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java index ba5c54ead6..da157974b1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java @@ -12,7 +12,6 @@ import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.attachments.Attachment; import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.GroupDatabase; import org.thoughtcrime.securesms.database.GroupReceiptDatabase.GroupReceiptInfo; import org.thoughtcrime.securesms.database.MmsDatabase; import org.thoughtcrime.securesms.database.NoSuchMessageException; @@ -203,7 +202,7 @@ public class PushGroupSendJob extends PushSendJob { } if (message.isViewOnce()) { - DatabaseFactory.getAttachmentDatabase(context).deleteAttachmentFilesForMessage(messageId); + DatabaseFactory.getAttachmentDatabase(context).deleteAttachmentFilesForViewOnceMessage(messageId); } } else if (!networkFailures.isEmpty()) { throw new RetryLaterException(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java index ea15ed963a..72b0c8591b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java @@ -150,7 +150,7 @@ public class PushMediaSendJob extends PushSendJob { } if (message.isViewOnce()) { - DatabaseFactory.getAttachmentDatabase(context).deleteAttachmentFilesForMessage(messageId); + DatabaseFactory.getAttachmentDatabase(context).deleteAttachmentFilesForViewOnceMessage(messageId); } log(TAG, "Sent message: " + messageId); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushProcessMessageJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushProcessMessageJob.java index ef303a1852..b73e3f5817 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushProcessMessageJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushProcessMessageJob.java @@ -656,7 +656,7 @@ public final class PushProcessMessageJob extends BaseJob { handleReaction(content, message.getMessage()); threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(getSyncMessageDestination(message)); threadId = threadId != -1 ? threadId : null; - } else if (message.getMessage().getAttachments().isPresent() || message.getMessage().getQuote().isPresent() || message.getMessage().getPreviews().isPresent() || message.getMessage().getSticker().isPresent()) { + } else if (message.getMessage().getAttachments().isPresent() || message.getMessage().getQuote().isPresent() || message.getMessage().getPreviews().isPresent() || message.getMessage().getSticker().isPresent() || message.getMessage().isViewOnce()) { threadId = handleSynchronizeSentMediaMessage(message); } else { threadId = handleSynchronizeSentTextMessage(message); @@ -742,7 +742,7 @@ public final class PushProcessMessageJob extends BaseJob { MessageRecord record = DatabaseFactory.getMmsSmsDatabase(context).getMessageFor(timestamp, author); if (record != null && record.isMms()) { - DatabaseFactory.getAttachmentDatabase(context).deleteAttachmentFilesForMessage(record.getId()); + DatabaseFactory.getAttachmentDatabase(context).deleteAttachmentFilesForViewOnceMessage(record.getId()); } MessageNotifier.setLastDesktopActivityTimestamp(envelopeTimestamp); @@ -842,7 +842,8 @@ public final class PushProcessMessageJob extends BaseJob { Optional> sharedContacts = getContacts(message.getMessage().getSharedContacts()); Optional> previews = getLinkPreviews(message.getMessage().getPreviews(), message.getMessage().getBody().or("")); boolean viewOnce = message.getMessage().isViewOnce(); - List syncAttachments = viewOnce ? Collections.emptyList() : PointerAttachment.forPointers(message.getMessage().getAttachments()); + List syncAttachments = viewOnce ? Collections.singletonList(new TombstoneAttachment(MediaUtil.VIEW_ONCE, false)) + : PointerAttachment.forPointers(message.getMessage().getAttachments()); if (sticker.isPresent()) { syncAttachments.add(sticker.get()); @@ -1289,7 +1290,9 @@ public final class PushProcessMessageJob extends BaseJob { if (message.isMms()) { MmsMessageRecord mmsMessage = (MmsMessageRecord) message; - if (!mmsMessage.isViewOnce()) { + if (mmsMessage.isViewOnce()) { + attachments.add(new TombstoneAttachment(MediaUtil.VIEW_ONCE, true)); + } else { attachments = mmsMessage.getSlideDeck().asAttachments(); if (attachments.isEmpty()) { @@ -1298,8 +1301,6 @@ public final class PushProcessMessageJob extends BaseJob { .map(lp -> lp.getThumbnail().get()) .toList()); } - } else if (quote.get().getAttachments().size() > 0) { - attachments.add(new TombstoneAttachment(quote.get().getAttachments().get(0).getContentType(), true)); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushSendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushSendJob.java index 4a777819ff..50184bb831 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushSendJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushSendJob.java @@ -217,12 +217,15 @@ public abstract class PushSendJob extends SendJob { protected Optional getQuoteFor(OutgoingMediaMessage message) { if (message.getOutgoingQuote() == null) return Optional.absent(); - long quoteId = message.getOutgoingQuote().getId(); - String quoteBody = message.getOutgoingQuote().getText(); - RecipientId quoteAuthor = message.getOutgoingQuote().getAuthor(); - List quoteAttachments = new LinkedList<>(); + long quoteId = message.getOutgoingQuote().getId(); + String quoteBody = message.getOutgoingQuote().getText(); + RecipientId quoteAuthor = message.getOutgoingQuote().getAuthor(); + List quoteAttachments = new LinkedList<>(); + List filteredAttachments = Stream.of(message.getOutgoingQuote().getAttachments()) + .filterNot(a -> MediaUtil.isViewOnceType(a.getContentType())) + .toList(); - for (Attachment attachment : message.getOutgoingQuote().getAttachments()) { + for (Attachment attachment : filteredAttachments) { BitmapUtil.ScaleResult thumbnailData = null; SignalServiceAttachment thumbnail = null; String thumbnailType = MediaUtil.IMAGE_JPEG; diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/Slide.java b/app/src/main/java/org/thoughtcrime/securesms/mms/Slide.java index 6789a0a6de..5febeece34 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mms/Slide.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/Slide.java @@ -105,6 +105,10 @@ public abstract class Slide { return false; } + public boolean hasViewOnce() { + return false; + } + public @NonNull String getContentDescription() { return ""; } public @NonNull Attachment asAttachment() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/SlideDeck.java b/app/src/main/java/org/thoughtcrime/securesms/mms/SlideDeck.java index 8d654df472..aabca8a1f1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mms/SlideDeck.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/SlideDeck.java @@ -88,7 +88,7 @@ public class SlideDeck { public boolean containsMediaSlide() { for (Slide slide : slides) { - if (slide.hasImage() || slide.hasVideo() || slide.hasAudio() || slide.hasDocument() || slide.hasSticker()) { + if (slide.hasImage() || slide.hasVideo() || slide.hasAudio() || slide.hasDocument() || slide.hasSticker() || slide.hasViewOnce()) { return true; } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/ViewOnceSlide.java b/app/src/main/java/org/thoughtcrime/securesms/mms/ViewOnceSlide.java new file mode 100644 index 0000000000..3d25c11c92 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/ViewOnceSlide.java @@ -0,0 +1,28 @@ +package org.thoughtcrime.securesms.mms; + + +import android.content.Context; +import android.net.Uri; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import org.thoughtcrime.securesms.attachments.Attachment; +import org.thoughtcrime.securesms.util.MediaUtil; + +/** + * Slide used for attachments with contentType {@link MediaUtil#VIEW_ONCE}. + * Attachments will only get this type *after* they've been viewed, or if they were synced from a + * linked device. Incoming unviewed messages will have the appropriate image/video contentType. + */ +public class ViewOnceSlide extends Slide { + + public ViewOnceSlide(@NonNull Context context, @NonNull Attachment attachment) { + super(context, attachment); + } + + @Override + public boolean hasViewOnce() { + return true; + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/revealable/ViewOnceMessageManager.java b/app/src/main/java/org/thoughtcrime/securesms/revealable/ViewOnceMessageManager.java index 7e9550e31e..e5df5f57ab 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/revealable/ViewOnceMessageManager.java +++ b/app/src/main/java/org/thoughtcrime/securesms/revealable/ViewOnceMessageManager.java @@ -54,7 +54,7 @@ public class ViewOnceMessageManager extends TimedEventManager= 23) { return true; @@ -373,6 +382,7 @@ public class MediaUtil { AUDIO, MMS, LONG_TEXT, + VIEW_ONCE, DOCUMENT } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 07ddf4f4a3..3b2d313d39 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -819,6 +819,7 @@ Sticker Disappearing photo Disappearing video + Disappearing media %s is on Signal! Disappearing messages disabled Disappearing message time set to %s