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 16c46c8835..ddbddb219a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/InputPanel.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/InputPanel.java @@ -236,6 +236,11 @@ public class InputPanel extends LinearLayout this.linkPreview.setLoading(); } + public void setLinkPreviewNoPreview() { + this.linkPreview.setVisibility(View.VISIBLE); + this.linkPreview.setNoPreview(); + } + public void setLinkPreview(@NonNull GlideRequests glideRequests, @NonNull Optional preview) { if (preview.isPresent()) { this.linkPreview.setVisibility(View.VISIBLE); diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/LinkPreviewView.java b/app/src/main/java/org/thoughtcrime/securesms/components/LinkPreviewView.java index c21cba8a57..441e20b7cd 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/LinkPreviewView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/LinkPreviewView.java @@ -21,6 +21,9 @@ import org.thoughtcrime.securesms.util.ThemeUtil; import okhttp3.HttpUrl; +/** + * The view shown in the compose box that represents the state of the link preview. + */ public class LinkPreviewView extends FrameLayout { private static final int TYPE_CONVERSATION = 0; @@ -33,6 +36,7 @@ public class LinkPreviewView extends FrameLayout { private View divider; private View closeButton; private View spinner; + private View noPreview; private int type; private int defaultRadius; @@ -60,6 +64,7 @@ public class LinkPreviewView extends FrameLayout { divider = findViewById(R.id.linkpreview_divider); spinner = findViewById(R.id.linkpreview_progress_wheel); closeButton = findViewById(R.id.linkpreview_close); + noPreview = findViewById(R.id.linkpreview_no_preview); defaultRadius = getResources().getDimensionPixelSize(R.dimen.thumbnail_default_radius); cornerMask = new CornerMask(this); outliner = new Outliner(); @@ -102,6 +107,15 @@ public class LinkPreviewView extends FrameLayout { site.setVisibility(GONE); thumbnail.setVisibility(GONE); spinner.setVisibility(VISIBLE); + noPreview.setVisibility(INVISIBLE); + } + + public void setNoPreview() { + title.setVisibility(GONE); + site.setVisibility(GONE); + thumbnail.setVisibility(GONE); + spinner.setVisibility(GONE); + noPreview.setVisibility(VISIBLE); } public void setLinkPreview(@NonNull GlideRequests glideRequests, @NonNull LinkPreview linkPreview, boolean showThumbnail) { @@ -109,6 +123,7 @@ public class LinkPreviewView extends FrameLayout { site.setVisibility(VISIBLE); thumbnail.setVisibility(VISIBLE); spinner.setVisibility(GONE); + noPreview.setVisibility(GONE); title.setText(linkPreview.getTitle()); 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 6c33ffa2bf..d9bfbbc5d7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java @@ -1820,7 +1820,7 @@ public class ConversationActivity extends PassphraseRequiredActivity private void initializeLinkPreviewObserver() { linkPreviewViewModel = ViewModelProviders.of(this, new LinkPreviewViewModel.Factory(new LinkPreviewRepository())).get(LinkPreviewViewModel.class); - if (!TextSecurePreferences.isLinkPreviewsEnabled(this)) { + if (!SignalStore.settings().isLinkPreviewsEnabled()) { linkPreviewViewModel.onUserCancel(); return; } @@ -1831,6 +1831,9 @@ public class ConversationActivity extends PassphraseRequiredActivity if (previewState.isLoading()) { Log.d(TAG, "Loading link preview."); inputPanel.setLinkPreviewLoading(); + } else if (previewState.hasLinks() && !previewState.getLinkPreview().isPresent()) { + Log.d(TAG, "No preview found."); + inputPanel.setLinkPreviewNoPreview(); } else { Log.d(TAG, "Setting link preview: " + previewState.getLinkPreview().isPresent()); inputPanel.setLinkPreview(glideRequests, previewState.getLinkPreview()); @@ -2591,7 +2594,7 @@ public class ConversationActivity extends PassphraseRequiredActivity buttonToggle.display(sendButton); quickAttachmentToggle.hide(); - if (!attachmentManager.isAttachmentPresent() && !linkPreviewViewModel.hasLinkPreview()) { + if (!attachmentManager.isAttachmentPresent() && !linkPreviewViewModel.hasLinkPreviewUi()) { inlineAttachmentToggle.show(); } else { inlineAttachmentToggle.hide(); @@ -2600,7 +2603,7 @@ public class ConversationActivity extends PassphraseRequiredActivity } private void updateLinkPreviewState() { - if (TextSecurePreferences.isLinkPreviewsEnabled(this) && !sendButton.getSelectedTransport().isSms() && !attachmentManager.isAttachmentPresent()) { + if (SignalStore.settings().isLinkPreviewsEnabled() && !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/jobs/PushProcessMessageJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushProcessMessageJob.java index fa9d8e21c6..e5187fa9f0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushProcessMessageJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushProcessMessageJob.java @@ -58,6 +58,7 @@ import org.thoughtcrime.securesms.jobmanager.Data; import org.thoughtcrime.securesms.jobmanager.Job; import org.thoughtcrime.securesms.jobmanager.JobManager; import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; +import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.linkpreview.Link; import org.thoughtcrime.securesms.linkpreview.LinkPreview; import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil; @@ -830,7 +831,7 @@ public final class PushProcessMessageJob extends BaseJob { } if (configurationMessage.getLinkPreviews().isPresent()) { - TextSecurePreferences.setLinkPreviewsEnabled(context, configurationMessage.getReadReceipts().get()); + SignalStore.settings().setLinkPreviewsEnabled(configurationMessage.getReadReceipts().get()); } } @@ -966,7 +967,7 @@ public final class PushProcessMessageJob extends BaseJob { ApplicationDependencies.getJobManager().add(new MultiDeviceConfigurationUpdateJob(TextSecurePreferences.isReadReceiptsEnabled(context), TextSecurePreferences.isTypingIndicatorsEnabled(context), TextSecurePreferences.isShowUnidentifiedDeliveryIndicatorsEnabled(context), - TextSecurePreferences.isLinkPreviewsEnabled(context))); + SignalStore.settings().isLinkPreviewsEnabled())); ApplicationDependencies.getJobManager().add(new MultiDeviceStickerPackSyncJob()); } @@ -1692,7 +1693,7 @@ public final class PushProcessMessageJob extends BaseJob { Optional url = Optional.fromNullable(preview.getUrl()); Optional title = Optional.fromNullable(preview.getTitle()); boolean hasContent = !TextUtils.isEmpty(title.or("")) || thumbnail.isPresent(); - boolean presentInBody = url.isPresent() && Stream.of(LinkPreviewUtil.findWhitelistedUrls(message)).map(Link::getUrl).collect(Collectors.toSet()).contains(url.get()); + boolean presentInBody = url.isPresent() && Stream.of(LinkPreviewUtil.findValidPreviewUrls(message)).map(Link::getUrl).collect(Collectors.toSet()).contains(url.get()); boolean validDomain = url.isPresent() && LinkPreviewUtil.isValidPreviewUrl(url.get()); if (hasContent && presentInBody && validDomain) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SettingsValues.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SettingsValues.java new file mode 100644 index 0000000000..f7cd610414 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SettingsValues.java @@ -0,0 +1,27 @@ +package org.thoughtcrime.securesms.keyvalue; + +import androidx.annotation.NonNull; + +public final class SettingsValues extends SignalStoreValues { + + public static final String LINK_PREVIEWS = "settings.link_previews"; + + SettingsValues(@NonNull KeyValueStore store) { + super(store); + } + + @Override + void onFirstEverAppLaunch() { + getStore().beginWrite() + .putBoolean(LINK_PREVIEWS, true) + .apply(); + } + + public boolean isLinkPreviewsEnabled() { + return getBoolean(LINK_PREVIEWS, false); + } + + public void setLinkPreviewsEnabled(boolean enabled) { + putBoolean(LINK_PREVIEWS, enabled); + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SignalStore.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SignalStore.java index 0d7be52f78..a948b78a35 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SignalStore.java +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SignalStore.java @@ -24,6 +24,7 @@ public final class SignalStore { private final MiscellaneousValues misc; private final InternalValues internalValues; private final EmojiValues emojiValues; + private final SettingsValues settingsValues; private SignalStore() { this.store = ApplicationDependencies.getKeyValueStore(); @@ -37,6 +38,7 @@ public final class SignalStore { this.misc = new MiscellaneousValues(store); this.internalValues = new InternalValues(store); this.emojiValues = new EmojiValues(store); + this.settingsValues = new SettingsValues(store); } public static void onFirstEverAppLaunch() { @@ -49,6 +51,7 @@ public final class SignalStore { tooltips().onFirstEverAppLaunch(); misc().onFirstEverAppLaunch(); internalValues().onFirstEverAppLaunch(); + settings().onFirstEverAppLaunch(); } public static @NonNull KbsValues kbsValues() { @@ -91,6 +94,10 @@ public final class SignalStore { return INSTANCE.emojiValues; } + public static @NonNull SettingsValues settings() { + return INSTANCE.settingsValues; + } + public static @NonNull GroupsV2AuthorizationSignalStoreCache groupsV2AuthorizationCache() { return new GroupsV2AuthorizationSignalStoreCache(getStore()); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreviewUtil.java b/app/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreviewUtil.java index ff2cef061f..b1fb95ccbc 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreviewUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreviewUtil.java @@ -40,7 +40,7 @@ public final class LinkPreviewUtil { /** * @return All whitelisted URLs in the source text. */ - public static @NonNull List findWhitelistedUrls(@NonNull String text) { + public static @NonNull List findValidPreviewUrls(@NonNull String text) { SpannableString spannable = new SpannableString(text); boolean found = Linkify.addLinks(spannable, Linkify.WEB_URLS); 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 163d5e73be..4fbaec51aa 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreviewViewModel.java +++ b/app/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreviewViewModel.java @@ -41,6 +41,10 @@ public class LinkPreviewViewModel extends ViewModel { return linkPreviewState.getValue() != null && linkPreviewState.getValue().getLinkPreview().isPresent(); } + public boolean hasLinkPreviewUi() { + return linkPreviewState.getValue() != null && linkPreviewState.getValue().hasContent(); + } + public @NonNull List getActiveLinkPreviews() { final LinkPreviewState state = linkPreviewState.getValue(); @@ -61,7 +65,7 @@ public class LinkPreviewViewModel extends ViewModel { return; } - List links = LinkPreviewUtil.findWhitelistedUrls(text); + List links = LinkPreviewUtil.findValidPreviewUrls(text); Optional link = links.isEmpty() ? Optional.absent() : Optional.of(links.get(0)); if (link.isPresent() && link.get().getUrl().equals(activeUrl)) { @@ -75,7 +79,7 @@ public class LinkPreviewViewModel extends ViewModel { if (!link.isPresent() || !isCursorPositionValid(text, link.get(), cursorStart, cursorEnd)) { activeUrl = null; - linkPreviewState.setValue(LinkPreviewState.forEmpty()); + linkPreviewState.setValue(LinkPreviewState.forNoLinks()); return; } @@ -85,7 +89,11 @@ public class LinkPreviewViewModel extends ViewModel { activeRequest = repository.getLinkPreview(context, link.get().getUrl(), lp -> { Util.runOnMain(() -> { if (!userCanceled) { - linkPreviewState.setValue(LinkPreviewState.forPreview(lp)); + if (lp.isPresent()) { + linkPreviewState.setValue(LinkPreviewState.forPreview(lp.get())); + } else { + linkPreviewState.setValue(LinkPreviewState.forLinksWithNoPreview()); + } } activeRequest = null; }); @@ -103,7 +111,7 @@ public class LinkPreviewViewModel extends ViewModel { activeUrl = null; debouncer.clear(); - linkPreviewState.setValue(LinkPreviewState.forEmpty()); + linkPreviewState.setValue(LinkPreviewState.forNoLinks()); } public void onEnabled() { @@ -133,32 +141,46 @@ public class LinkPreviewViewModel extends ViewModel { public static class LinkPreviewState { private final boolean isLoading; + private final boolean hasLinks; private final Optional linkPreview; - private LinkPreviewState(boolean isLoading, Optional linkPreview) { + private LinkPreviewState(boolean isLoading, boolean hasLinks, Optional linkPreview) { this.isLoading = isLoading; + this.hasLinks = hasLinks; this.linkPreview = linkPreview; } private static LinkPreviewState forLoading() { - return new LinkPreviewState(true, Optional.absent()); + return new LinkPreviewState(true, false, Optional.absent()); } - private static LinkPreviewState forPreview(@NonNull Optional linkPreview) { - return new LinkPreviewState(false, linkPreview); + private static LinkPreviewState forPreview(@NonNull LinkPreview linkPreview) { + return new LinkPreviewState(false, true, Optional.of(linkPreview)); } - private static LinkPreviewState forEmpty() { - return new LinkPreviewState(false, Optional.absent()); + private static LinkPreviewState forLinksWithNoPreview() { + return new LinkPreviewState(false, true, Optional.absent()); + } + + private static LinkPreviewState forNoLinks() { + return new LinkPreviewState(false, false, Optional.absent()); } public boolean isLoading() { return isLoading; } + public boolean hasLinks() { + return hasLinks; + } + public Optional getLinkPreview() { return linkPreview; } + + boolean hasContent() { + return isLoading || hasLinks; + } } public static class Factory extends ViewModelProvider.NewInstanceFactory { diff --git a/app/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreviewsMegaphoneView.java b/app/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreviewsMegaphoneView.java new file mode 100644 index 0000000000..5842323ef9 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreviewsMegaphoneView.java @@ -0,0 +1,48 @@ +package org.thoughtcrime.securesms.linkpreview; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.widget.FrameLayout; + +import androidx.annotation.NonNull; + +import org.thoughtcrime.securesms.R; +import org.thoughtcrime.securesms.keyvalue.SignalStore; +import org.thoughtcrime.securesms.megaphone.Megaphone; +import org.thoughtcrime.securesms.megaphone.MegaphoneActionController; + +public class LinkPreviewsMegaphoneView extends FrameLayout { + + private View yesButton; + private View noButton; + + public LinkPreviewsMegaphoneView(Context context) { + super(context); + initialize(context); + } + + public LinkPreviewsMegaphoneView(Context context, AttributeSet attrs) { + super(context, attrs); + initialize(context); + } + + private void initialize(@NonNull Context context) { + inflate(context, R.layout.link_previews_megaphone, this); + + this.yesButton = findViewById(R.id.linkpreview_megaphone_ok); + this.noButton = findViewById(R.id.linkpreview_megaphone_disable); + } + + public void present(@NonNull Megaphone megaphone, @NonNull MegaphoneActionController listener) { + this.yesButton.setOnClickListener(v -> { + SignalStore.settings().setLinkPreviewsEnabled(true); + listener.onMegaphoneCompleted(megaphone.getEvent()); + }); + + this.noButton.setOnClickListener(v -> { + SignalStore.settings().setLinkPreviewsEnabled(false); + listener.onMegaphoneCompleted(megaphone.getEvent()); + }); + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/megaphone/Megaphone.java b/app/src/main/java/org/thoughtcrime/securesms/megaphone/Megaphone.java index cca7e590df..1b6094229a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/megaphone/Megaphone.java +++ b/app/src/main/java/org/thoughtcrime/securesms/megaphone/Megaphone.java @@ -172,6 +172,9 @@ public class Megaphone { /** Specialized style for announcing reactions. */ REACTIONS, + /** Specialized style for announcing link previews. */ + LINK_PREVIEWS, + /** Basic bottom of the screen megaphone with optional snooze and action buttons. */ BASIC, diff --git a/app/src/main/java/org/thoughtcrime/securesms/megaphone/MegaphoneViewBuilder.java b/app/src/main/java/org/thoughtcrime/securesms/megaphone/MegaphoneViewBuilder.java index 77d0fb9d5d..9f6cba6889 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/megaphone/MegaphoneViewBuilder.java +++ b/app/src/main/java/org/thoughtcrime/securesms/megaphone/MegaphoneViewBuilder.java @@ -6,6 +6,7 @@ import android.view.View; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import org.thoughtcrime.securesms.linkpreview.LinkPreviewsMegaphoneView; import org.thoughtcrime.securesms.reactions.ReactionsMegaphoneView; public class MegaphoneViewBuilder { @@ -21,6 +22,8 @@ public class MegaphoneViewBuilder { return null; case REACTIONS: return buildReactionsMegaphone(context, megaphone, listener); + case LINK_PREVIEWS: + return buildLinkPreviewsMegaphone(context, megaphone, listener); case POPUP: return buildPopupMegaphone(context, megaphone, listener); default: @@ -46,6 +49,15 @@ public class MegaphoneViewBuilder { return view; } + private static @NonNull View buildLinkPreviewsMegaphone(@NonNull Context context, + @NonNull Megaphone megaphone, + @NonNull MegaphoneActionController listener) + { + LinkPreviewsMegaphoneView view = new LinkPreviewsMegaphoneView(context); + view.present(megaphone, listener); + return view; + } + private static @NonNull View buildPopupMegaphone(@NonNull Context context, @NonNull Megaphone megaphone, @NonNull MegaphoneActionController listener) diff --git a/app/src/main/java/org/thoughtcrime/securesms/megaphone/Megaphones.java b/app/src/main/java/org/thoughtcrime/securesms/megaphone/Megaphones.java index 3ff569a06b..d2c7655fb6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/megaphone/Megaphones.java +++ b/app/src/main/java/org/thoughtcrime/securesms/megaphone/Megaphones.java @@ -91,6 +91,7 @@ public final class Megaphones { put(Event.PIN_REMINDER, new SignalPinReminderSchedule()); put(Event.MESSAGE_REQUESTS, shouldShowMessageRequestsMegaphone() ? ALWAYS : NEVER); put(Event.MENTIONS, shouldShowMentionsMegaphone() ? ALWAYS : NEVER); + put(Event.LINK_PREVIEWS, SignalStore.settings().isLinkPreviewsEnabled() ? NEVER : ALWAYS); }}; } @@ -106,6 +107,8 @@ public final class Megaphones { return buildMessageRequestsMegaphone(context); case MENTIONS: return buildMentionsMegaphone(); + case LINK_PREVIEWS: + return buildLinkPreviewsMegaphone(); default: throw new IllegalArgumentException("Event not handled!"); } @@ -196,6 +199,12 @@ public final class Megaphones { .build(); } + private static @NonNull Megaphone buildLinkPreviewsMegaphone() { + return new Megaphone.Builder(Event.LINK_PREVIEWS, Megaphone.Style.LINK_PREVIEWS) + .setMandatory(true) + .build(); + } + private static boolean shouldShowMessageRequestsMegaphone() { return Recipient.self().getProfileName() == ProfileName.EMPTY; } @@ -209,7 +218,8 @@ public final class Megaphones { PINS_FOR_ALL("pins_for_all"), PIN_REMINDER("pin_reminder"), MESSAGE_REQUESTS("message_requests"), - MENTIONS("mentions"); + MENTIONS("mentions"), + LINK_PREVIEWS("link_previews"); private final String key; diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/AppProtectionPreferenceFragment.java b/app/src/main/java/org/thoughtcrime/securesms/preferences/AppProtectionPreferenceFragment.java index 6bb68c486e..7d5954ada0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/preferences/AppProtectionPreferenceFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/AppProtectionPreferenceFragment.java @@ -38,12 +38,15 @@ import org.thoughtcrime.securesms.jobs.MultiDeviceConfigurationUpdateJob; import org.thoughtcrime.securesms.jobs.RefreshAttributesJob; import org.thoughtcrime.securesms.keyvalue.KbsValues; import org.thoughtcrime.securesms.keyvalue.PinValues; +import org.thoughtcrime.securesms.keyvalue.SettingsValues; import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.lock.PinHashing; import org.thoughtcrime.securesms.lock.v2.CreateKbsPinActivity; import org.thoughtcrime.securesms.lock.v2.KbsConstants; import org.thoughtcrime.securesms.lock.v2.RegistrationLockUtil; import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.megaphone.MegaphoneRepository; +import org.thoughtcrime.securesms.megaphone.Megaphones; import org.thoughtcrime.securesms.pin.RegistrationLockV2Dialog; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.service.KeyCachingService; @@ -90,13 +93,18 @@ public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment this.findPreference(TextSecurePreferences.PASSPHRASE_TIMEOUT_INTERVAL_PREF).setOnPreferenceClickListener(new PassphraseIntervalClickListener()); this.findPreference(TextSecurePreferences.READ_RECEIPTS_PREF).setOnPreferenceChangeListener(new ReadReceiptToggleListener()); this.findPreference(TextSecurePreferences.TYPING_INDICATORS).setOnPreferenceChangeListener(new TypingIndicatorsToggleListener()); - this.findPreference(TextSecurePreferences.LINK_PREVIEWS).setOnPreferenceChangeListener(new LinkPreviewToggleListener()); this.findPreference(PREFERENCE_CATEGORY_BLOCKED).setOnPreferenceClickListener(new BlockedContactsClickListener()); this.findPreference(TextSecurePreferences.SHOW_UNIDENTIFIED_DELIVERY_INDICATORS).setOnPreferenceChangeListener(new ShowUnidentifiedDeliveryIndicatorsChangedListener()); this.findPreference(TextSecurePreferences.UNIVERSAL_UNIDENTIFIED_ACCESS).setOnPreferenceChangeListener(new UniversalUnidentifiedAccessChangedListener()); this.findPreference(PREFERENCE_UNIDENTIFIED_LEARN_MORE).setOnPreferenceClickListener(new UnidentifiedLearnMoreClickListener()); disablePassphrase.setOnPreferenceChangeListener(new DisablePassphraseClickListener()); + + SwitchPreferenceCompat linkPreviewPref = (SwitchPreferenceCompat) this.findPreference(SettingsValues.LINK_PREVIEWS); + linkPreviewPref.setChecked(SignalStore.settings().isLinkPreviewsEnabled()); + linkPreviewPref.setPreferenceDataStore(SignalStore.getPreferenceDataStore()); + linkPreviewPref.setOnPreferenceChangeListener(new LinkPreviewToggleListener()); + initializeVisibility(); } @@ -241,7 +249,7 @@ public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment ApplicationDependencies.getJobManager().add(new MultiDeviceConfigurationUpdateJob(enabled, TextSecurePreferences.isTypingIndicatorsEnabled(requireContext()), TextSecurePreferences.isShowUnidentifiedDeliveryIndicatorsEnabled(getContext()), - TextSecurePreferences.isLinkPreviewsEnabled(getContext()))); + SignalStore.settings().isLinkPreviewsEnabled())); }); return true; @@ -258,7 +266,7 @@ public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment ApplicationDependencies.getJobManager().add(new MultiDeviceConfigurationUpdateJob(TextSecurePreferences.isReadReceiptsEnabled(requireContext()), enabled, TextSecurePreferences.isShowUnidentifiedDeliveryIndicatorsEnabled(getContext()), - TextSecurePreferences.isLinkPreviewsEnabled(getContext()))); + SignalStore.settings().isLinkPreviewsEnabled())); if (!enabled) { ApplicationContext.getInstance(requireContext()).getTypingStatusRepository().clear(); @@ -279,6 +287,9 @@ public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment TextSecurePreferences.isTypingIndicatorsEnabled(requireContext()), TextSecurePreferences.isShowUnidentifiedDeliveryIndicatorsEnabled(requireContext()), enabled)); + if (enabled) { + ApplicationDependencies.getMegaphoneRepository().markFinished(Megaphones.Event.LINK_PREVIEWS); + } }); return true; } @@ -383,7 +394,7 @@ public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment ApplicationDependencies.getJobManager().add(new MultiDeviceConfigurationUpdateJob(TextSecurePreferences.isReadReceiptsEnabled(getContext()), TextSecurePreferences.isTypingIndicatorsEnabled(getContext()), enabled, - TextSecurePreferences.isLinkPreviewsEnabled(getContext()))); + SignalStore.settings().isLinkPreviewsEnabled())); }); return true; diff --git a/app/src/main/java/org/thoughtcrime/securesms/storage/StorageSyncHelper.java b/app/src/main/java/org/thoughtcrime/securesms/storage/StorageSyncHelper.java index 1c9bab9937..1da0fb2412 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/storage/StorageSyncHelper.java +++ b/app/src/main/java/org/thoughtcrime/securesms/storage/StorageSyncHelper.java @@ -394,7 +394,7 @@ public final class StorageSyncHelper { .setTypingIndicatorsEnabled(TextSecurePreferences.isTypingIndicatorsEnabled(context)) .setReadReceiptsEnabled(TextSecurePreferences.isReadReceiptsEnabled(context)) .setSealedSenderIndicatorsEnabled(TextSecurePreferences.isShowUnidentifiedDeliveryIndicatorsEnabled(context)) - .setLinkPreviewsEnabled(TextSecurePreferences.isLinkPreviewsEnabled(context)) + .setLinkPreviewsEnabled(SignalStore.settings().isLinkPreviewsEnabled()) .build(); return SignalStorageRecord.forAccount(account); @@ -414,7 +414,7 @@ public final class StorageSyncHelper { TextSecurePreferences.setReadReceiptsEnabled(context, update.isReadReceiptsEnabled()); TextSecurePreferences.setTypingIndicatorsEnabled(context, update.isTypingIndicatorsEnabled()); TextSecurePreferences.setShowUnidentifiedDeliveryIndicatorsEnabled(context, update.isSealedSenderIndicatorsEnabled()); - TextSecurePreferences.setLinkPreviewsEnabled(context, update.isLinkPreviewsEnabled()); + SignalStore.settings().setLinkPreviewsEnabled(update.isLinkPreviewsEnabled()); if (fetchProfile && update.getAvatarUrlPath().isPresent()) { ApplicationDependencies.getJobManager().add(new RetrieveProfileAvatarJob(Recipient.self(), update.getAvatarUrlPath().get())); diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/TextSecurePreferences.java b/app/src/main/java/org/thoughtcrime/securesms/util/TextSecurePreferences.java index 73fd549b89..811f3de859 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/TextSecurePreferences.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/TextSecurePreferences.java @@ -419,10 +419,6 @@ public class TextSecurePreferences { setBooleanPreference(context, TYPING_INDICATORS, enabled); } - public static boolean isLinkPreviewsEnabled(Context context) { - return getBooleanPreference(context, LINK_PREVIEWS, true); - } - public static void setLinkPreviewsEnabled(Context context, boolean enabled) { setBooleanPreference(context, LINK_PREVIEWS, enabled); } diff --git a/app/src/main/res/drawable/ic_megaphone_link_previews.xml b/app/src/main/res/drawable/ic_megaphone_link_previews.xml new file mode 100644 index 0000000000..b40dd02faa --- /dev/null +++ b/app/src/main/res/drawable/ic_megaphone_link_previews.xml @@ -0,0 +1,27 @@ + + + + + + + + + diff --git a/app/src/main/res/layout/link_preview.xml b/app/src/main/res/layout/link_preview.xml index 69961de33a..01e2948456 100644 --- a/app/src/main/res/layout/link_preview.xml +++ b/app/src/main/res/layout/link_preview.xml @@ -84,18 +84,31 @@ + app:matProg_progressIndeterminate="true" + app:matProg_barWidth="3dp"/> + + diff --git a/app/src/main/res/layout/link_previews_megaphone.xml b/app/src/main/res/layout/link_previews_megaphone.xml new file mode 100644 index 0000000000..72c31c73d9 --- /dev/null +++ b/app/src/main/res/layout/link_previews_megaphone.xml @@ -0,0 +1,91 @@ + + + + + + + + + + + + + +