From 90e9a67be3d8679c30d7773ab606dc927edf777f Mon Sep 17 00:00:00 2001 From: Stephen Paul Weber Date: Mon, 28 Oct 2024 23:33:56 -0500 Subject: [PATCH] Initial message requests feature --- src/cheogram/res/values/strings.xml | 1 + .../conversations/entities/Conversation.java | 25 +++++++++ .../services/NotificationService.java | 8 +-- .../services/XmppConnectionService.java | 4 ++ .../ui/ConversationsActivity.java | 53 +++++++++++++++---- .../NotificationsSettingsFragment.java | 7 +++ .../xmpp/jingle/JingleConnectionManager.java | 11 ++-- src/main/res/values/arrays.xml | 12 +++++ src/main/res/values/defaults.xml | 1 + .../res/xml/preferences_notifications.xml | 15 +++--- 10 files changed, 107 insertions(+), 30 deletions(-) diff --git a/src/cheogram/res/values/strings.xml b/src/cheogram/res/values/strings.xml index 437854cd1978db8f9ec2fa6cccd79820ec659bc2..9a45eedbb05b5910c8c99c80bbc9486a3e3e3841 100644 --- a/src/cheogram/res/values/strings.xml +++ b/src/cheogram/res/values/strings.xml @@ -48,4 +48,5 @@ Block inviter Add Chat Received invite from stranger + Hide chats in Chat Requests area diff --git a/src/main/java/eu/siacs/conversations/entities/Conversation.java b/src/main/java/eu/siacs/conversations/entities/Conversation.java index ede29d066eeb5e525208e7a4fd55e9a0bd3ee692..223b28114e01635b0f88dc7ca88f6790ebb32ba1 100644 --- a/src/main/java/eu/siacs/conversations/entities/Conversation.java +++ b/src/main/java/eu/siacs/conversations/entities/Conversation.java @@ -218,6 +218,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl protected HashMap threads = new HashMap<>(); protected Multimap reactions = HashMultimap.create(); private String displayState = null; + protected boolean anyMatchSpam = false; public Conversation(final String name, final Account account, final Jid contactJid, final int mode) { @@ -1388,19 +1389,36 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl } } + public void checkSpam(Message... messages) { + if (anyMatchSpam) return; + + final var locale = java.util.Locale.getDefault(); + final var script = locale.getScript(); + for (final var m : messages) { + final var body = m.getRawBody(); + if (body.length() > 320 || (!"Cyrl".equals(script) && body.matches(".*\\p{IsCyrillic}.*")) || body.matches(".*(?:\\n.*\\n.*\\n|[Aa]\\s*d\\s*v\\s*v\\s*e\\s*r\\s*t|[Pp]romotion|[Dd][Dd][Oo][Ss]|[Ee]scrow|payout|seller|\\?OTR|write me when will be|[Pp]rii?vee?t|there online|bit\\.ly|goo\\.gl|tinyurl\\.com|tiny\\.cc|lc\\.chat|is\\.gd|soo\\.gd|s2r\\.co|clicky\\.me|budrul\\.com|bc\\.vc|uguu\\.se).*")) { + anyMatchSpam = true; + return; + } + } + } + public void add(Message message) { + checkSpam(message); synchronized (this.messages) { this.messages.add(message); } } public void prepend(int offset, Message message) { + checkSpam(message); synchronized (this.messages) { this.messages.add(Math.min(offset, this.messages.size()), message); } } public void addAll(int index, List messages) { + checkSpam(messages.toArray(new Message[0])); synchronized (this.messages) { this.messages.addAll(index, messages); } @@ -1494,6 +1512,13 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl return sentMessagesCount() > 0; } + public boolean isChatRequest(final String pref) { + if ("disable".equals(pref)) return false; + if ("strangers".equals(pref)) return isWithStranger(); + if (!isWithStranger() && !strangerInvited()) return false; + return anyMatchSpam; + } + public boolean isWithStranger() { final Contact contact = getContact(); return mode == MODE_SINGLE diff --git a/src/main/java/eu/siacs/conversations/services/NotificationService.java b/src/main/java/eu/siacs/conversations/services/NotificationService.java index d50d7a75879354266a14fef5dda42277b5d7f419..6c04eb93b341ceb4159eb10f33cb5fb9ee23bdcc 100644 --- a/src/main/java/eu/siacs/conversations/services/NotificationService.java +++ b/src/main/java/eu/siacs/conversations/services/NotificationService.java @@ -389,10 +389,11 @@ public class NotificationService { private boolean notifyMessage(final Message message) { final Conversation conversation = (Conversation) message.getConversation(); + final var chatRequestsPref = mXmppConnectionService.getStringPreference("chat_requests", R.string.default_chat_requests); return message.getStatus() == Message.STATUS_RECEIVED && !conversation.isMuted() && (conversation.alwaysNotify() || (wasHighlightedOrPrivate(message) || (conversation.notifyReplies() && wasReplyToMe(message)))) - && (!conversation.isWithStranger() || notificationsFromStrangers()) + && !conversation.isChatRequest(chatRequestsPref) && message.getType() != Message.TYPE_RTP_SESSION; } @@ -401,11 +402,6 @@ public class NotificationService { && message.getStatus() == Message.STATUS_RECEIVED; } - public boolean notificationsFromStrangers() { - return mXmppConnectionService.getBooleanPreference( - "notifications_from_strangers", R.bool.notifications_from_strangers); - } - private boolean isQuietHours(Account account) { return isQuietHours(mXmppConnectionService, account); } diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index ba8b4baf34c801418f8e439c3fa74a214578415a..3b2282af8ff1af8e4e2cff2dee65caa137407bdb 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -4985,6 +4985,10 @@ public class XmppConnectionService extends Service { return getPreferences().getBoolean(name, getResources().getBoolean(res)); } + public String getStringPreference(String name, @BoolRes int res) { + return getPreferences().getString(name, getResources().getString(res)); + } + public boolean confirmMessages() { return getBooleanPreference("confirm_messages", R.bool.confirm_messages); } diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationsActivity.java b/src/main/java/eu/siacs/conversations/ui/ConversationsActivity.java index 68b70fa891846d1b0bc6ed1505d7ece05e06e181..f9a57546566b6b26bde0d21c978afb15a212681a 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationsActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationsActivity.java @@ -152,13 +152,14 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio public static final long DRAWER_MANAGE_ACCOUNT = 4; public static final long DRAWER_MANAGE_PHONE_ACCOUNTS = 5; public static final long DRAWER_CHANNELS = 6; - public static final long DRAWER_SETTINGS = 7; - public static final long DRAWER_START_CHAT = 8; - public static final long DRAWER_START_CHAT_CONTACT = 9; - public static final long DRAWER_START_CHAT_NEW = 10; - public static final long DRAWER_START_CHAT_GROUP = 11; - public static final long DRAWER_START_CHAT_PUBLIC = 12; - public static final long DRAWER_START_CHAT_DISCOVER = 13; + public static final long DRAWER_CHAT_REQUESTS = 7; + public static final long DRAWER_SETTINGS = 8; + public static final long DRAWER_START_CHAT = 9; + public static final long DRAWER_START_CHAT_CONTACT = 10; + public static final long DRAWER_START_CHAT_NEW = 11; + public static final long DRAWER_START_CHAT_GROUP = 12; + public static final long DRAWER_START_CHAT_PUBLIC = 13; + public static final long DRAWER_START_CHAT_DISCOVER = 14; //secondary fragment (when holding the conversation, must be initialized before refreshing the overview fragment private static final @IdRes @@ -200,6 +201,7 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio if (accountHeader == null) return; + final var chatRequestsPref = xmppConnectionService.getStringPreference("chat_requests", R.string.default_chat_requests); final var accountUnreads = new HashMap(); binding.drawer.apply(dr -> { final var items = binding.drawer.getItemAdapter().getAdapterItems(); @@ -208,6 +210,7 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio var totalUnread = 0; var dmUnread = 0; var channelUnread = 0; + var chatRequests = 0; final var selectedAccount = selectedAccount(); populateWithOrderedConversations(conversations, false, false); for (final var c : conversations) { @@ -219,6 +222,7 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio } else { dmUnread += unread; } + if (c.isChatRequest(chatRequestsPref)) chatRequests++; } var accountUnread = accountUnreads.get(c.getAccount()); if (accountUnread == null) accountUnread = 0; @@ -255,6 +259,27 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio new com.mikepenz.materialdrawer.holder.StringHolder(channelUnread > 0 ? new Integer(channelUnread).toString() : null) ); + if (chatRequests > 0) { + if (binding.drawer.getItemAdapter().getAdapterPosition(DRAWER_CHAT_REQUESTS) < 0) { + final var color = MaterialColors.getColor(binding.drawer, com.google.android.material.R.attr.colorPrimaryContainer); + final var textColor = MaterialColors.getColor(binding.drawer, com.google.android.material.R.attr.colorOnPrimaryContainer); + final var requests = new com.mikepenz.materialdrawer.model.PrimaryDrawerItem(); + requests.setIdentifier(DRAWER_CHAT_REQUESTS); + com.mikepenz.materialdrawer.model.interfaces.NameableKt.setNameText(requests, "Chat Requests"); + com.mikepenz.materialdrawer.model.interfaces.IconableKt.setIconRes(requests, R.drawable.ic_person_add_24dp); + requests.setBadgeStyle(new com.mikepenz.materialdrawer.holder.BadgeStyle(com.mikepenz.materialdrawer.R.drawable.material_drawer_badge, color, color, textColor)); + binding.drawer.getItemAdapter().add(binding.drawer.getItemAdapter().getGlobalPosition(binding.drawer.getItemAdapter().getAdapterPosition(DRAWER_CHANNELS) + 1), requests); + } + com.mikepenz.materialdrawer.util.MaterialDrawerSliderViewExtensionsKt.updateBadge( + binding.drawer, + DRAWER_CHAT_REQUESTS, + new com.mikepenz.materialdrawer.holder.StringHolder(chatRequests > 0 ? new Integer(chatRequests).toString() : null) + ); + } else { + binding.drawer.getItemAdapter().removeByIdentifier(DRAWER_CHAT_REQUESTS); + } + + final var endOfMainFilters = chatRequests > 0 ? 6 : 5; long id = 1000; final var inDrawer = new HashMap(); for (final var item : ImmutableList.copyOf(items)) { @@ -283,11 +308,11 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio final var color = MaterialColors.getColor(binding.drawer, com.google.android.material.R.attr.colorPrimaryContainer); final var textColor = MaterialColors.getColor(binding.drawer, com.google.android.material.R.attr.colorOnPrimaryContainer); item.setBadgeStyle(new com.mikepenz.materialdrawer.holder.BadgeStyle(com.mikepenz.materialdrawer.R.drawable.material_drawer_badge, color, color, textColor)); - binding.drawer.getItemAdapter().add(binding.drawer.getItemAdapter().getGlobalPosition(5), item); + binding.drawer.getItemAdapter().add(binding.drawer.getItemAdapter().getGlobalPosition(endOfMainFilters), item); } } - items.subList(5, 5 + tags.size()).sort((x, y) -> x.getTag() == null ? -1 : ((Comparable) x.getTag()).compareTo(y.getTag())); + items.subList(endOfMainFilters, endOfMainFilters + tags.size()).sort((x, y) -> x.getTag() == null ? -1 : ((Comparable) x.getTag()).compareTo(y.getTag())); binding.drawer.getItemAdapter().getFastAdapter().notifyDataSetChanged(); return kotlin.Unit.INSTANCE; }); @@ -489,7 +514,7 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio launchStartConversation(R.id.create_public_channel); } else if (id == DRAWER_START_CHAT_DISCOVER) { launchStartConversation(R.id.discover_public_channels); - } else if (id == DRAWER_ALL_CHATS || id == DRAWER_UNREAD_CHATS || id == DRAWER_DIRECT_MESSAGES || id == DRAWER_CHANNELS) { + } else if (id == DRAWER_ALL_CHATS || id == DRAWER_UNREAD_CHATS || id == DRAWER_DIRECT_MESSAGES || id == DRAWER_CHANNELS || id == DRAWER_CHAT_REQUESTS) { selectedTag.clear(); mainFilter = id; binding.drawer.getSelectExtension().deselect(); @@ -516,7 +541,7 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio binding.drawer.setOnDrawerItemLongClickListener((v, drawerItem, pos) -> { final var id = drawerItem.getIdentifier(); - if (id == DRAWER_ALL_CHATS || id == DRAWER_UNREAD_CHATS || id == DRAWER_DIRECT_MESSAGES || id == DRAWER_CHANNELS) { + if (id == DRAWER_ALL_CHATS || id == DRAWER_UNREAD_CHATS || id == DRAWER_DIRECT_MESSAGES || id == DRAWER_CHANNELS || id == DRAWER_CHAT_REQUESTS) { selectedTag.clear(); mainFilter = id; binding.drawer.getSelectExtension().deselect(); @@ -639,6 +664,7 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio } protected void filterByMainFilter(List list) { + final var chatRequests = xmppConnectionService.getStringPreference("chat_requests", R.string.default_chat_requests); for (final var c : ImmutableList.copyOf(list)) { if (mainFilter == DRAWER_CHANNELS && c.getMode() != Conversation.MODE_MULTI) { list.remove(c); @@ -646,6 +672,11 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio list.remove(c); } else if (mainFilter == DRAWER_UNREAD_CHATS && c.unreadCount(xmppConnectionService) < 1) { list.remove(c); + } else if (mainFilter == DRAWER_CHAT_REQUESTS && !c.isChatRequest(chatRequests)) { + list.remove(c); + } + if (mainFilter != DRAWER_CHAT_REQUESTS && c.isChatRequest(chatRequests)) { + list.remove(c); } } } diff --git a/src/main/java/eu/siacs/conversations/ui/fragment/settings/NotificationsSettingsFragment.java b/src/main/java/eu/siacs/conversations/ui/fragment/settings/NotificationsSettingsFragment.java index 3af9a0c38c3c162db08be1649c0158dac55b95ca..4ba9ac8c41a517673d77f60024d248b84a9a472a 100644 --- a/src/main/java/eu/siacs/conversations/ui/fragment/settings/NotificationsSettingsFragment.java +++ b/src/main/java/eu/siacs/conversations/ui/fragment/settings/NotificationsSettingsFragment.java @@ -15,6 +15,7 @@ import androidx.activity.result.ActivityResultLauncher; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.preference.Preference; +import androidx.preference.ListPreference; import com.google.common.base.Optional; @@ -65,6 +66,7 @@ public class NotificationsSettingsFragment extends XmppPreferenceFragment { final var notificationHeadsUp = findPreference(AppSettings.NOTIFICATION_HEADS_UP); final var notificationVibrate = findPreference(AppSettings.NOTIFICATION_VIBRATE); final var notificationLed = findPreference(AppSettings.NOTIFICATION_LED); + final var chatRequests = (ListPreference) findPreference("chat_requests"); final var foregroundService = findPreference(AppSettings.KEEP_FOREGROUND_SERVICE); if (messageNotificationSettings == null || fullscreenNotification == null @@ -91,6 +93,11 @@ public class NotificationsSettingsFragment extends XmppPreferenceFragment { .canUseFullScreenIntent()) { fullscreenNotification.setVisible(false); } + + final var sharedPreferences = getPreferenceManager().getSharedPreferences(); + if (!sharedPreferences.getBoolean("notifications_from_strangers", true) && sharedPreferences.getString("chat_requests", null) == null) { + chatRequests.setValue("strangers"); + } } @Override diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java index 20ce6bdda935f219128b5903bfd934fd0ca659be..068c4654976135942eb7e8a090c44658adbebd5b 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java @@ -16,6 +16,7 @@ import com.google.common.collect.Collections2; import com.google.common.collect.ComparisonChain; import com.google.common.collect.ImmutableSet; +import eu.siacs.conversations.R; import eu.siacs.conversations.Config; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Contact; @@ -255,13 +256,9 @@ public class JingleConnectionManager extends AbstractConnectionManager { } private boolean isWithStrangerAndStrangerNotificationsAreOff(final Account account, Jid with) { - final boolean notifyForStrangers = - mXmppConnectionService.getNotificationService().notificationsFromStrangers(); - if (notifyForStrangers) { - return false; - } - final Contact contact = account.getRoster().getContact(with); - return !contact.showInContactList(); + final var chatRequestsPref = mXmppConnectionService.getStringPreference("chat_requests", R.string.default_chat_requests); + final var conversation = mXmppConnectionService.findOrCreateConversation(account, with, false, true); + return conversation.isChatRequest(chatRequestsPref); } ScheduledFuture schedule( diff --git a/src/main/res/values/arrays.xml b/src/main/res/values/arrays.xml index 486bb025f720404fadedd1bda7d7fcd622703031..06278a7e6fd533be7970542845f22ab5e7b3e678 100644 --- a/src/main/res/values/arrays.xml +++ b/src/main/res/values/arrays.xml @@ -106,6 +106,18 @@ @string/video_original + + Never + Suspected SPAM + Chats from strangers + + + + disable + spam + strangers + + @string/jabber_network @string/local_server diff --git a/src/main/res/values/defaults.xml b/src/main/res/values/defaults.xml index 8a6d2c8b4f892d88e9e111bd37720055829f1d7b..3b02d9d0102d04ecb77f08f3ad341ab9fdf6a21a 100644 --- a/src/main/res/values/defaults.xml +++ b/src/main/res/values/defaults.xml @@ -55,4 +55,5 @@ true true true + spam diff --git a/src/main/res/xml/preferences_notifications.xml b/src/main/res/xml/preferences_notifications.xml index 5daa4c5245e712b79f1313db6bfd65d3dbf71316..8088a85491d9d4d78f33a8bbbfce6f948ef5980e 100644 --- a/src/main/res/xml/preferences_notifications.xml +++ b/src/main/res/xml/preferences_notifications.xml @@ -1,5 +1,6 @@ - + - + android:key="chat_requests" + android:title="@string/pref_chat_requests" + app:useSimpleSummaryProvider="true" />