From c09dbfa47c50b2d316705b49d759eb492af4813b Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Thu, 27 Aug 2020 10:52:47 -0400 Subject: [PATCH] Prevent corner-case where link previews were generated for SMS. Also added some hardening to make sure that it's impossible for any link previews to be fetched if the setting is disabled (this was already the case in practice, we just have some assertions in there now). Fixes #9956 --- .../conversation/ConversationActivity.java | 3 +- .../linkpreview/LinkPreviewViewModel.java | 35 +++++++++++++++---- 2 files changed, 30 insertions(+), 8 deletions(-) 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 fcedbdba5..02771d15d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java @@ -1840,6 +1840,7 @@ public class ConversationActivity extends PassphraseRequiredActivity sendButton.addOnTransportChangedListener((newTransport, manuallySelected) -> { calculateCharactersRemaining(); updateLinkPreviewState(); + linkPreviewViewModel.onTransportChanged(newTransport.isSms()); composeText.setTransport(newTransport); buttonToggle.getBackground().setColorFilter(newTransport.getBackgroundColor(), PorterDuff.Mode.MULTIPLY); @@ -2692,7 +2693,7 @@ public class ConversationActivity extends PassphraseRequiredActivity } private void updateLinkPreviewState() { - if (SignalStore.settings().isLinkPreviewsEnabled() && !sendButton.getSelectedTransport().isSms() && !attachmentManager.isAttachmentPresent()) { + if (SignalStore.settings().isLinkPreviewsEnabled() && isSecureText && !sendButton.getSelectedTransport().isSms() && !attachmentManager.isAttachmentPresent()) { linkPreviewViewModel.onEnabled(); linkPreviewViewModel.onTextChanged(this, composeText.getTextTrimmed().toString(), composeText.getSelectionStart(), composeText.getSelectionEnd()); } else { diff --git a/app/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreviewViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreviewViewModel.java index fe34eebd1..5ec5f0b77 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreviewViewModel.java +++ b/app/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreviewViewModel.java @@ -7,9 +7,11 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; +import androidx.lifecycle.Transformations; import androidx.lifecycle.ViewModel; import androidx.lifecycle.ViewModelProvider; +import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.net.RequestController; import org.thoughtcrime.securesms.util.Debouncer; import org.thoughtcrime.securesms.util.Util; @@ -23,32 +25,36 @@ public class LinkPreviewViewModel extends ViewModel { private final LinkPreviewRepository repository; private final MutableLiveData linkPreviewState; + private final LiveData linkPreviewSafeState; private String activeUrl; private RequestController activeRequest; private boolean userCanceled; private Debouncer debouncer; + private boolean enabled; private LinkPreviewViewModel(@NonNull LinkPreviewRepository repository) { - this.repository = repository; - this.linkPreviewState = new MutableLiveData<>(); - this.debouncer = new Debouncer(250); + this.repository = repository; + this.linkPreviewState = new MutableLiveData<>(); + this.debouncer = new Debouncer(250); + this.enabled = SignalStore.settings().isLinkPreviewsEnabled(); + this.linkPreviewSafeState = Transformations.map(linkPreviewState, state -> enabled ? state : LinkPreviewState.forNoLinks()); } public LiveData getLinkPreviewState() { - return linkPreviewState; + return linkPreviewSafeState; } public boolean hasLinkPreview() { - return linkPreviewState.getValue() != null && linkPreviewState.getValue().getLinkPreview().isPresent(); + return linkPreviewSafeState.getValue() != null && linkPreviewSafeState.getValue().getLinkPreview().isPresent(); } public boolean hasLinkPreviewUi() { - return linkPreviewState.getValue() != null && linkPreviewState.getValue().hasContent(); + return linkPreviewSafeState.getValue() != null && linkPreviewSafeState.getValue().hasContent(); } public @NonNull List getActiveLinkPreviews() { - final LinkPreviewState state = linkPreviewState.getValue(); + final LinkPreviewState state = linkPreviewSafeState.getValue(); if (state == null || !state.getLinkPreview().isPresent()) { return Collections.emptyList(); @@ -58,6 +64,8 @@ public class LinkPreviewViewModel extends ViewModel { } public void onTextChanged(@NonNull Context context, @NonNull String text, int cursorStart, int cursorEnd) { + if (!enabled) return; + debouncer.publish(() -> { if (TextUtils.isEmpty(text)) { userCanceled = false; @@ -129,6 +137,14 @@ public class LinkPreviewViewModel extends ViewModel { linkPreviewState.setValue(LinkPreviewState.forNoLinks()); } + public void onTransportChanged(boolean isSms) { + enabled = !isSms; + + if (isSms) { + onUserCancel(); + } + } + public void onSend() { if (activeRequest != null) { activeRequest.cancel(); @@ -143,7 +159,12 @@ public class LinkPreviewViewModel extends ViewModel { } public void onEnabled() { + if (!SignalStore.settings().isLinkPreviewsEnabled()) { + throw new AssertionError(); + } + userCanceled = false; + enabled = true; } @Override