From 1752972be936052989332b71510da8309fef6ba4 Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Thu, 2 Jul 2020 12:37:58 -0400 Subject: [PATCH] Update delete for everyone functionality to match requirements. --- .../conversation/ConversationFragment.java | 31 ++++++++++++++----- .../conversation/ConversationItem.java | 16 ++++++---- .../ConversationListItem.java | 2 +- .../securesms/keyvalue/UiHints.java | 11 ++++++- .../securesms/recipients/Recipient.java | 6 ++++ .../securesms/util/RemoteDeleteUtil.java | 20 +++++++----- app/src/main/res/values/attrs.xml | 1 + app/src/main/res/values/strings.xml | 3 ++ app/src/main/res/values/themes.xml | 2 ++ 9 files changed, 70 insertions(+), 22 deletions(-) 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 39f5f3906a..69b69ec96d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java @@ -81,6 +81,7 @@ import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.groups.GroupId; import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob; import org.thoughtcrime.securesms.jobs.MultiDeviceViewOnceOpenJob; +import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.linkpreview.LinkPreview; import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.longmessage.LongMessageActivity; @@ -694,19 +695,35 @@ public class ConversationFragment extends LoggingFragment { }); if (RemoteDeleteUtil.isValidSend(messageRecords, System.currentTimeMillis())) { - builder.setNeutralButton(R.string.ConversationFragment_delete_for_everyone, (dialog, which) -> { - SignalExecutors.BOUNDED.execute(() -> { - for (MessageRecord message : messageRecords) { - MessageSender.sendRemoteDelete(context, message.getId(), message.isMms()); - } - }); - }); + builder.setNeutralButton(R.string.ConversationFragment_delete_for_everyone, (dialog, which) -> handleDeleteForEveryone(messageRecords)); } builder.setNegativeButton(android.R.string.cancel, null); return builder; } + private void handleDeleteForEveryone(Set messageRecords) { + Runnable deleteForEveryone = () -> { + SignalExecutors.BOUNDED.execute(() -> { + for (MessageRecord message : messageRecords) { + MessageSender.sendRemoteDelete(ApplicationDependencies.getApplication(), message.getId(), message.isMms()); + } + }); + }; + + if (SignalStore.uiHints().hasConfirmedDeleteForEveryoneOnce()) { + deleteForEveryone.run(); + } else { + new AlertDialog.Builder(requireActivity()) + .setMessage(R.string.ConversationFragment_this_message_will_be_permanently_deleted_for_everyone) + .setPositiveButton(R.string.ConversationFragment_delete_for_everyone, (dialog, which) -> { + SignalStore.uiHints().markHasConfirmedDeleteForEveryoneOnce(); + deleteForEveryone.run(); + }) + .setNegativeButton(android.R.string.cancel, null) + .show(); + } + } private void handleDisplayDetails(MessageRecord message) { startActivity(MessageDetailsActivity.getIntentForMessageDetails(requireContext(), message, recipient.getId(), threadId)); 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 d74e08be4a..6e454e3d5e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java @@ -36,7 +36,6 @@ import android.text.style.BackgroundColorSpan; import android.text.style.CharacterStyle; import android.text.style.ClickableSpan; import android.text.style.ForegroundColorSpan; -import android.text.style.RelativeSizeSpan; import android.text.style.StyleSpan; import android.text.style.URLSpan; import android.text.util.Linkify; @@ -389,11 +388,11 @@ public class ConversationItem extends LinearLayout implements BindableConversati /// MessageRecord Attribute Parsers private void setBubbleState(MessageRecord messageRecord) { - if (messageRecord.isOutgoing()) { + if (messageRecord.isOutgoing() && !messageRecord.isRemoteDelete()) { bodyBubble.getBackground().setColorFilter(defaultBubbleColor, PorterDuff.Mode.MULTIPLY); footer.setTextColor(ThemeUtil.getThemedColor(context, R.attr.conversation_item_sent_text_secondary_color)); footer.setIconColor(ThemeUtil.getThemedColor(context, R.attr.conversation_item_sent_icon_color)); - } else if (isViewOnceMessage(messageRecord) && ViewOnceUtil.isViewed((MmsMessageRecord) messageRecord)) { + } else if (messageRecord.isRemoteDelete() || (isViewOnceMessage(messageRecord) && ViewOnceUtil.isViewed((MmsMessageRecord) messageRecord))) { bodyBubble.getBackground().setColorFilter(ThemeUtil.getThemedColor(context, R.attr.conversation_item_reveal_viewed_background_color), PorterDuff.Mode.MULTIPLY); footer.setTextColor(ThemeUtil.getThemedColor(context, R.attr.conversation_item_sent_text_secondary_color)); footer.setIconColor(ThemeUtil.getThemedColor(context, R.attr.conversation_item_sent_icon_color)); @@ -456,7 +455,8 @@ public class ConversationItem extends LinearLayout implements BindableConversati } private boolean shouldDrawBodyBubbleOutline(MessageRecord messageRecord) { - return !messageRecord.isOutgoing() && isViewOnceMessage(messageRecord) && ViewOnceUtil.isViewed((MmsMessageRecord) messageRecord); + boolean isIncomingViewedOnce = !messageRecord.isOutgoing() && isViewOnceMessage(messageRecord) && ViewOnceUtil.isViewed((MmsMessageRecord) messageRecord); + return isIncomingViewedOnce || messageRecord.isRemoteDelete(); } private boolean isCaptionlessMms(MessageRecord messageRecord) { @@ -529,12 +529,16 @@ public class ConversationItem extends LinearLayout implements BindableConversati bodyText.setMovementMethod(LongClickMovementMethod.getInstance(getContext())); if (messageRecord.isRemoteDelete()) { - String deletedMessage = context.getString(R.string.ConversationItem_this_message_was_deleted); + String deletedMessage = context.getString(messageRecord.isOutgoing() ? R.string.ConversationItem_you_deleted_this_message : R.string.ConversationItem_this_message_was_deleted); SpannableString italics = new SpannableString(deletedMessage); - italics.setSpan(new RelativeSizeSpan(0.9f), 0, deletedMessage.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); italics.setSpan(new StyleSpan(android.graphics.Typeface.ITALIC), 0, deletedMessage.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + italics.setSpan(new ForegroundColorSpan(ThemeUtil.getThemedColor(context, R.attr.conversation_item_delete_for_everyone_text_color)), + 0, + deletedMessage.length(), + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); bodyText.setText(italics); + bodyText.setVisibility(View.VISIBLE); } else if (isCaptionlessMms(messageRecord)) { bodyText.setVisibility(View.GONE); } else { diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListItem.java b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListItem.java index e51c90b11d..4aeaa03617 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListItem.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListItem.java @@ -453,7 +453,7 @@ public class ConversationListItem extends RelativeLayout if (extra != null && extra.isViewOnce()) { return new SpannableString(emphasisAdded(getViewOnceDescription(context, thread.getContentType()))); } else if (extra != null && extra.isRemoteDelete()) { - return new SpannableString(emphasisAdded(context.getString(R.string.ThreadRecord_this_message_was_deleted))); + return new SpannableString(emphasisAdded(context.getString(thread.isOutgoing() ? R.string.ThreadRecord_you_deleted_this_message : R.string.ThreadRecord_this_message_was_deleted))); } else { return new SpannableString(Util.emptyIfNull(thread.getBody())); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/UiHints.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/UiHints.java index ab4414c166..f476934516 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/UiHints.java +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/UiHints.java @@ -4,7 +4,8 @@ import androidx.annotation.NonNull; public class UiHints extends SignalStoreValues { - private static final String HAS_SEEN_GROUP_SETTINGS_MENU_TOAST = "uihints.has_seen_group_settings_menu_toast"; + private static final String HAS_SEEN_GROUP_SETTINGS_MENU_TOAST = "uihints.has_seen_group_settings_menu_toast"; + private static final String HAS_CONFIRMED_DELETE_FOR_EVERYONE_ONCE = "uihints.has_confirmed_delete_for_everyone_once"; UiHints(@NonNull KeyValueStore store) { super(store); @@ -22,4 +23,12 @@ public class UiHints extends SignalStoreValues { public boolean hasSeenGroupSettingsMenuToast() { return getBoolean(HAS_SEEN_GROUP_SETTINGS_MENU_TOAST, false); } + + public void markHasConfirmedDeleteForEveryoneOnce() { + putBoolean(HAS_CONFIRMED_DELETE_FOR_EVERYONE_ONCE, true); + } + + public boolean hasConfirmedDeleteForEveryoneOnce() { + return getBoolean(HAS_CONFIRMED_DELETE_FOR_EVERYONE_ONCE, false); + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.java b/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.java index a9446cf181..e4ddd80573 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.java +++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.java @@ -10,6 +10,8 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.WorkerThread; +import com.annimon.stream.Stream; + import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.color.MaterialColor; import org.thoughtcrime.securesms.contacts.avatars.ContactColors; @@ -633,6 +635,10 @@ public class Recipient { return groupId != null && groupId.isV2(); } + public boolean isActiveGroup() { + return Stream.of(getParticipants()).anyMatch(Recipient::isLocalNumber); + } + public @NonNull List getParticipants() { return new ArrayList<>(participants); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/RemoteDeleteUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/RemoteDeleteUtil.java index 41b8ced9a1..db7ac63a4f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/RemoteDeleteUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/RemoteDeleteUtil.java @@ -13,7 +13,7 @@ import java.util.concurrent.TimeUnit; public final class RemoteDeleteUtil { private static final long RECEIVE_THRESHOLD = TimeUnit.DAYS.toMillis(1); - private static final long SEND_THRESHOLD = TimeUnit.MINUTES.toMillis(30); + private static final long SEND_THRESHOLD = TimeUnit.HOURS.toMillis(3); private RemoteDeleteUtil() {} @@ -21,17 +21,23 @@ public final class RemoteDeleteUtil { boolean isValidSender = (deleteSender.isLocalNumber() && targetMessage.isOutgoing()) || (!deleteSender.isLocalNumber() && !targetMessage.isOutgoing()); - return isValidSender && + return isValidSender && targetMessage.getIndividualRecipient().equals(deleteSender) && (deleteServerTimestamp - targetMessage.getServerTimestamp()) < RECEIVE_THRESHOLD; } public static boolean isValidSend(@NonNull Collection targetMessages, long currentTime) { // TODO [greyson] [remote-delete] Update with server timestamp when available for outgoing messages - return Stream.of(targetMessages) - .allMatch(message -> message.isOutgoing() && - !message.isRemoteDelete() && - !message.isPending() && - (currentTime - message.getDateSent()) < SEND_THRESHOLD); + return Stream.of(targetMessages).allMatch(message -> isValidSend(message, currentTime)); + } + + private static boolean isValidSend(MessageRecord message, long currentTime) { + return message.isOutgoing() && + message.isPush() && + (!message.getRecipient().isGroup() || message.getRecipient().isActiveGroup()) && + !message.getRecipient().isLocalNumber() && + !message.isRemoteDelete() && + !message.isPending() && + (currentTime - message.getDateSent()) < SEND_THRESHOLD; } } diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index 7d316a9305..f171b379e2 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -138,6 +138,7 @@ + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 346ea37ebb..07b4bd9af3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -204,6 +204,7 @@   Download More   Pending This message was deleted. + You deleted this message. Reset secure session? @@ -312,6 +313,7 @@ Deleting messages… Delete for me Delete for everyone + This message will be permanently deleted for everyone in the conversation. Members will be able to see that you deleted a message. Original message not found Original message no longer available Failed to open message @@ -1162,6 +1164,7 @@ View-once video View-once media This message was deleted. + You deleted this message. %s is on Signal! Disappearing messages disabled Disappearing message time set to %s diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 980438d5b7..3a9e495c1d 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -319,6 +319,7 @@ @color/core_grey_60 @color/transparent_black_20 ?conversation_background + @color/core_grey_90 @drawable/scroll_to_bottom_background_light @color/grey_600 @@ -545,6 +546,7 @@ @color/core_grey_25 @color/transparent_white_20 ?conversation_background + @color/core_grey_15 @color/core_grey_75 @color/core_grey_05